C++를 활용한 PROJ 라이브러리 좌표 변환 API 구현 및 활용

PROJ 라이브러리 개요

PROJ는 지리 공간 데이터의 좌표계를 서로 다른 기준 좌표계(CRS)로 변환하는 데 특화된 오픈소스 라이브러리입니다. 명령줄 인터페이스(CLI)뿐만 아니라 C/C++ API를 통해 애플리케이션에 직접 통합할 수 있습니다. 이 글에서는 C++ 환경에서 PROJ의 C API를 호출하여 좌표 변환 기능을 구현하고 활용하는 방법을 다룹니다.

CentOS 환경에서의 설치 및 검증

CentOS 7.9 환경을 기준으로 설명합니다. C++ 코드에서 API를 호출하려면 런타임 공유 라이브러리와 개발용 헤더 파일 및 정적 라이브러리가 모두 필요합니다.

sudo yum install proj proj-devel

설치가 정상적으로 완료되었는지 확인하기 위해 핵심 파일의 존재 여부를 검사합니다.

헤더 파일 확인:

ls /usr/include/proj_api.h

위 명령어 실행 시 proj_api.h 파일이 출력되면 개발 패키지가 성공적으로 설치된 것입니다.

라이브러리 파일 확인:

ls /usr/lib64/libproj.so*

공유 객체 파일이 존재하면 런타임 라이브러리 설치가 완료된 것입니다.

주요 API 및 자료형

참고: 본 구현은 PROJ 4.x 및 5.x 버전의 proj_api.h 헤더를 기반으로 합니다. PROJ 6.0 이상 버전에서는 proj.h를 사용하는 새로운 API로 마이그레이션되었으므로, 최신 환경에서는 공식 문서의 마이그레이션 가이드를 참고해야 합니다.

  • projPJ: 초기화된 투영(Projection) 객체를 나타내는 포인터 타입입니다.
  • pj_init_plus(): PROJ4 형식의 정의 문자열을 파싱하여 투영 객체를 생성하고 초기화합니다.
  • pj_free(): 사용이 끝난 투영 객체의 메모리를 해제합니다.
  • pj_transform(): 소스 좌표계에서 대상 좌표계로 포인트 데이터를 변환합니다.

C++ 객체 지향 설계 및 코드 구현

C API를 직접 호출하면 메모리 누수나 리소스 관리 문제가 발생할 수 있습니다. 따라서 RAII(Resource Acquisition Is Initialization) 패턴을 적용하여 투영 객체를 관리하고, 좌표 구조체와 변환 함수를 캡슐화하여 안정성을 높입니다. 또한 pj_transform 함수가 경위도 데이터에 대해 라디안(Radian) 단위를 요구하므로, 이를 자동으로 처리하는 로직을 추가합니다.

// GeoSpatial.h
#pragma once
#include <iostream>
#include <string>
#include <stdexcept>
#include <proj_api.h>

#ifndef DEG_TO_RAD
#define DEG_TO_RAD (M_PI / 180.0)
#endif
#ifndef RAD_TO_DEG
#define RAD_TO_DEG (180.0 / M_PI)
#endif

struct GeoPoint {
    double x;
    double y;
    double z;

    GeoPoint(double px = 0.0, double py = 0.0, double pz = 0.0) 
        : x(px), y(py), z(pz) {}
};

class SpatialReference {
public:
    explicit SpatialReference(const std::string& definition) {
        m_handle = pj_init_plus(definition.c_str());
        if (!m_handle) {
            throw std::runtime_error("좌표계 초기화 실패: " + definition);
        }
    }

    ~SpatialReference() {
        if (m_handle) {
            pj_free(m_handle);
        }
    }

    projPJ getHandle() const {
        return m_handle;
    }

    SpatialReference(const SpatialReference&) = delete;
    SpatialReference& operator=(const SpatialReference&) = delete;

private:
    projPJ m_handle;
};

inline GeoPoint transformPoint(const SpatialReference& src, const SpatialReference& dst, GeoPoint pt) {
    double tx = pt.x * DEG_TO_RAD;
    double ty = pt.y * DEG_TO_RAD;
    double tz = pt.z;

    int err = pj_transform(src.getHandle(), dst.getHandle(), 1, 1, &tx, &ty, &tz);
    if (err != 0) {
        throw std::runtime_error("좌표 변환 중 오류 발생: 코드 " + std::to_string(err));
    }

    return GeoPoint(tx * RAD_TO_DEG, ty * RAD_TO_DEG, tz);
}

테스트 및 실행

WGS84 경위도 좌표를 Web Mercator 투영 좌표로 변환하는 테스트 코드를 작성하여 변환 로직을 검증합니다.

// main.cpp
#include "GeoSpatial.h"

int main() {
    try {
        const std::string wgs84Def = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";
        const std::string mercatorDef = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs";

        SpatialReference srcCrs(wgs84Def);
        SpatialReference dstCrs(mercatorDef);

        // 서울 시청 대략적인 경위도
        GeoPoint seoulCityHall(126.978, 37.566);

        GeoPoint converted = transformPoint(srcCrs, dstCrs, seoulCityHall);

        std::cout << "변환된 Web Mercator 좌표:" << std::endl;
        std::cout << "X (Easting): " << converted.x << " m" << std::endl;
        std::cout << "Y (Northing): " << converted.y << " m" << std::endl;

    } catch (const std::exception& e) {
        std::cerr << "오류: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

자주 사용되는 PROJ4 좌표계 정의 문자열

다양한 좌표계 변환을 위해 아래와 같은 PROJ4 문자열을 활용할 수 있습니다.

  • WGS84 (경위도):
    +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs
  • UTM 52N 구역 (EPSG:32652):
    +proj=utm +zone=52 +ellps=WGS84 +datum=WGS84 +units=m +no_defs
  • Web Mercator (EPSG:3857):
    +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs
  • Bessel 1841 기반 횡축 메르카토르 (TM):
    +proj=tmerc +ellps=bessel +lon_0=127 +x_0=500000 +k=1.0

추가적인 좌표계 정의 문자열은 Spatial Reference 웹사이트나 EPSG 레지스트리에서 "proj4 string for [좌표계 이름]" 형식으로 검색하여 확인할 수 있습니다.

태그: PROJ C++ 좌표변환 PROJ4 SpatialReference

6월 13일 20:36에 게시됨