يعتقد معظم المطورين أن صفحة PDF هي ورقة عليها نصوص وصور. لكن ملف PDF المرجعي جغرافياً (georeferenced) هو أكثر من ذلك. فهو يحمل معلومات كافية لأخذ نقطة على الصفحة، مقاسة بوحدات الصفحة العادية، والإبلاغ عن خط العرض وخط الطول الذي تقع عليه في العالم الحقيقي. هذه الحقيقة الوحيدة هي ما يحول ملف PDF إلى ناقل قابل للاستخدام لخريطة طوبوغرافية، أو مخطط مسح عقاري، أو معرض لمنطقة فيضان، أو أي تصدير لنظام المعلومات الجغرافية (GIS) يجب طباعته ولا يزال يحمل معنى. الهندسة موجودة في الملف؛ والسؤال الوحيد هو ما إذا كان برنامج التحميل الخاص بك يقرأها أم لا
السبب في تفويت ذلك هو أن GeoPDF يُفتح ويُطبع تماماً مثل أي ملف PDF آخر. لا شيء في الصفحة المعروضة يعلن أن الخريطة مسجلة في نظام إحداثيات. يعيش التسجيل في قواميس معلقة بكائن الصفحة، ولا يتم رسمها أبداً، والعارض الذي يتجاهلها يعرض لك الخريطة بشكل طبيعي. للقيام بأي شيء مكاني باستخدام الملف، مثل قراءة إحداثيات المسح، أو إعادة الإسقاط، أو التراكب مقابل طبقات أخرى، يجب عليك تصفح تلك القواميس بنفسك
معياران مستخدمان في الواقع
يجب على القارئ الذي يريد التعامل مع الملفات في العالم الحقيقي أن يتعامل مع نظامين للتسجيل الجغرافي، لأن كلاهما متداول وقد يستخدم ملف معين أياً منهما. الأقدم هو ترميز OGC الموصوف في OGC 08-139r2، والذي يرفق LGIDict (قاموس تسجيل جغرافي مكاني) بالصفحة. وهو يسبق أي اعتماد من ISO وكان التنسيق الفعلي لمخرجات GeoPDF المبكرة، لذلك فإن مجموعة كبيرة من الخرائط القديمة تحمله ولا شيء غيره
المخطط الحديث هو ما قامت ISO بتوحيده في ISO 32000-1 §8.8.2. فبدلاً من قاموس واحد على مستوى الصفحة، فإنه يصمم البيانات الجغرافية المكانية كإطار عرض (Viewport) للصفحة مع قاموس قياس (Measure) مرفق، ويسمي قاموس القياس نظام إحداثيات جغرافية. هذا هو الترميز الذي يكتبه Acrobat ومصدرو GIS الحاليون. يتحقق المستورد القوي من كليهما: قراءة إطارات العرض لنموذج ISO، والرجوع إلى (أو فحص إضافي) لـ LGIDict للملفات التي تحمل التسجيل القديم فقط
إطارات العرض وحدودها
في نموذج ISO، تكون وحدة التسجيل الجغرافي هي إطار العرض (viewport)، وقد تحتوي الصفحة على عدة إطارات. يمكن لورقة كبيرة وضع خريطة رئيسية في مستطيل واحد، وخريطة داخلية بمقياس مختلف في مستطيل آخر، ولوحة مفاتيح (legend) غير مرجعية جغرافياً على الإطلاق. يحمل كل إطار عرض BBox، وهو المستطيل الموجود على الصفحة الذي يتحكم فيه إطار العرض، حتى يعرف القارئ أي جزء من الورقة ينطبق عليه نظام إحداثيات معين. اختبار نقطة تم النقر عليها مقابل هذه المربعات هو الطريقة التي يقرر بها العارض أي قاموس قياس يجب استخدامه
يكشف PDFlibPas عن إطارات العرض للصفحة المحددة مباشرةً. يُرجع GetPageViewPortCount عددها، ويحول GetPageViewPortID الفهرس القائم على واحد إلى مقبض ViewPortID، ويقرأ GetViewPortBBox المستطيل المحيط بُعداً تلو الآخر. تحدد وسيطة Dimension الحافة أو الامتداد الذي تريده: 0 هو اليسار (Left)، و1 هو الأعلى (Top)، و2 هو العرض (Width)، و3 هو الارتفاع (Height)، و4 هو اليمين (Right)، و5 هو الأسفل (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 معين، أو صفراً عندما لا يحتوي إطار العرض على قاموس قياس، وهو الحالة العادية للوحة مفاتيح (legend) أو عنوان. يحمل قاموس القياس ثلاثة أشياء تستحق القراءة: أنظمة الإحداثيات التي يشير إليها، والمصفوفات التي تربط نقاط الصفحة بالنقاط الجغرافية، والوحدة التي يتم التعبير عن بيانات النقطة بها
التسجيل نفسه عبارة عن مصفوفتين متوازيتين. GPTS هي مصفوفة النقاط الجغرافية، وهي أزواج خطوط العرض والطول المعطاة في نظام الإحداثيات الجغرافية. LPTS هي مصفوفة نقاط مساحة الصفحة، معبراً عنها ككسور من BBox الخاص بإطار العرض حتى تنجو من القياس. العنصر n من LPTS والعنصر n من GPTS يسميان نفس الموقع المادي، مرة بإحداثيات الصفحة ومرة على الكرة الأرضية. ثلاثة أو أكثر من هذه الأزواج تثبت التحويل التآلفي (affine)، أو في الحالة العامة الإسقاطي (projective)، الذي يعين أي إحداثي صفحة داخل إطار العرض إلى إحداثي عالمي. قراءتها مسألة تصفح كلا المصفوفتين بخطوة متزامنة
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 مقارنة بالمسند الوطني (datum) الأقدم. يجيب قاموس القياس على ذلك من خلال قاموس نظام الإحداثيات، والذي يتم الوصول إليه باستخدام GetMeasureDictGCSDict للنظام الجغرافي. يصف PDF ذلك النظام بإحدى طريقتين قابلتين للتبديل، ويجب على القارئ قبول أي منهما
الأولى هي WKT (النص المعروف جيداً - Well-Known Text)، وهي سلسلة قائمة بذاتها توضح المسند (datum)، والمجسم الإهليلجي (ellipsoid)، وخط الطول الأساسي، والوحدات بالكامل. إنها مطولة ولكنها لا لبس فيها ولا تحتاج إلى جدول بحث خارجي. الثانية هي رمز 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 القديم
الملفات التي تسبق نموذج إطار العرض (viewport)، أو التي تم إنتاجها بواسطة أدوات لا تزال تصدر الترميز الأقدم، تحمل تسجيلها في 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، إلى جانب واجهات برمجة تطبيقات (APIs) التحميل والاستخراج والعرض التي تمت تغطيتها في مكان آخر في هذه المدونة