De fleste utviklere tenker på en PDF-side som et ark med tekst og bilder på. En georeferert PDF er mer enn det. Den bærer nok informasjon til å ta et punkt på siden, målt i vanlige sideenheter, og rapportere bredde- og lengdegraden det ligger over i den virkelige verden. Dette enkle faktumet er det som gjør en PDF til en brukbar bærer for et topografisk kart, et eiendomskart, en flomsone-illustrasjon, eller enhver GIS-eksport som må skrives ut og fortsatt bety noe. Geometrien finnes der i filen; det eneste spørsmålet er om innleseren din leser den.
Grunnen til at dette blir oversett, er at en GeoPDF åpnes og skrives ut nøyaktig som alle andre PDF-er. Ingenting på den rendret siden kunngjør at kartet er registrert til et koordinatsystem. Registreringen lever i ordbøker som henger på sideobjektet, som aldri tegnes, og et visningsprogram som ignorerer dem viser deg kartet likevel. For å gjøre noe romlig med filen, som avlesing av landmålingskoordinater, reprojeksjon eller overlegg mot andre lag, må du gå gjennom disse ordbøkene selv.
To standarder lever i det fri
En leser som ønsker å håndtere reelle filer, må takle to georegistreringsmetoder, ettersom begge er i omløp og en gitt fil kan bruke hvilken som helst av dem. Den eldste er OGC-kodingen beskrevet i OGC 08-139r2, som knytter en LGIDict (en ordbok for geospasial registrering) til siden. Den er eldre enn noen ISO-godkjenning og var de facto-formatet for tidlige GeoPDF-utdata, så en stor mengde eldre kart inneholder denne og ingenting annet.
Den moderne metoden er den som ISO standardiserte i ISO 32000-1 §8.8.2. I stedet for en enkelt ordbok på sidenivå modellerer den geospasiale data som en side Viewport med en tilknyttet Measure-ordbok, og måleordboken navngir et geografisk koordinatsystem. Dette er kodingen Acrobat og gjeldende GIS-eksportører skriver. En robust importør sjekker begge: Les viewporter for ISO-modellen, og fall tilbake til (eller inspiser i tillegg) LGIDict for filer som bare har den eldre registreringen.
Viewporter og deres grenser
I ISO-modellen er enheten for georegistrering en viewport, og en side kan ha flere. Et stort ark kan plassere et hovedkart i ett rektangel, et innfelt kart i en annen skala i et annet, og et tegnforklaringspanel som ikke er georeferert i det hele tatt. Hver viewport har en BBox, rektangelet på siden som viewporten styrer, slik at leseren vet hvilken del av arket et gitt koordinatsystem gjelder for. Å teste et klikket punkt mot disse boksene er måten en viser bestemmer hvilken måleordbok som skal brukes.
PDFlibPas eksponerer viewportene på den valgte siden direkte. GetPageViewPortCount returnerer hvor mange det finnes, GetPageViewPortID gjør en en-basert indeks om til et ViewPortID-håndtak, og GetViewPortBBox leser det avgrensende rektangelet én dimensjon om gangen. Argumentet Dimension velger hvilken kant eller utstrekning du vil ha: 0 er Left, 1 er Top, 2 er Width, 3 is Height, 4 er Right og 5 er 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;
En ViewPortID på null fra GetPageViewPortID betyr at viewporten på denne indeksen ikke ble funnet, så sjekk den før du sender håndtaket videre.
Inne i måleordboken
Geometrien som registrerer siden til verden, lever i måleordboken tilknyttet en viewport. GetViewPortMeasureDict returnerer en MeasureDictID for en gitt ViewPortID, eller null når viewporten ikke har noen måleordbok, noe som er normalt for en tegnforklaring eller et tittelpanel. Måleordboken inneholder tre ting som er verdt å lese: Koordinatsystemene den refererer til, arrayene som knytter sidepunkter til geografiske punkter, og enheten som punktdatene er uttrykt i.
Selve registreringen er to parallelle arrayer. GPTS er arrayen med geografiske punkter, bredde- og lengdegradspar gitt i det geografiske koordinatsystemet. LPTS er arrayen med punkter på siden, uttrykt som brøkdeler av viewportens BBox, slik at de overlever skalering. Element n i LPTS og element n i GPTS navngir samme fysiske plassering, én gang i sidekoordinater og én gang på jordkloden. Tre eller flere slike par fastsetter den affine, eller generelt projektive, transformasjonen som mapper enhver sidekoordinat inne i viewporten til en verdenskoodinat. Å lese dem er et spørsmål om å gå gjennom begge arrayene i takt.
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;
Måleordboken rapporterer også sine visningsenheter via GetMeasureDictPDU, som tar en UnitIndex på 1 for lineære, 2 for areal- eller 3 for vinkelenheter og returnerer en kode som identifiserer den spesifikke enheten, for eksempel en meter eller en internasjonal fot for den lineære kategorien. Arrayen Bounds, som leses med GetMeasureDictBoundsItem, beskriver firkanten innenfor viewporten som målingen faktisk dekker, noe som ikke alltid er hele rektangelet.
WKT mot EPSG
Bredde- og lengdegraden i GPTS er meningsløse uten å vite hvilket geografisk koordinatsystem de tilhører, ettersom en koordinat på 51,5, -0,1 lands in a different physical spot under WGS 84 than under an older national datum. Måleordboken besvarer dette via en ordbok for koordinatsystemer, nådd med GetMeasureDictGCSDict for det geografiske systemet. PDF beskriver dette systemet på én av to utskiftbare måter, og en leser må godta begge.
Den første er WKT, Well-Known Text, en selvstendig streng som staver ut datum, ellipsoide, nullmeridian og enheter i sin helhet. Den er ordrik, men entydig og trenger ingen ekstern oppslagstabell. Den andre er en EPSG-kode, et enkelt heltall som indekserer et koordinatsystem i EPSG-registeret; 4326 er WGS 84, referansen de fleste GPS-data for forbrukere bruker. EPSG er kompakt, men forutsetter at leseren kan slå opp koden mot en database. Filer dukker opp med den ene, den andre eller begge, noe som er grunnen til at API-et tilbyr alle tre av GetCSDictType, GetCSDictEPSG, og GetCSDictWKT. GetCSDictType rapporterer om systemet er geografisk (et GEOGCS, returverdi 1) eller projisert (et PROJCS, returverdi 2), slik at du kan tolke resten riktig før du stoler på det.
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;
Lese den eldre LGIDict
Filer som er eldre enn viewport-modellen, eller som ble produsert av verktøy som fortsatt sender ut den eldre kodingen, har sin registrering i en LGIDict på siden i stedet for i en måleordbok. PDFlibPas rapporterer hvor mange slike ordbøker en side har via GetPageLGIDictCount og gir tilbake det rå innholdet i hver med GetPageLGIDictContent, indeksert fra én. Den returnerte teksten er ordboken slik den ble skrevet, og inneholder OGC 08-139r2-registreringsfeltene, som koden din deretter tolker for å gjenvinne samme type side-til-verden-mapping som måleordboken gir. På skrivesiden knytter AddLGIDictToPage en LGIDict til den gjeldende siden, slik at en konverterer kan round-trippe den eldre formen når en gammel mottaker fortsatt forventer det.
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;
Sette innlesingen sammen
En komplett importør behandler de to metodene som to omganger over hver side. Velg siden, be GetPageViewPortCount om ISO-viewporter, og for hver viewport som eier en måleordbok, hent ut dens BBox, dens GPTS- og LPTS-arrayer, dens punktdatapresentasjon og GCS-beskrivelsen via ordboken for koordinatsystemer. Sjekk deretter GetPageLGIDictCount for eventuell eldre registrering som viewport-omgangen ikke dekket. Et kart som har begge deler bør ha samsvar mellom dem; et kart som bare har det ene løses likevel, fordi du lette begge steder. Håndtakene som returneres underveis, ViewPortID, MeasureDictID, CSDictID, er vanlige heltall som forblir gyldige så lenge dokumentet er lastet, slik at hele gjennomgangen er noen få nestede løkker over sidelisten uten tildelinger å administrere.
Når du kan gjenvinne registreringen, siden blir en datakilde i stedet for bare et bilde. De tilhørende teknikkene for å lese resten av en side er dekket i artikkelen om tekst-, bilde- og fontuttrekking, og rendring av et georeferert ark til en enhet for måling på skjermen er beskrevet i gjennomgangen av utskrifts- og forhåndsvisningsenhetskontekst. Den geospasiale leseren beskrevet her leveres som en del av losLab PDF Library for Delphi og C++Builder, sammen med API-ene for lasting, uttrekking og rendring som er dekket andre steder på denne bloggen.