Tehnički članak

GeoPDF u Delphi-ju: Geospatne koordinate s PDF Library for Delphi-om

Većina programera misli o PDF stranici kao o listu papira s tekstom i slikama na njemu. Georeferencirani PDF je više od toga. On nosi dovoljno informacija da uzme točku na stranici, izmjerenu u običnim jedinicama stranice, i prijavi geografsku širinu i dužinu na kojoj se nalazi u stvarnom svijetu. Ta jedina činjenica je ono što pretvara PDF u koristan nosač za topografsku kartu, katastarski plan, prikaz poplavne zone ili bilo koji GIS izvoz koji se mora ispisati i dalje imati smisla. Geometrija je tamo u datoteci; jedino je pitanje čita li je vaš učitač

Razlog zašto se to propušta jest taj što se GeoPDF otvara i ispisuje točno kao i svaki drugi PDF. Ništa na prikazanoj stranici ne najavljuje da je karta registrirana u koordinatnom sustavu. Registracija živi u rječnicima koji vise s objekta stranice, nikada se ne crtaju, a preglednik koji ih zanemaruje svejedno vam prikazuje kartu. Da biste radili bilo što prostorno s datotekom - očitanja koordinata izmjere, reprojekciju, preklapanje s drugim slojevima - morate sami proći kroz te rječnike

Dva standarda žive u divljini

Čitač koji želi rukovati datotekama iz stvarnog svijeta mora se nositi s dvije sheme georegistracije, jer su obje u optjecaju i određena datoteka može koristiti bilo koju. Starija je OGC kodiranje opisano u OGC 08-139r2, koje stranici pridružuje LGIDict (rječnik geoprostorne registracije). Ona prethodi bilo kakvom ISO blagoslovu i bila je de facto format za rane GeoPDF izlaze, tako da veliko tijelo naslijeđenih karata nosi nju i ništa drugo

Moderna shema je ona koju je ISO standardizirao u ISO 32000-1 §8.8.2. Umjesto jednog rječnika na razini stranice, ona modelira geoprostorne podatke kao Viewport (vidni okvir) stranice s priloženim rječnikom Measure (mjera), a rječnik mjera imenuje geografski koordinatni sustav. To je kodiranje koje pišu Acrobat i trenutni GIS izvoznici. Robusni uvoznik provjerava oboje: čita vidne okvire za ISO model i vraća se na (ili dodatno provjerava) LGIDict za datoteke koje nose samo naslijeđenu registraciju

PDF Library for Delphi dijagram koji suprotstavlja ISO model georegistracije s viewportom i mjernim rječnikom starijoj LGIDict shemi koju nose starije GeoPDF datoteke
Dvije sheme georegistracije kruže u stvarnim GeoPDF datotekama, a stranica može nositi jednu, drugu ili obje. Svaka putanja koordinate stranice razrješava u geografsku širinu i dužinu

Vidni okviri i njihove granice

U ISO modelu jedinica georegistracije je vidni okvir, a stranica ih može imati nekoliko. Veliki list može postaviti glavnu kartu u jedan pravokutnik, umetak u drugom mjerilu u drugi, a ploču s legendom koja uopće nije georeferencirana u treći. Svaki vidni okvir nosi BBox, pravokutnik na stranici kojim vidni okvir upravlja, tako da čitač zna na koji se dio lista primjenjuje određeni koordinatni sustav. Provjera pogotka kliknute točke na te okvire je način na koji preglednik odlučuje koji rječnik mjera koristiti

PDF Library for Delphi izlaže vidne okvire odabrane stranice direktno. GetPageViewPortCount vraća koliko ih ima, GetPageViewPortID pretvara indeks baziran na jedinici u ViewPortID ručku, a GetViewPortBBox čita granični pravokutnik jednu po jednu dimenziju. Argument Dimension odabire koji rub ili opseg želite: 0 je Left, 1 je Top, 2 je Width, 3 je Height, 4 je Right, i 5 je 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 opisuju područje karte za ovaj viewport
    end;
  finally
    Pdf.Free;
  end;
end;

ViewPortID s vrijednošću nula iz GetPageViewPortID znači da se vidni okvir na tom indeksu nije mogao pronaći, pa ga provjerite prije prosljeđivanja ručke dalje

Unutar rječnika mjera

Geometrija koja registrira stranicu u svijet živi u rječniku mjera pridruženom vidnom okviru. GetViewPortMeasureDict vraća MeasureDictID za zadani ViewPortID, ili nulu kada vidni okvir nema rječnik mjera, što je uobičajen slučaj za legendu ili ploču s naslovom. Rječnik mjera sadrži tri stvari koje vrijedi pročitati: koordinatne sustave na koje se odnosi, nizove koji povezuju točke stranice s geografskim točkama i jedinicu u kojoj su izraženi podaci o točkama

Sama registracija su dva paralelna niza. GPTS is the array of geographic points, latitude and longitude pairs given in the geographic coordinate system. LPTS is the array of page-space points, expressed as fractions of the viewport's BBox so they survive scaling. Item n u LPTS-u i stavka n u GPTS-u imenuju istu fizičku lokaciju, jednom u koordinatama stranice i jednom na globusu. Tri ili više takvih parova određuju afinu, ili u općem slučaju projektivnu, transformaciju koja preslikava bilo koju koordinatu stranice unutar vidnog okvira u svjetsku koordinatu. Njihovo čitanje je stvar istovremenog prolaska kroz oba niza

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 sadrži parove lat/lon; LPTS sadrži odgovarajuće razlomke stranice.
    // Oba polja se čitaju s jedno-indeksiranim indeksima stavki.
    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) na stranici odgovara (lat, lon) na tlu
      Inc(j, 2);
    end;
  end;
