일반적으로 개발자들은 PDF 페이지를 텍스트와 그림이 인쇄된 흰 종이로 생각합니다. 하지만 지리 정보 속성이 포함된(georeferenced) PDF는 단순한 문서 그 이상입니다. 페이지 내부의 특정 지점의 가상 레이아웃 포인트 좌표 정보를 받아서, 해당 점이 지구 표면의 위도와 경도 기준으로 어디에 위치하는지 실시간 산출하는 데 필요한 지리 데이터를 담고 있습니다. 덕분에 PDF는 지형도 도면, 지적 경계 도표, 침수 피해 예측도, 혹은 종이에 출력해 사용해야 하는 모든 종류의 GIS 좌표 명세서 데이터를 보관하는 유용한 아카이브 포맷이 됩니다. 좌표 데이터는 파일 내에 완벽히 들어 있으며, 남은 임무는 페이지 로더가 이를 파싱해 내는 것입니다.
이 기능을 간과하기 쉬운 이유는 GeoPDF 문서 역시 화면 preview 및 인쇄 출력 시 일반 PDF 문서와 100% 동일하게 움직이기 때문입니다. 렌더링된 인쇄 캔버스 표면상에는 실제 좌표 매핑 정보가 활성화되어 있는지에 대한 어떤 표시도 없습니다. 지리 좌표 매핑 정보는 페이지 객체 하위에 계층 구조의 사전(dictionary) 속성 노드로 매달려 화면에는 그려지지 않으며, 이를 인식하지 못하는 레거시 뷰어들은 평범한 지도 그림 파일 형태로만 문서를 렌더링합니다. 이 파일로부터 토지 측량 결과 산출, 타 좌표계 투영 변환, 그리고 다른 레이어 도면과의 위치 보정 겹치기 등 지리 정보 작업을 수행하려면 개발자가 수동으로 사전 구조 노드를 탐색해야 합니다.
업계에 통용되는 두 가지 표준 규격
다양한 생성처에서 빌드된 실무 도면을 읽어 들이려면 두 지리 정보 매핑 메커니즘을 모두 해독할 수 있어야 합니다. 업계에 두 방식이 혼용되어 쓰이고 있기 때문입니다. 비교적 역사가 긴 방식은 OGC 08-139r2 문서에 정의된 OGC 인코딩 형태이며, 페이지 객체 하위에 LGIDict(지리 정보 매핑 정의 사전) 노드를 직접 임베드합니다. ISO 표준 제정 전에 만들어졌고 초기 GeoPDF 툴들의 기본 포맷으로 광범위하게 쓰였기 때문에, 기존에 보관된 역사 지형도 데이터 상당수가 이 방식으로 보관되어 있습니다.
보다 현대적인 방식은 ISO 32000-1 §8.8.2에 정립된 공식 표준입니다. 단순 페이지 사전 구조 대신 페이지 내 Viewport(뷰포트)와 여기에 바인딩된 Measure(측정) 사전 노드를 연계해 지리 지식을 모델링하며, 측정 사전 내부에 실제 지구 좌표계를 선언합니다. Adobe Acrobat을 포함한 최신 GIS 전용 인코더 장비들이 이 규격을 생성합니다. 견고하게 동작해야 하는 지리 정보 임포터는 두 방식을 단계적으로 교차 검사해야 합니다. 먼저 ISO 규격의 뷰포트 사전을 우선 검색해 값을 확인하고, 레거시 도면 유입에 대처할 수 있도록 차선책으로 LGIDict 데이터가 심어져 있는지를 이중으로 체크해야 합니다.
뷰포트 정의 영역 및 바운딩 사각형
ISO 규격 하에서 지리 매핑 정보의 수용 단위는 뷰포트이며, 한 페이지 내에 여러 개 존재할 수 있습니다. 예를 들어 널찍한 도면 용지 내 특정 사각형 사방 영역에는 핵심 광역 지형도를 배치하고, 구석 영역에는 축척 비율이 상이한 국소 확대도를 얹으며, 나머지 여백부에는 지리 정보가 없는 범례 안내문을 배치하는 구조입니다. 각 뷰포트는 지리 영역을 강제할 문서 내 가상 영역 정의 프레임인 BBox 영역 정보를 가집니다. 덕분에 로더는 페이지 내 어떤 위치에 특정 좌표 정보 공식이 유효한지 파악할 수 있으며, 사용자가 마우스로 클릭한 포인트 위치가 해당 BBox 프레임 내에 포함되는지 체크(hit-test)하여 적절한 지리 사전 노드를 선택해 계측 연산을 대입해 줍니다.
PDFlibPas는 현재 지정된 페이지의 뷰포트 정보를 직접 전달해 줍니다. GetPageViewPortCount는 해당 뷰포트의 총개수를 확인하고, GetPageViewPortID는 1 기반의 목록 인덱스를 입력받아 가상 뷰포트 핸들(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;
GetPageViewPortID 호출 결과 뷰포트 ID가 0으로 확인되면 지리 영역을 탐색할 수 없음을 뜻하므로, 해당 가상 핸들 주소를 타 함수 매개변수로 전송하기 전에 유효성을 확인하십시오.
측정 사전 노드 내부 구조
가상 문서 지점 좌표를 실제 지구 표면 위치로 보정하는 공식 데이터는 뷰포트에 매핑된 측정 사전에 존재합니다. GetViewPortMeasureDict는 지정한 뷰포트 ID에 매핑된 측정 사전 식별 주소(MeasureDictID)를 반환하며, 범례 판넬처럼 매핑 정보가 없으면 0을 리턴합니다. 측정 사전 노드에서 읽어내야 할 핵심 구성원은 다음과 같이 세 가지입니다. 매칭에 투입된 기준 지리 좌표계 정보, 레이아웃 공간 좌표를 지구 지리 좌표와 연동시키는 대응 좌표 리스트 어레이, 그리고 대응 데이터 표기에 쓰인 치수 단위 코드입니다.
대응 관계 좌표 목록 정보는 두 가지 대칭형 리스트 어레이로 구성됩니다. GPTS는 위도와 경도 눈금값 정보가 좌표계 규격 순서대로 수반된 지구 실제 위치 정보 좌표 리스트이며, LPTS는 화면 비율 크기 가변 상태에서도 배율 오차 영향이 없도록 뷰포트 BBox를 기준으로 환산된 가상 페이지 비율 좌표 리스트입니다. LPTS와 GPTS의 n번째 좌표 쌍은 동일한 실제 지점을 의미하며, 각각 가상 도면 좌표와 지구 실제 지리 좌표 위치에 1대 1 대입됩니다. 최소 3쌍 이상의 대응 좌표 데이터가 확보되면, 아핀(affine) 투영 공식 혹은 투영 변환 공식을 연립방정식 형태로 해를 구해 내어, BBox 내 임의의 가상 지점을 위경도로 매핑 변환할 변환 매트릭스를 산출해 낼 수 있습니다. 이 과정은 두 어레이 루프를 동일 순서로 동시에 탐색하며 값을 매핑하는 방식으로 가동됩니다.
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 메서드를 통해 사용 단위 규격 코드도 보고합니다. 1은 선형 거리, 2는 평면 면적, 3은 도 각도 단위를 조회하며, 미터 단위 코드나 피트 단위 명칭 코드를 리턴해 줍니다. GetMeasureDictBoundsItem으로 파싱하는 Bounds 좌표 어레이는, 사각형 BBox 프레임 내에서도 실제 측정 공식 연산이 보장되는 다각형 세부 공간 경계를 실측해 알려 줍니다.
WKT 대 EPSG
지구 위경도 값 정보는 측정에 쓰인 고유 지구 타원체 좌표계 정의 형식을 모르면 연산이 무의미합니다. 동일한 51.5, -0.1 위경도 값이라도 WGS 84 체계 하에서의 지점과 과거 측정된 국가 측지계 기준 위치는 전혀 엉뚱한 국가 지점으로 뒤틀리기 때문입니다. 측정 사전은 GetMeasureDictGCSDict를 통해 도출되는 측지 좌표계 설명 사전 정보로 이에 대응합니다. PDF 포맷은 이를 위해 교차 표기가 가능한 두 지리 설명 규격 형식을 수반하며, 로더는 두 형식을 모두 판독할 수 있어야 합니다.
첫 번째 방식은 WKT(Well-Known Text) 규격으로, 기준 타원체, 지구 중심 자오선, 축척 치수 명칭 정보 등을 평문 텍스트 설명 구조로 서술해 놓은 포맷입니다. 서술부가 길지만 명백하며 외부 정보 사전 조회를 생략할 수 있습니다. 두 번째는 EPSG 지리 색인 코드로, 세계 기준 EPSG 등록 데이터베이스 상의 좌표계 번호를 의미하는 정수 번호입니다. WGS 84 지리계는 4326 코드로 함축 정의되며, 현대적 GPS 수신기에서 도출되는 데이터와 부합합니다. 규격은 소형화되어 편리하지만 로더 프로세스 내에 EPSG 번호 사전을 내포하고 있어야 해독이 가능합니다. 이 규격들이 다양하게 수반되므로 API는 GetCSDictType, GetCSDictEPSG, GetCSDictWKT 파싱 함수들을 독립 노출합니다. GetCSDictType은 측지계 정보가 구체 형태의 경위도 형식(GEOGCS, 1값)인지 2D 평면 투영 공식 형식(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 파싱 가이드
과거의 뷰포트 규격 제정 전에 작성되었거나 과거 인코딩 명세를 탑재한 구형 GIS 내보내기 툴에서 빌드된 데이터는 뷰포트 사전 대신 페이지 개체 표면에 바로 LGIDict 정의 텍스트 블록 정보 사전을 주입합니다. PDFlibPas 컴포넌트는 GetPageLGIDictCount로 이 노출 개수를 감지하고, 1 기반 인덱스 형식으로 GetPageLGIDictContent 함수를 돌려 개별 XML 사본 속성 텍스트를 고스란히 뽑아 전달해 줍니다. 획득한 평문 정보를 OGC 08-139r2 지리 사전 규칙에 맞춰 파싱하면, 뷰포트 측정을 파싱할 때와 완전히 동일한 계측 아핀 공식을 유도해 낼 수 있습니다. 역으로 레거시 정보 판독기 호환 유통이 요구되는 도면을 쓸 때는, AddLGIDictToPage 함수를 사용해 현재 기록 중인 페이지 내부에 지리 정의 사전을 강제 각인시켜 보낼 수 있습니다.
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;
전체 파싱 프로세스의 통합 설계
완벽히 동작해야 하는 GIS 데이터 리더는 각 페이지별로 이 두 지리 지표 검사 경로를 단계적으로 순회하는 프로세스로 설계해야 합니다. 페이지를 고르고, GetPageViewPortCount로 최신 ISO 뷰포트를 읽은 다음, 유효 측정 노드가 확인되는 뷰포트 내부에서 BBox 크기 정보, GPTS/LPTS 대응 좌표 매트릭스 목록, 기본 거리 단위, 타원체 정의 정보를 순차 수집합니다. 이후 GetPageLGIDictCount 함수를 사용해 ISO 뷰포트에 포함되지 않은 레거시 LGIDict 지리 데이터가 있는지 이중 체크해야 합니다. 두 방식이 혼재된 도면 데이터라도 공통 좌표 위치로 동일 해석되어 수렴할 것이며, 하나의 방식만 등재된 문서라도 누수 없이 판독해 낼 수 있습니다. 이 과정에서 획득하게 되는 ViewPortID, MeasureDictID, CSDictID 식별자 값들은 메모리 소멸이 일어나지 않는 기본 자료형 정수 포인터들이므로, 가상 문서가 열려 있는 한 복잡한 메모리 해제 고민 없이 단순 중첩 루프문만으로 깔끔하게 데이터를 읽어 낼 수 있습니다.
지리 정보 좌표 데이터 획득 경로가 확보되면 도면 페이지는 단순한 지형도 그림이 아니라, 실제 물리 지구 정보를 보관한 데이터 저장소 형태로 활용되기 시작합니다. 페이지 내 일반 텍스트 정보 및 그래픽 요소를 읽어내는 방법은 PDF 텍스트 이미지 추출 안내문을 참고할 수 있으며, 지리 도면을 화면 출력 장치 크기에 맞춰 고속 묘사하는 렌더링 기술은 프린트 뷰 캔버스 장치 연동 기술 문서에 기록되어 있습니다. 지리 정보 구문 분석 기술은 Delphi 및 C++Builder용 losLab PDF Library 제품에 통합되어 로딩 및 렌더링 기술과 함께 제공됩니다.