A legtöbb fejlesztő úgy gondol a PDF oldalra, mint egy papírlapra, amelyen szövegek és képek vannak. A georeferált PDF ennél több. Elegendő információt hordoz ahhoz, hogy az oldalon lévő, szokásos oldalszámokkal mért pontból megmondja a szélességi és hosszúsági fokot, amely felett a valós világban elhelyezkedik. Ez az egyetlen tény teszi a PDF-et használható hordozóvá domborzati térképek, kataszteri felmérési rajzok, árvízvédelmi bemutatók vagy bármely olyan GIS-export számára, amelyet ki kell nyomtatni, és mégis jelentéssel bír. A geometria ott van a fájlban; az egyetlen kérdés az, hogy a betöltő beolvassa-e.
Amiért ez elkerüli a figyelmet, az az, hogy a GeoPDF pontosan úgy nyílik meg és nyomtatódik, mint bármely más PDF. A renderelt oldalon semmi sem jelzi, hogy a térkép egy koordináta-rendszerhez van igazítva. A regisztráció az oldalobjektumból lógó szótárakban él, soha nem rajzolódik ki, és az a megjelenítő, amely figyelmen kívül hagyja őket, ugyanúgy megmutatja a térképet. Ahhoz, hogy bármilyen térbeli műveletet végezzen a fájllal – felmérési koordináták leolvasása, újra-vetítés, más rétegekkel való átfedés –, saját magának kell bejárnia ezeket a szótárakat.
Két szabvány él a gyakorlatban
Az olyan olvasónak, amely valós fájlokat akar kezelni, meg birkóznia kell a két georeferálási sémával, mert mindkettő forgalomban van, és egy adott fájl bármelyiket használhatja. A régebbi az OGC 08-139r2-ben leírt OGC kódolás, amely egy LGIDict-et (térinformatikai regisztrációs szótárt) csatol az oldalhoz. Ez megelőz minden ISO jóváhagyást, és a korai GeoPDF kimenet de facto formátuma volt, így a régi térképek nagy része ezt hordozza és semmi mást.
A modern séma az, amelyet az ISO az ISO 32000-1 §8.8.2-ben szabványosított. Ahelyett, hogy egyetlen oldalszintű szótár lenne, a térinformatikai adatokat oldal-nézetablakként (Viewport) modellezi egy csatolt méret-szótárral (Measure dictionary), és a méret-szótár megnevez egy földrajzi koordináta-rendszert. Ezt a kódolást írja az Acrobat és a jelenlegi GIS exportőrök. Egy robusztus importer mindkettőt ellenőrzi: beolvassa a nézetablakokat az ISO modellhez, és visszalép (vagy kiegészítően ellenőrzi) a LGIDict-re azoknál a fájloknál, amelyek csak az örökölt regisztrációt hordozzák.
Nézetablakok és határaik
Az ISO modellben a georeferálás egysége a nézetablak (viewport), és egy oldalon több is lehet. A nagy lap elhelyezhet egy fő térképet egy téglalapban, egy részletet más méretarányban egy másikban, és egy jelmagyarázat panelt, amely egyáltalán nincs georeferálva. Minden nézetablak hordoz egy BBox-ot (a téglalapot az oldalon, amelyet a nézetablak szabályoz), így az olvasó tudja, hogy a lap melyik részére vonatkozik az adott koordináta-rendszer. A kattintott pont ütközésvizsgálata ezen dobozokkal szemben az, ahogyan a megjelenítő eldönti, melyik méret-szótárat használja.
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;
A GetPageViewPortID-ből származó nulla ViewPortID azt jelenti, hogy az adott indexű nézetablak nem található, ezért ellenőrizze azt, mielőtt továbbadná a kezelőt.
A méret-szótár belsejében
Az oldalt a világhoz igazító geometria a nézetablakhoz csatolt méret-szótárban él. A GetViewPortMeasureDict egy MeasureDictID-t ad vissza egy adott ViewPortID-hez, vagy nullát, ha a nézetablak nem rendelkezik méret-szótárral, ami a szokásos eset egy jelmagyarázat vagy címpanel esetén. A méret-szótár három dolgot tartalmaz, amit érdemes elolvasni: a koordináta-rendszereket, amelyekre hivatkozik, a tömböket, amelyek az oldal pontjait a földrajzi pontokhoz kötik, és az egységet, amelyben a pontadatok ki vannak fejezve.
Maga a regisztráció két párhuzamos tömb. A GPTS a földrajzi pontok tömbje, a földrajzi koordináta-rendszerben megadott szélességi és hosszúsági fokok párjai. Az LPTS az oldaltérbeli pontok tömbje, a nézetablak BBox-ának törtjeiként kifejezve, hogy túléljék a méretezést. Az LPTS n-edik eleme és a GPTS n-edik eleme ugyanazt a fizikai helyet jelöli meg, egyszer az oldal koordinátáiban, egyszer pedig a földgömbön. Három vagy több ilyen pár rögzíti az affin, vagy általános esetben projektív transzformációt, amely a nézetablakon belüli bármely oldalkoordinátát a világ koordinátájára képez le. Beolvasásuk a két tömb lépésben történő bejárását jelenti.
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;
A méret-szótár a megjelenítési egységeit is jelzi a GetMeasureDictPDU segítségével, amely a lineárishoz 1-es, a területhez 2-es, a szögegységekhez pedig 3-as UnitIndex-et fogad, és visszaadja az adott egységet azonosító kódot, például métert vagy nemzetközi lábat a lineáris kategóriához. A GetMeasureDictBoundsItem segítségével beolvasott Bounds tömb azt a nézetablakon belüli négyszöget írja le, amelyet a mérés valójában lefed, ami nem mindig a teljes téglalap.
Gyakorlati háttér
A GPTS-ben lévő szélességi és hosszúsági fok értelmetlen anélkül, hogy tudnánk, melyik földrajzi koordináta-rendszerhez tartoznak, mivel a 51.5, -0.1 koordináta más fizikai helyre esik a WGS 84 alatt, mint egy régebbi nemzeti dátum (datum) alatt. A méret-szótár erre egy koordináta-rendszer szótárral válaszol, amelyet a földrajzi rendszerhez a GetMeasureDictGCSDict segítségével érhetünk el. PDF describes that system in one of two interchangeable ways, and a reader has to accept either.
Az első a WKT (Well-Known Text), egy önálló karakterlánc, amely teljes egészében leírja a dátumot, az ellipszoidot, a kezdőmeridiánt és az egységeket. Részletes, de egyértelmű, és nem igényel külső keresőtáblát. A második az EPSG kód, egyetlen egész szám, amely egy koordináta-rendszert indexel az EPSG regiszterben; a 4326 a WGS 84, az a keret, amelyet a legtöbb fogyasztói GPS adat használ. Az EPSG kompakt, de feltételezi, hogy az olvasó fel tudja oldani a kódot egy adatbázissal szemben. Files appear with one, the other, or both, which is why the API surfaces all three of GetCSDictType, GetCSDictEPSG, and GetCSDictWKT. GetCSDictType reports whether the system is geographic (a GEOGCS, return value 1) or projected (a PROJCS, return value 2), letting you interpret the rest correctly before you trust it.
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;
Az örökölt LGIDict beolvasása
A nézetablak modellt megelőző fájlok, vagy a még mindig a régebbi kódolást kibocsátó eszközökkel előállított fájlok a regisztrációjukat az oldalon lévő LGIDict-ben hordozzák a méret-szótár helyett. A PDFlibPas a GetPageLGIDictCount segítségével jelzi, hány ilyen szótára van egy oldalnak, és a GetPageLGIDictContent segítségével adja vissza mindegyik nyers tartalmát egyről indexelve. A visszakapott szöveg a szótár úgy, ahogy le van írva, tartalmazva az OGC 08-139r2 regisztrációs mezőket, amelyeket a kódja ezután elemez, hogy visszanyerje ugyanazt az oldal-világ leképezést, amelyet a méret-szótár biztosít. Az írói oldalon az AddLGIDictToPage csatol egy LGIDict-et az aktuális oldalhoz, így egy konverter képes oda-vissza alakítani az örökölt formát, ha a régi fogyasztó még mindig ezt várja.
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;
A beolvasás összerakása
Egy teljes importer a két sémát két menetként kezeli minden oldalon. Válassza ki az oldalt, kérdezze le a GetPageViewPortCount-ot az ISO nézetablakokhoz, és minden olyan nézetablakhoz, amely méret-szótárral rendelkezik, húzza le a BBox-ot, a GPTS és LPTS tömböket, a pontadat egységet és a GCS leírást a koordináta-rendszer szótáron keresztül. Ezután ellenőrizze a GetPageLGIDictCount-ot minden olyan örökölt regisztrációhoz, amelyet a nézetablak menet nem fedett le. Egy mindkettőt hordozó térképnek meg kell egyeznie egymással; az csak az egyiket hordozó térkép szintén feloldódik, mert mindkét helyen kereste. Az út során visszakapott kezelők (ViewPortID, MeasureDictID, CSDictID) egyszerű egészek, amelyek érvényesek maradnak a dokumentum betöltése alatt, így a teljes bejárás néhány egymásba ágyazott ciklus az oldallistában, lefoglalás-kezelés nélkül.
Once you can recover the registration, the page becomes a data source rather than a picture. The companion techniques for reading the rest of a page are covered in the article on text, image, and font extraction, and rendering a georeferenced sheet to a device for on-screen measurement is described in the print and preview device-context walkthrough. The geospatial reader described here ships as part of the losLab PDF Library for Delphi and C++Builder, alongside the loading, extraction, and rendering APIs covered elsewhere on this blog.