end;

Rječnik mjera također izvještava o svojim jedinicama prikaza putem GetMeasureDictPDU, koji uzima UnitIndex s vrijednošću 1 za linearne, 2 za površinske ili 3 za kutne jedinice i vraća kod koji identificira određenu jedinicu, na primjer metar ili međunarodnu stopu za linearnu kategoriju. Niz Bounds, čitan s GetMeasureDictBoundsItem, opisuje četverokut unutar vidnog okvira koji mjerenje zapravo pokriva, što nije uvijek puni pravokutnik

WKT naspram EPSG-a

Geografska širina i dužina u GPTS-u su besmislene bez znanja kojem geografskom koordinatnom sustavu pripadaju, budući da koordinata 51.5, -0.1 slijeće na drugo fizičko mjesto pod WGS 84 nego pod starijim nacionalnim datumom. Rječnik mjera odgovara na to kroz rječnik koordinatnog sustava, do kojeg se dolazi s GetMeasureDictGCSDict za geografski sustav. PDF opisuje taj sustav na jedan od dva međusobno zamjenjiva načina, a čitač mora prihvatiti bilo koji

PDF Library for Delphi: maketa PDF stranice koja prikazuje glavni viewport karte, umetnuti viewport i nepovezanu legendu s testiranjem klikova na BBox svakog viewporta
Svaki viewport upravlja samo područjem unutar svog BBox, pa hit-testiranje kaže čitaču koji se rječnik mjera primjenjuje. Nula u ViewPortID znači da traženi viewport nije pronađen

Prvi je WKT, Well-Known Text, samostalan niz koji u potpunosti definira datum, elipsoid, početni meridijan i jedinice. Opširan je, ali nedvosmislen i ne treba vanjsku tablicu pretraživanja. Drugi je EPSG kod, jedan cijeli broj koji indeksira koordinatni sustav u EPSG registru; 4326 je WGS 84, okvir koji koristi većina potrošačkih GPS podataka. EPSG je kompaktan, ali pretpostavlja da čitač može riješiti kod u odnosu na bazu podataka. Datoteke se pojavljuju s jednim, drugim ili oboma, zbog čega API prikazuje sva tri poziva GetCSDictType, GetCSDictEPSG i GetCSDictWKT. GetCSDictType javlja je li sustav geografski (GEOGCS, povratna vrijednost 1) ili projiciran (PROJCS, povratna vrijednost 2), omogućujući vam da ispravno protumačite ostatak prije nego što mu povjerujete

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);   // npr. 4326 za WGS 84, 0 ako nema
    wkt    := Pdf.GetCSDictWKT(gcsID);    // puni tekstualni opis, '' ako nema
    // Preferiraj EPSG kad postoji; inače se oslanjaj na parsiranje WKT.
  end;
end;

Čitanje naslijeđenog LGIDict-a

Naslijeđeni OGC model georegistracije ne koristi vidne okvire. LGIDict je rječnik na razini stranice koji sadrži izravne informacije o projekciji i transformacijske parove. Iako je zastario prema ISO standardu, i dalje je prisutan u mnogim GIS izvozima. PDF Library for Delphi pruža pozive za provjeru i čitanje ovog rječnika ako vidni okviri nisu definirani

var
  lgiCount, k: Integer;
  dictText: WideString;
begin
  lgiCount := Pdf.GetPageLGIDictCount;
  for k := 1 to lgiCount do
  begin
    dictText := Pdf.GetPageLGIDictContent(k);
    // dictText nosi OGC 08-139r2 registraciju za parsiranje
  end;
end;

Spajanje očitanja

Potpuni uvoznik tretira dvije sheme kao par prolaza preko svake stranice. Odaberite stranicu, zatražite od GetPageViewPortCount ISO vidne okvire, i za svaki vidni okvir koji posjeduje rječnik mjera izvucite njegov BBox, njegove GPTS i LPTS nizove, njegovu jedinicu podataka točke i opis GCS-a kroz rječnik koordinatnog sustava. Zatim provjerite GetPageLGIDictCount za bilo koju naslijeđenu registraciju koju prolaz vidnog okvira nije pokrio. Karta koja nosi oboje trebala bi se slagati; karta koja nosi samo jedno i dalje se rješava, jer ste tražili na oba mjesta. Ručke vraćene usput, ViewPortID, MeasureDictID, CSDictID, obični su cijeli brojevi koji ostaju važeći dok je dokument učitan, tako da je cijeli prolaz nekoliko ugniježđenih petlji preko popisa stranica bez alokacije kojom treba upravljati

PDF Library for Delphi: upareni LPTS frakciji prostora stranice i GPTS nizovi geografske širine i dužine spojeni stavka po stavka radi fiksiranja afinog transformiranja stranice u svijet
LPTS stavka n i GPTS stavka n imenuju istu fizičku lokaciju jednom na stranici i jednom na globusu. Tri ili više parova fiksiraju afina ili projektivnu transformaciju

Jednom kada možete oporaviti registraciju, stranica postaje izvor podataka, a ne slika. Prateće tehnike za čitanje ostatka stranice pokrivene su u članku o ekstrakciji teksta, slika i fontova, a iscrtavanje georeferenciranog lista na uređaj za mjerenje na zaslonu opisano je u vodiču kroz kontekst uređaja za ispis i pregled. Geospatni čitač koji je ovdje opisan isporučuje se kao dio softvera losLab PDF Library za Delphi i C++Builder, zajedno s API-jima za učitavanje, ekstrakciju i prikazivanje koji su pokriveni drugdje na ovom blogu