นักพัฒนาส่วนใหญ่มักคิดว่าหน้ากระดาษ PDF เป็นเพียงแผ่นกระดาษที่มีตัวอักษรและรูปภาพวางอยู่ แต่ไฟล์ PDF อ้างอิงทางภูมิศาสตร์ (georeferenced PDF) มีคุณค่าเหนือกว่านั้น มันบรรจุข้อมูลที่ละเอียดเพียงพอที่จะจับตำแหน่งจุดใด ๆ บนหน้ากระดาษซึ่งคำนวณในหน่วยทั่วไปของหน้ากระดาษ และรายงานพิกัดละติจูดและลองจิจูดจริงบนพื้นโลกของจุดดังกล่าวออกมา ข้อเท็จจริงข้อนี้คือสิ่งที่เปลี่ยนไฟล์ PDF ให้เป็นตัวขนส่งข้อมูลแผนที่ภูมิประเทศ (topographic map) แผนผังการสำรวจโฉนดที่ดิน แผนผังแสดงเขตอุทกภัย หรือผลลัพธ์ของ GIS ใด ๆ ที่ต้องการความหมายจริงจังเมื่อสั่งพิมพ์ ข้อมูลเชิงเรขาคณิตเหล่านี้บันทึกอยู่ภายในไฟล์เรียบร้อยแล้ว คำถามเพียงอย่างเดียวคือตัวโหลดข้อมูลของคุณได้เปิดอ่านมันขึ้นมาหรือไม่
สาเหตุที่ประเด็นนี้มักถูกมองข้ามก็เพราะไฟล์ GeoPDF สามารถเปิดอ่านและพิมพ์ออกเครื่องพิมพ์ได้ปกติเหมือนไฟล์ PDF ทั่วไป ไม่มีสิ่งใดที่ถูกเรนเดอร์ในหน้ากระดาษคอยบอกว่าแผนที่นี้ถูกตรึงกับระบบพิกัดทางภูมิศาสตร์ การเชื่อมโยงข้อมูลพิกัดจะบันทึกอยู่ในพจนานุกรม (dictionaries) ที่เชื่อมโยงเข้ากับอ็อบเจกต์หน้ากระดาษโดยที่ไม่มีการแสดงผลการวาดใด ๆ และโปรแกรมแสดงผลที่มองข้ามข้อมูลเหล่านี้ก็จะยังคงแสดงแผนที่ให้คุณเห็นได้ปกติ หากคุณต้องการดำเนินการใด ๆ ในเชิงมิติพิกัดกับตัวไฟล์ เช่น การอ่านพิกัดสำรวจ การเปลี่ยนฉายแผนที่ใหม่ (reprojection) หรือการวางซ้อนแผนที่ทับบนชั้นข้อมูล (layer) อื่น ๆ คุณจำเป็นต้องเขียนโค้ดไล่อ่านข้อมูลพจนานุกรมเหล่านั้นด้วยตนเอง
มาตรฐานสองแบบที่มีใช้งานจริง
โปรแกรมอ่านข้อมูลที่ต้องการจัดการกับไฟล์ที่ใช้งานจริงบนพื้นโลกจำเป็นต้องรองรับรูปแบบการเชื่อมโยงพิกัดภูมิศาสตร์ (georegistration) สองรูปแบบ เนื่องจากมีข้อมูลทั้งสองแบบไหลเวียนอยู่และไฟล์ที่ได้รับมาอาจเลือกใช้งานแบบใดก็ได้ รูปแบบเก่าคือการเข้ารหัสแบบ OGC ที่อธิบายใน OGC 08-139r2 ซึ่งจะผูกข้อมูลพจนานุกรม LGIDict (พจนานุกรมพิกัดภูมิสารสนเทศ) เข้ากับหน้ากระดาษ รูปแบบนี้เกิดขึ้นมาก่อนมาตรฐาน ISO และเคยเป็นโครงสร้างมาตรฐานหลักสำหรับผลลัพธ์ GeoPDF รุ่นแรก แผนที่เวอร์ชันเก่า ๆ จำนวนมากจึงยังคงบันทึกในรูปแบบนี้เป็นหลัก
รูปแบบสมัยใหม่คือมาตรฐานที่ได้รับการรับรองจาก ISO ใน ISO 32000-1 §8.8.2 แทนที่จะเก็บเป็นพจนานุกรมตัวเดียวในระดับหน้ากระดาษ มันจะจำลองข้อมูลพิกัดทางภูมิศาสตร์ให้อยู่ในรูปพอร์ตการมองเห็นหน้ากระดาษ (Viewport) ควบคู่กับพจนานุกรมการวัดขนาด (Measure) และพจนานุกรมการวัดขนาดจะทำหน้าที่ตั้งชื่อระบบพิกัดทางภูมิศาสตร์ นี่คือการเข้ารหัสข้อมูลที่โปรแกรม Acrobat และตัวส่งออก GIS ในปัจจุบันเลือกใช้ โปรแกรมนำเข้าข้อมูลที่ดีควรตรวจสอบทั้งสองจุด: ให้อ่านจากพอร์ตการมองเห็นในโมเดล ISO ก่อน และหากไม่พบค่อยดึงข้อมูลย้อนกลับ (หรือตรวจวิเคราะห์ควบคู่) ไปยัง LGIDict สำหรับไฟล์ประเภทที่เก็บข้อมูลเฉพาะรูปแบบเก่าดั้งเดิม
พอร์ตการมองเห็นและขอบเขต
ในโมเดลมาตรฐาน ISO หน่วยย่อยของการเชื่อมโยงพิกัดคือพอร์ตการมองเห็น (viewport) และในหนึ่งหน้ากระดาษสามารถประกาศพอร์ตการมองเห็นได้หลายตัว แผ่นกระดาษแผ่นใหญ่สามารถวางแผนที่หลักไว้ในสี่เหลี่ยมผืนผ้ารูปหนึ่ง วางแผนที่ย่อยย่อส่วนที่มีระดับมาตราส่วนต่างออกไปในสี่เหลี่ยมผืนผ้าอีกรูปหนึ่ง และมีแผงคำอธิบายสัญลักษณ์แผนที่ (legend) ที่ไม่มีระบบอ้างอิงพิกัดเลย พอร์ตการมองเห็นแต่ละตัวจะมาพร้อมข้อมูล BBox ซึ่งก็คือสี่เหลี่ยมผืนผ้าบนหน้ากระดาษที่พอร์ตการมองเห็นนั้นควบคุมดูแล เพื่อให้โปรแกรมอ่านทราบว่าระบบพิกัดนั้นมีผลใช้งานอยู่บนขอบเขตส่วนใดของหน้ากระดาษ การรันคำสั่งตรวจสอบจุดคลิก (hit-testing) เทียบกับขอบเขตสี่เหลี่ยมเหล่านี้คือวิธีที่โปรแกรมแสดงผลใช้ตัดสินใจว่าควรนำพจนานุกรมการวัดขนาดตัวใดมาใช้งาน
PDFlibPas exposes the viewports of the selected page directly. GetPageViewPortCount returns how many there are, GetPageViewPortID turns a one-based index into a ViewPortID handle, and GetViewPortBBox reads the bounding rectangle one dimension at a time. The Dimension argument selects which edge or extent you want: 0 is Left, 1 is Top, 2 is Width, 3 is Height, 4 is Right, and 5 is 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;
ค่า ViewPortID ที่ส่งกลับออกมาเป็นศูนย์จากฟังก์ชัน GetPageViewPortID หมายความว่าไม่พบพอร์ตการมองเห็น ณ ตำแหน่งดัชนีนั้น ดังนั้นควรเพิ่มขั้นตอนการตรวจสอบก่อนส่งตัวจัดการดังกล่าวไปใช้งานต่อ
ภายในพจนานุกรมการวัดขนาด
รายละเอียดเรขาคณิตที่ทำหน้าที่เชื่อมหน้ากระดาษเข้ากับพิกัดจริงบนพื้นโลกจะเก็บรักษาอยู่ในพจนานุกรมการวัดขนาดที่ผูกอยู่กับพอร์ตการมองเห็น ฟังก์ชัน GetViewPortMeasureDict จะส่งคืนค่าตัวจัดการ MeasureDictID จากค่า ViewPortID ที่กำหนด หรือส่งคืนค่าเป็นศูนย์หากพอร์ตการมองเห็นนั้นไม่มีพจนานุกรมการวัดขนาดแนบมาด้วย (ซึ่งพบได้ปกติในส่วนคำอธิบายสัญลักษณ์หรือแถบหัวข้อ) พจนานุกรมการวัดขนาดจะเก็บข้อมูลสำคัญสามส่วนที่คุ้มค่าในการเปิดอ่าน: ได้แก่ระบบอ้างอิงพิกัดอ้างอิง อาร์เรย์ที่เชื่อมจุดในหน้ากระดาษเข้ากับพิกัดทางภูมิศาสตร์ และหน่วยสำหรับระบุข้อมูลจุดแสดงผล
ขั้นตอนการเชื่อมโยงข้อมูลพิกัดจะเก็บอยู่ในรูปแบบอาร์เรย์คู่ขนานสองชุด GPTS คืออาร์เรย์ของจุดภูมิศาสตร์ ซึ่งเก็บพิกัดละติจูดและลองจิจูดคู่กันในระบบพิกัดภูมิศาสตร์ LPTS คืออาร์เรย์ของจุดพิกัดในพื้นที่ของหน้ากระดาษ ซึ่งแสดงในรูปสัดส่วนทศนิยมเทียบกับกล่อง BBox ของพอร์ตการมองเห็นเพื่อให้รองรับระบบการปรับย่อขยายขนาดได้ ข้อมูลลำดับที่ n ของ LPTS และข้อมูลลำดับที่ n ของ GPTS จะอ้างอิงตำแหน่งจริงทางกายภาพตำแหน่งเดียวกัน โดยตัวแรกระบุในพิกัดหน้ากระดาษและตัวหลังระบุเป็นพิกัดจริงบนผิวโลก การมีจุดพิกัดคู่กันตั้งแต่สามคู่ขึ้นไปจะเพียงพอสำหรับการคำนวณการแปลงพิกัด (affine transform หรือในกรณีทั่วไปคือ projective transform) เพื่อจับคู่จากพิกัดใด ๆ บนหน้ากระดาษภายใต้พอร์ตการมองเห็นเข้ากับพิกัดจริงบนพื้นโลก การอ่านข้อมูลจึงเป็นการวิเคราะห์ไล่ดูอาร์เรย์ทั้งสองชุดคู่ขนานกันไป:
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;
พจนานุกรมการวัดขนาดยังรายงานหน่วยแสดงผลผ่านฟังก์ชัน GetMeasureDictPDU ซึ่งรับค่าพารามิเตอร์ UnitIndex เป็น 1 สำหรับมิติเส้นตรง 2 สำหรับมิติพื้นที่ หรือ 3 สำหรับหน่วยมุม และส่งคืนรหัสเฉพาะที่ระบุหน่วยนั้น ๆ เช่น ส่งรหัสระบุเมตรหรือฟุตสำหรับมิติเส้นตรง ส่วนอาร์เรย์ Bounds ที่อ่านด้วยฟังก์ชัน GetMeasureDictBoundsItem จะให้พิกัดรูปร่างสี่เหลี่ยมด้านไม่เท่าในพอร์ตการมองเห็นที่ครอบคลุมบริเวณที่มีการวัดขนาดจริง ๆ ซึ่งในบางกรณีอาจจะไม่เท่ากับขอบเขตสี่เหลี่ยมผืนผ้าทั้งหมดของพอร์ต
เปรียบเทียบ WKT กับ EPSG
พิกัดละติจูดและลองจิจูดในอาร์เรย์ GPTS จะไม่มีประโยชน์ใด ๆ เลยหากไม่ทราบว่าพิกัดนั้นสังกัดอยู่ในระบบพิกัดภูมิศาสตร์ใด เนื่องจากตัวเลขพิกัด 51.5, -0.1 จะชี้ไปยังตำแหน่งจริงบนพื้นผิวโลกที่ต่างกันออกไประหว่างการใช้งานมาตรฐานระบบ WGS 84 เทียบกับระบบพิกัดอ้างอิงเก่าของแต่ละประเทศ พจนานุกรมการวัดขนาดจะระบุข้อมูลเรื่องนี้ไว้ในพจนานุกรมระบบพิกัด (coordinate system dictionary) ซึ่งสามารถเข้าถึงได้ผ่านฟังก์ชัน GetMeasureDictGCSDict สถาปัตยกรรมเอกสาร PDF จะระบุระบบอ้างอิงทางภูมิศาสตร์ผ่านวิธีการที่แลกเปลี่ยนกันได้สองรูปแบบ และโปรแกรมอ่านจำเป็นต้องสามารถรองรับได้ทั้งสองระบบ
รูปแบบแรกคือสตริงข้อความประเภท WKT (Well-Known Text) ซึ่งเป็นสตริงชุดข้อความสมบูรณ์ที่ระบุค่าพื้นฐานรูปทรงโลก (datum) ทรงรีสัณฐานโลก (ellipsoid) เส้นเมอริเดียนแรก (prime meridian) และหน่วยวัดอย่างครบถ้วน แม้จะมีความยาวข้อความค่อนข้างยาวแต่มีความชัดเจนสูงและไม่จำเป็นต้องพึ่งพาตารางค้นหาภายนอก รูปแบบที่สองคือรหัส EPSG ซึ่งเป็นเลขจำนวนเต็มตัวเดียวที่ใช้เป็นดัชนีระบุพิกัดในระบบฐานข้อมูล EPSG เช่น รหัส 4326 จะตรงกับพิกัด WGS 84 ซึ่งเป็นกรอบพิกัดที่อุปกรณ์ GPS ทั่วไปนิยมใช้ รหัส EPSG มีความกะทัดรัดแต่ผู้เรียกใช้จำเป็นต้องมีความสามารถในการแปลงรหัสร่วมกับฐานข้อมูลพิกัด แผนที่แผนผังที่พบอาจเลือกเก็บค่ารูปแบบใดรูปแบบหนึ่ง หรือเก็บข้อมูลไว้ทั้งสองรูปแบบ ซึ่งเป็นสาเหตุที่ API นำเสนอเมธอดครอบคลุมทั้งสามตัว ได้แก่ GetCSDictType, GetCSDictEPSG และ GetCSDictWKT โดยที่ GetCSDictType จะรายงานว่าระบบพิกัดนั้นเป็นระบบพิกัดภูมิศาสตร์ (GEOGCS ส่งคืนค่าเป็น 1) หรือเป็นระบบพิกัดแผนที่แบบโปรเจกชัน (PROJCS ส่งคืนค่าเป็น 2) ช่วยให้คุณเข้าใจรายละเอียดและนำไปตีความพิกัดอ้างอิงได้อย่างถูกต้อง
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;
การอ่านค่า LGIDict รูปแบบเก่าดั้งเดิม
ไฟล์เอกสารที่ถูกสร้างขึ้นก่อนหน้าประมวลผลด้วยโมเดลพอร์ตการมองเห็น หรือไฟล์ที่ถูกส่งออกมาจากเครื่องมือที่ยังคงส่งออกด้วยระบบการเขียนพิกัดแบบเก่า จะเก็บข้อมูลเชื่อมโยงพิกัดไว้ใน LGIDict ของหน้ากระดาษแทนการใช้พจนานุกรมการวัดขนาด ยูนิต PDFlibPas จะรายงานจำนวนพจนานุกรมลักษณะนี้ที่มีทั้งหมดในหน้ากระดาษผ่านฟังก์ชัน GetPageLGIDictCount และส่งคืนข้อความดิบข้างในผ่านฟังก์ชัน GetPageLGIDictContent โดยลำดับดัชนีเริ่มต้นด้วยเลขหนึ่ง ข้อความที่ได้รับคือโครงสร้างพจนานุกรมตามที่จัดเก็บจริงในไฟล์ ซึ่งเก็บฟิลด์พิกัด OGC 08-139r2 เอาไว้ โค้ดของคุณสามารถนำข้อมูลดังกล่าวมาวิเคราะห์เพื่อกู้คืนข้อมูลจับคู่พิกัดในทัศนะเดียวกับที่พจนานุกรมการวัดขนาดมอบให้ ในฝั่งกระบวนการเขียนคำสั่ง AddLGIDictToPage จะทำหน้าที่ผูกโครงสร้าง AddLGIDictToPage เข้ากับหน้ากระดาษปัจจุบัน เพื่อรองรับกระบวนการแปลงไฟล์ย้อนกลับ (round-trip) ในรูปแบบเก่าเมื่อปลายทางยังคงต้องการใช้งานระบบเดิม
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;
การรวบรวมการวิเคราะห์ข้อมูลเข้าด้วยกัน
โปรแกรมนำเข้าข้อมูลที่สมบูรณ์จะแยกวิเคราะห์แผนที่ทั้งสองระบบควบคู่กันไปในการประมวลผลแต่ละหน้ากระดาษ ให้ทำการเลือกหน้ากระดาษที่ต้องการ เรียกเมธอด GetPageViewPortCount เพื่อดึงพอร์ตการมองเห็นในโมเดล ISO และสำหรับพอร์ตแต่ละตัวที่มีพจนานุกรมการวัดขนาด ให้ดึงข้อมูล BBox อาร์เรย์ GPTS และ LPTS หน่วยข้อมูลจุด และรายละเอียดระบบพิกัด GCS ผ่านทางพจนานุกรมระบบพิกัด หลังจากนั้นให้รันการตรวจสอบฟังก์ชัน GetPageLGIDictCount เพื่ออ่านข้อมูลอ้างอิงพิกัดแบบเก่าดั้งเดิมที่โมเดล ISO อาจไม่ได้ครอบคลุม แผนที่แผนผังที่เก็บข้อมูลพิกัดทั้งสองระบบควรมีพิกัดที่สอดคล้องตรงกัน แต่หากแผนที่เก็บข้อมูลเพียงระบบเดียวก็ยังสามารถประมวลผลวิเคราะห์ได้ เนื่องจากคุณได้ตรวจสอบจากทั้งสองจุดเรียบร้อยแล้ว ตัวจัดการต่าง ๆ ที่ส่งคืนกลับมาตลอดกระบวนการ ไม่ว่าจะเป็น ViewPortID, MeasureDictID และ CSDictID ล้วนเป็นเลขจำนวนเต็มธรรมดาที่ยังคงใช้งานได้ดีตลอดระยะเวลาการโหลดเอกสาร ดังนั้นขั้นตอนการไล่อ่านข้อมูลทั้งหมดจึงเป็นเพียงแค่ลูปซ้อนลูปง่าย ๆ ในรายการหน้ากระดาษโดยไม่มีขั้นตอนจองพื้นที่เพิ่มเติมให้ต้องจัดการคอยดูแล
เมื่อคุณสามารถดึงพิกัดภูมิศาสตร์กลับมาได้ หน้าเอกสารกระดาษก็จะเปลี่ยนหน้าที่จากรูปภาพเปล่า ๆ กลายเป็นแหล่งข้อมูลดิจิทัลที่ใช้งานได้จริง เทคนิคเสริมสำหรับการอ่านข้อมูลส่วนอื่น ๆ ของหน้าเอกสารระบุไว้ใน บทความการดึงข้อความ รูปภาพ และฟอนต์ของเรา และส่วนการแสดงผลเอกสารอ้างอิงภูมิศาสตร์ออกสู่อุปกรณ์เพื่อคำนวณวัดระยะบนหน้าจอมีอธิบายไว้ใน คู่มือการพรีวิวและการพิมพ์ในบริบทอุปกรณ์ ตัวอ่านข้อมูลเชิงพื้นที่ทางภูมิศาสตร์ที่อธิบายไว้ที่นี่มีพร้อมใช้งานในฐานะส่วนหนึ่งของ losLab PDF Library สำหรับ Delphi และ C++Builder ร่วมกับ API การโหลด การแยกข้อมูล และการแสดงผลที่ระบุไว้ในบล็อกนี้