Műszaki cikk

GeoPDF Delphiben: Térinformatikai koordináták PDF Library for Delphi segítségével

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

PDF Library for Delphi diagram: az ISO viewport és measure szótár georegisztrációs modellje szemben a régebbi GeoPDF fájlok által hordozott LGIDict sémával
Két georegisztrációs séma kering valódi GeoPDF fájlokban, és egy oldal hordozhatja az egyiket, a másikat vagy mindkettőt. Mindkét út oldalkoordinátákat bont szélességre és hosszúságra

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

PDF Library for Delphi 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

PDF Library for Delphi: PDF-oldal makett: fő térkép viewport, beszúrt viewport és hivatkozás nélküli jelmagyarázat, kattintásos találatvizsgálattal minden viewport BBoxán
Minden viewport csak a saját BBox-án belüli területet irányítja, így a találatvizsgálat megmondja az olvasónak, mely mérték-szótár érvényes. A nulla ViewPortID azt jelenti, hogy a kért viewport nem található
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);
      // A Left/Top/Width/Height a térkép területét írja le ehhez a nézetablakhoz
    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

PDF Library for Delphi: párosított LPTS oldalterületi törtek és GPTS szélességi-hosszúsági tömbök, elemenként összekapcsolva az affin oldal-világ transzformáció rögzítéséhez
Az n. LPTS elem és az n. GPTS elem ugyanazt a fizikai helyet nevezi meg egyszer az oldalon, egyszer a földgömbön. Három vagy több pár rögzíti az affin vagy projekciós transzformációt
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);
    // A GPTS szélességi/hosszúsági pontpárokat tárol; az LPTS a megfelelő oldaltörteket.
    // Mindkét tömböt egyalapú elemindexekkel olvassuk be.
    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);
      // A (px, py) az oldalon a (lat, lon) földi koordinátának felel meg
      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);   // pl. 4326 a WGS 84-hez, 0, ha hiányzik
    wkt    := Pdf.GetCSDictWKT(gcsID);    // teljes szöveges leírás, '', ha hiányzik
    // Ha van EPSG, azt részesítsük előnyben; egyébként a WKT elemzésére essünk vissza.
  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 PDF Library for Delphi 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);
    // A dictText hordozza az elemzendő OGC 08-139r2 regisztrációt
  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