يفكر معظم المطورين في صفحة PDF كـ ورقة بها نص وصور. لكن ملف PDF ذي المرجع الجغرافي هو أكثر من ذلك. إنه يحمل معلومات كافية لأخذ نقطة في الصفحة، مقاسة بوحدات الصفحة العادية، والإبلاغ عن خطوط الطول والعرض التي تقع فوقها في العالم الحقيقي. هذه الحقيقة الفردية هي ما يحول ملف PDF إلى حامل قابل للاستخدام لخريطة طبوغرافية، أو مخطط مسح كادسترالي، أو معرض لمنطقة فيضان، أو أي تصدير لنظام المعلومات الجغرافية (GIS) يتعين عليه الطباعة ولا يزال يعني شيئاً. الهندسة موجودة في الملف؛ السؤال الوحيد هو ما إذا كان برنامج التحميل يقرأها.
السبب في تفويت ذلك هو أن GeoPDF يفتح ويطبع تماماً مثل أي ملف PDF آخر. لا شيء في الصفحة المرسومة يعلن أن الخريطة مسجلة في نظام إحداثيات. يعيش التسجيل في قواميس معلقة بكائن الصفحة، ولا يتم رسمها أبداً، ويعرض لك العارض الذي يتجاهلها الخريطة مع ذلك. للقيام بأي شيء مكاني بالملف، مثل قراءات إحداثيات المسح، أو إعادة الإسقاط، أو التراكب ضد طبقات أخرى، يتعين عليك تصفح تلك القواميس بنفسك.
معياران يعيشان في الطبيعة
يتعين على القارئ الذي يريد التعامل مع ملفات العالم الحقيقي التعامل مع مخططين للتسجيل الجغرافي، لأن كلاهما قيد التداول وقد يستخدم ملف معين أياً منهما. الأقدم هو ترميز OGC الموصوف في OGC 08-139r2، والذي يربط LGIDict (قاموس تسجيل جغرافي مكاني) بالصفحة. إنه يسبق أي مباركة من ISO وكان التنسيق الفعلي لمخرجات GeoPDF المبكرة، لذا فإن جسداً كبيراً من الخرائط القديمة يحمله ولا شيء غيره.
المخطط الحديث هو المخطط الذي وضعته ISO في المواصفة ISO 32000-1 §8.8.2. بدلاً من قاموس واحد على مستوى الصفحة، فإنه ينمذج البيانات الجغرافية المكانية كـ منفذ عرض (Viewport) للصفحة مع قاموس قياس (Measure) مرفق، ويسمي قاموس القياس نظام إحداثيات جغرافية. هذا هو الترميز الذي يكتبه Acrobat ومصدرو نظم المعلومات الجغرافية الحاليون. يتحقق المستورد القوي من كليهما: يقرأ منافذ العرض لنموذج ISO، ويتراجع إلى (أو يفحص بالإضافة إلى ذلك) LGIDict للملفات التي تحمل التسجيل القديم فقط.
منافذ العرض وحدودها
في نموذج ISO، وحدة التسجيل الجغرافي هي منفذ العرض، وقد تحتوي الصفحة على عدة منافذ. يمكن لورقة كبيرة أن تضع خريطة رئيسية في مستطيل واحد، وخريطة ملحقة بمقياس رسم مختلف في مستطيل آخر، ولوحة مفاتيح خريطة ليست ذات مرجع جغرافي على الإطلاق. يحمل كل منفذ عرض BBox، وهو المستطيل الموجود على الصفحة الذي يحكمه منفذ العرض، حتى يعرف القارئ أي جزء من الورقة ينطبق عليه نظام إحداثيات معين. اختبار الاصطدام لنقطة تم النقر عليها ضد تلك المربعات هو كيفية تحديد العارض لقاموس القياس الذي سيستخدمه.
PDFlibPas يكشف عن منافذ عرض الصفحة المحددة مباشرة. يرجع GetPageViewPortCount عددها، ويحول GetPageViewPortID الفهرس الذي يبدأ من واحد إلى مقبض ViewPortID، ويقرأ GetViewPortBBox المستطيل المحيط بعداً واحداً في كل مرة. تحدد معلمة Dimension الحافة أو الامتداد الذي تريده: 0 لـ اليسار، و 1 لـ الأعلى، و 2 لـ العرض، و 3 لـ الارتفاع، و 4 لـ اليمين، و 5 لـ الأسفل.
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)، أو الإسقاطي في الحالة العامة، الذي يرسم أي إحداثيات صفحة داخل منفذ العرض إلى إحداثيات العالم. قراءتها هي مسألة المرور عبر كلتا المصفوفتين خطوة بخطوة.
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 مقارنة بنظام مرجعي وطني أقدم. يجيب قاموس القياس على هذا من خلال قاموس نظام الإحداثيات، الذي يتم الوصول إليه باستخدام GetMeasureDictGCSDict للنظام الجغرافي. يصف PDF هذا النظام بإحدى طريقتين قابليتين للتبادل، ويتعين على القارئ قبول أي منهما.
الأول هو WKT، نص معروف جيداً (Well-Known Text)، وهي سلسلة قائمة بذاتها توضح المرجع والمجسم الإهليلجي وخط الزوال الرئيسي والوحدات بالكامل. إنها مطولة ولكنها غير غامضة ولا تحتاج إلى جدول بحث خارجي. الثاني هو رمز EPSG، وهو عدد صحيح واحد يفهرس نظام إحداثيات في سجل EPSG؛ و 4326 هو WGS 84، الإطار الذي تستخدمه معظم بيانات نظام تحديد المواقع العالمي (GPS) للمستهلكين. EPSG مضغوط ولكنه يفترض أن القارئ يمكنه حل الرمز مقابل قاعدة بيانات. تظهر الملفات بواحد أو الآخر أو كليهما، ولهذا السبب تظهر واجهة برمجة التطبيقات الثلاثة 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 قاموس LGIDict بالصفحة الحالية، بحيث يمكن للمحول معالجة الشكل القديم ذهاباً وإياباً عندما لا يزال مستهلك قديم يتوقعه.
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 عن أي تسجيل قديم لم تغطه تمريرة منفذ العرض. يجب أن تتفق الخريطة التي تحملهما معاً بينهما؛ وتظل الخريطة التي تحمل واحداً فقط قابلة للحل، لأنك نظرت في كلا المكانين. المقابض المرتجعة على طول الطريق، ViewPortID، MeasureDictID، CSDictID، هي أعداد صحيحة بسيطة تظل صالحة أثناء تحميل المستند، لذا فإن المسار بأكمله هو بضع حلقات متداخلة عبر قائمة الصفحات دون الحاجة إلى إدارة تخصيص.
بمجرد أن تتمكن من استرداد التسجيل، تصبح الصفحة مصدر بيانات بدلاً من مجرد صورة. التقنيات المصاحبة لقراءة بقية الصفحة مغطاة في المقال حول استخراج النصوص والصور والخطوط، وموصوف رسم ورقة ذات مرجع جغرافي على جهاز للقياس على الشاشة في الدليل الإرشادي لسياق جهاز الطباعة والمعاينة. يشحن القارئ الجغرافي المكاني الموصوف هنا كجزء من مكتبة losLab PDF لـ Delphi و C++Builder، جنباً إلى جنب مع واجهات برمجة التطبيقات للتحميل والاستخراج والرسم المغطاة في مكان آخر في هذه المدونة.