Hầu hết các lập trình viên đều nghĩ trang PDF giống như một tờ giấy có chữ và hình ảnh trên đó. Một tài liệu PDF được tham chiếu địa lý (georeferenced PDF) còn nhiều hơn thế. Nó mang đủ thông tin để lấy một điểm trên trang giấy, được đo bằng các đơn vị trang thông thường, và báo cáo vĩ độ cùng kinh độ mà nó nằm trên trong thế giới thực. Thực tế đơn giản đó là thứ biến một tệp PDF thành một phương tiện vận chuyển hữu ích cho một bản đồ địa hình, một sơ đồ khảo sát địa chính, một sơ đồ vùng ngập lụt, hoặc bất kỳ dữ liệu xuất GIS nào cần in ấn mà vẫn giữ nguyên giá trị thông tin. Cấu trúc hình học đã có sẵn trong tệp; câu hỏi duy nhất là liệu bộ tải của bạn có đọc được nó hay không.
Lý do thông tin này bị bỏ qua là vì một tệp GeoPDF được mở và in ấn chính xác giống như bất kỳ tệp PDF nào khác. Không có gì trong trang được hiển thị thông báo rằng bản đồ được đăng ký với một hệ tọa độ. Việc đăng ký nằm trong các từ điển liên kết với đối tượng trang, không bao giờ được vẽ ra, và một trình xem bỏ qua chúng vẫn hiển thị bản đồ cho bạn như bình thường. Để thực hiện bất kỳ hoạt động không gian nào với tệp tin, như đọc tọa độ khảo sát, tái chiếu (reprojection), hoặc phủ lên các lớp dữ liệu khác, bạn phải tự mình duyệt qua các từ điển đó.
Hai tiêu chuẩn tồn tại trong thực tế
Một trình đọc muốn xử lý các tệp trong thế giới thực phải đối phó với hai cơ chế đăng ký địa lý (georegistration schemes), vì cả hai đều đang lưu hành và một tệp nhất định có thể sử dụng bất kỳ cơ chế nào. Cơ chế cũ hơn là mã hóa OGC được mô tả trong OGC 08-139r2, vốn đính kèm một từ điển LGIDict (từ điển đăng ký địa không gian) vào trang. Nó xuất hiện trước bất kỳ sự công nhận ISO nào và là định dạng thực tế cho các đầu ra GeoPDF ban đầu, do đó một lượng lớn các bản đồ kế thừa mang định dạng này và không có gì khác.
Cơ chế hiện đại là cơ chế được ISO chuẩn hóa trong ISO 32000-1 §8.8.2. Thay vì một từ điển cấp trang đơn lẻ, nó mô hình hóa dữ liệu địa không gian dưới dạng một Viewport trang đi kèm một từ điển Measure, và từ điển measure xác định một hệ tọa độ địa lý. Đây là kiểu mã hóa mà Acrobat và các bộ xuất GIS hiện tại đang ghi. Một trình nhập dữ liệu mạnh mẽ sẽ kiểm tra cả hai: đọc các viewport cho mô hình ISO, và quay lại (hoặc kiểm tra bổ sung) từ điển LGIDict cho các tệp chỉ mang thông tin đăng ký kế thừa.
Viewport và ranh giới của chúng
Trong mô hình ISO, đơn vị đăng ký địa lý là viewport, và một trang có thể có nhiều viewport. Một bản in lớn có thể đặt một bản đồ chính trong một hình chữ nhật, một bản đồ phụ ở một tỷ lệ khác trong một hình chữ nhật khác, và một bảng chú giải không được tham chiếu địa lý chút nào. Mỗi viewport mang một cấu trúc BBox, hình chữ nhật trên trang mà viewport đó quản lý, để trình đọc biết phần nào của tờ giấy áp dụng hệ tọa độ nào. Kiểm tra va chạm (Hit-testing) điểm được nhấp với các hộp ranh giới đó là cách trình xem quyết định sử dụng từ điển measure nào.
PDFlibPas hiển thị trực tiếp các viewport của trang được chọn. GetPageViewPortCount trả về số lượng viewport hiện có, GetPageViewPortID chuyển đổi một chỉ mục bắt đầu từ một thành một tay cầm ViewPortID, và GetViewPortBBox đọc hình chữ nhật giới hạn từng chiều một. Đối số Dimension chọn cạnh hoặc phạm vi bạn muốn: 0 là Left, 1 là Top, 2 là Width, 3 là Height, 4 là Right, và 5 là Bottom.
var
Pdf: TPDFlib;
vpCount, i, vpID: Integer;
Left, Top, Width, Height: Double;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('topo_sheet.pdf', '') <> 1 then
raise Exception.Create('load failed');
Pdf.SelectPage(1);
vpCount := Pdf.GetPageViewPortCount;
for i := 1 to vpCount do
begin
vpID := Pdf.GetPageViewPortID(i);
Left := Pdf.GetViewPortBBox(vpID, 0);
Top := Pdf.GetViewPortBBox(vpID, 1);
Width := Pdf.GetViewPortBBox(vpID, 2);
Height := Pdf.GetViewPortBBox(vpID, 3);
// Left/Top/Width/Height describe the map area for this viewport
end;
finally
Pdf.Free;
end;
end;
Một giá trị ViewPortID bằng không từ GetPageViewPortID có nghĩa là không thể tìm thấy viewport tại chỉ mục đó, vì vậy hãy kiểm tra nó trước khi truyền tay cầm đi tiếp.
Bên trong từ điển measure
Cấu trúc hình học đăng ký trang giấy với thế giới thực tế nằm trong từ điển measure được đính kèm vào một viewport. GetViewPortMeasureDict trả về một MeasureDictID cho một ViewPortID cho trước, hoặc bằng không khi viewport không có từ điển measure, đây là trường hợp bình thường đối với bảng chú giải hoặc khung tiêu đề. Từ điển measure chứa ba điều đáng đọc: các hệ tọa độ nó tham chiếu, các mảng liên kết các điểm trên trang với các điểm địa lý, và đơn vị biểu diễn dữ liệu điểm.
Bản thân việc đăng ký là hai mảng song song. GPTS is the array of geographic points, latitude and longitude pairs given in the geographic coordinate system. LPTS is the array of page-space points, expressed as fractions of the viewport's BBox so they survive scaling. Phần tử n của LPTS và phần tử n của GPTS đặt tên cho cùng một vị trí vật lý, một lần trong tọa độ trang và một lần trên quả địa cầu. Ba hoặc nhiều cặp như vậy sẽ xác định phép biến đổi affine, hoặc trong trường hợp tổng quát là phép biến đổi phối cảnh (projective), ánh xạ bất kỳ tọa độ trang nào bên trong viewport thành tọa độ thế giới. Việc đọc chúng là việc duyệt qua cả hai mảng cùng một lúc.
var
measID, gptsCount, lptsCount, j: Integer;
lat, lon, px, py: Double;
begin
measID := Pdf.GetViewPortMeasureDict(vpID);
if measID <> 0 then
begin
gptsCount := Pdf.GetMeasureDictGPTSCount(measID);
lptsCount := Pdf.GetMeasureDictLPTSCount(measID);
// GPTS holds lat/lon pairs; LPTS holds the matching page fractions.
// Both arrays are read with one-based item indices.
j := 1;
while j < gptsCount do
begin
lat := Pdf.GetMeasureDictGPTSItem(measID, j);
lon := Pdf.GetMeasureDictGPTSItem(measID, j + 1);
px := Pdf.GetMeasureDictLPTSItem(measID, j);
py := Pdf.GetMeasureDictLPTSItem(measID, j + 1);
// (px, py) on the page corresponds to (lat, lon) on the ground
Inc(j, 2);
end;
end;
end;
Từ điển measure cũng báo cáo các đơn vị hiển thị của nó thông qua GetMeasureDictPDU, nhận một giá trị UnitIndex bằng 1 cho đơn vị tuyến tính, 2 cho diện tích, hoặc 3 cho đơn vị góc và trả về một mã xác định đơn vị cụ thể, ví dụ như mét hoặc foot quốc tế cho danh mục tuyến tính. Mảng Bounds, được đọc bằng GetMeasureDictBoundsItem, mô tả hình tứ giác trong viewport mà phép đo thực sự bao phủ, vốn không phải lúc nào cũng là toàn bộ hình chữ nhật.
WKT so với EPSG
Vĩ độ và kinh độ trong GPTS là vô nghĩa nếu không biết chúng thuộc về hệ tọa độ địa lý nào, vì tọa độ 51.5, -0.1 sẽ rơi vào một điểm vật lý khác dưới hệ WGS 84 so với dưới một hệ quy chiếu quốc gia cũ hơn. Từ điển measure giải quyết điều này thông qua một từ điển hệ tọa độ, đạt được bằng GetMeasureDictGCSDict cho hệ địa lý. Tài liệu PDF mô tả hệ thống đó bằng một trong hai cách có thể thay thế cho nhau, và một trình đọc phải chấp nhận cả hai.
Cách thứ nhất là WKT, Well-Known Text, một chuỗi khép kín thể hiện đầy đủ hệ quy chiếu (datum), ellipsoid, kinh tuyến gốc (prime meridian), và các đơn vị đo. Nó dài dòng nhưng rõ ràng và không cần bảng tra cứu bên ngoài. Cách thứ hai là một mã EPSG, một số nguyên duy nhất lập chỉ mục cho một hệ tọa độ trong đăng ký EPSG; 4326 là WGS 84, khung hệ thống mà hầu hết dữ liệu GPS của người tiêu dùng sử dụng. EPSG nhỏ gọn nhưng giả định trình đọc có thể giải quyết mã đó dựa trên một cơ sở dữ liệu. Các tệp xuất hiện với một trong hai, hoặc cả hai, đó là lý do tại sao API hiển thị cả ba phương thức GetCSDictType, GetCSDictEPSG, và GetCSDictWKT. GetCSDictType báo cáo hệ thống là địa lý (một GEOGCS, giá trị trả về là 1) hay chiếu (một PROJCS, giá trị trả về là 2), cho phép bạn diễn giải phần còn lại một cách chính xác trước khi tin cậy nó.
var
gcsID, csType, epsg: Integer;
wkt: WideString;
begin
gcsID := Pdf.GetMeasureDictGCSDict(measID);
if gcsID <> 0 then
begin
csType := Pdf.GetCSDictType(gcsID); // 1 = GEOGCS, 2 = PROJCS
epsg := Pdf.GetCSDictEPSG(gcsID); // e.g. 4326 for WGS 84, 0 if absent
wkt := Pdf.GetCSDictWKT(gcsID); // full text description, '' if absent
// Prefer EPSG when present; fall back to parsing WKT otherwise.
end;
end;
Đọc từ điển kế thừa LGIDict
Các tệp xuất hiện trước mô hình viewport, hoặc được tạo ra bởi các công cụ vẫn phát ra kiểu mã hóa cũ hơn, mang thông tin đăng ký của chúng trong một từ điển LGIDict trên trang thay vì trong từ điển measure. PDFlibPas báo cáo số lượng từ điển như vậy mà một trang có thông qua GetPageLGIDictCount và trả lại nội dung thô của từng từ điển bằng GetPageLGIDictContent, được lập chỉ mục bắt đầu từ một. Văn bản trả về là từ điển được viết sẵn, chứa các trường đăng ký OGC 08-139r2, mã nguồn của bạn sau đó sẽ phân tích cú pháp để khôi phục cùng loại ánh xạ từ trang sang thế giới thực mà từ điển measure cung cấp. Về phía ghi, AddLGIDictToPage đính kèm một từ điển LGIDict vào trang hiện tại, để một bộ chuyển đổi có thể xử lý khứ hồi dạng kế thừa khi một trình tiêu thụ cũ vẫn yêu cầu.
var
lgiCount, k: Integer;
dictText: WideString;
begin
lgiCount := Pdf.GetPageLGIDictCount;
for k := 1 to lgiCount do
begin
dictText := Pdf.GetPageLGIDictContent(k);
// dictText carries the OGC 08-139r2 registration to parse
end;
end;
Tổng hợp quy trình đọc
Một trình nhập dữ liệu hoàn chỉnh xử lý hai cơ chế này dưới dạng một cặp lượt duyệt qua mỗi trang. Chọn trang, yêu cầu GetPageViewPortCount cung cấp các viewport ISO, và đối với mỗi viewport sở hữu một từ điển measure, hãy lấy BBox của nó, các mảng GPTS và LPTS của nó, đơn vị dữ liệu điểm, và mô tả GCS thông qua từ điển hệ tọa độ. Sau đó kiểm tra GetPageLGIDictCount để tìm bất kỳ thông tin đăng ký kế thừa nào mà lượt duyệt qua viewport chưa bao phủ. Một bản đồ mang cả hai thông tin đăng ký nên đồng nhất dữ liệu giữa chúng; một bản đồ chỉ mang một thông tin vẫn sẽ giải quyết được, vì bạn đã tìm kiếm ở cả hai nơi. Các tay cầm được trả về trong quá trình này, như ViewPortID, MeasureDictID, CSDictID, là các số nguyên đơn giản vẫn giữ nguyên giá trị hợp lệ khi tài liệu được tải, do đó toàn bộ lượt duyệt chỉ là một vài vòng lặp lồng nhau trên danh sách trang mà không có hoạt động cấp phát nào cần quản lý.
Một khi bạn có thể khôi phục đăng ký địa lý, trang giấy sẽ trở thành một nguồn dữ liệu thay vì một bức ảnh đơn thuần. Các kỹ thuật đi kèm để đọc phần còn lại của trang được trình bày trong bài viết về trích xuất văn bản, hình ảnh và font chữ, và kết xuất một tờ giấy được tham chiếu địa lý ra một thiết bị để đo lường trên màn hình được mô tả trong hướng dẫn ngữ cảnh thiết bị in ấn và xem trước. Trình đọc địa không gian được mô tả ở đây đi kèm như một phần của losLab PDF Library dành cho Delphi và C++Builder, bên cạnh các API tải, trích xuất và hiển thị được đề cập ở các bài viết khác trên blog này.