지리공간

지리공간 (Geometry/Geography) 타입 (Geospatial Types)

Spark SQL이 공간 데이터를 위해 지원하는 GEOMETRYGEOGRAPHY 타입을 설명하는 문서예요. 두 타입의 차이점, SQL에서의 선언 구문, WKB로 값을 만드는 방법, 그리고 다루는 내장 함수까지 다뤄요. 어떤 타입을 써야 하는지 판단하는 기준도 알기 쉽게 정리돼 있어요.

출처: 문서

본문

Spark SQL은 Open Geospatial Consortium (OGC) Simple Feature Access 사양에 정의된 대로 공간 데이터를 위한 GEOMETRYGEOGRAPHY 타입을 지원해요. 런타임에 값은 **Well-Known Binary (WKB)**로 표현되고, 좌표계를 정의하는 **Spatial Reference Identifier (SRID)**와 연결돼요. 값이 어떻게 저장되는지는 각 데이터소스가 결정해요.

개요 (Overview)

타입 (Type) 좌표계 (Coordinate system) 일반적인 용도 및 참고 (Typical use and notes)
GEOMETRY 직교(Cartesian, 평면) 투영(projected) 또는 로컬 좌표; 평면 계산. 평평한 좌표계에서 점, 선, 다각형을 표현해요. Web Mercator(SRID 3857), UTM, 또는 로컬 그리드(예: 공학/CAD)에 적합해요. SRID 0(CRS 미지정)을 포함해 레지스트리에 있는 모든 SRID를 받아요.
GEOGRAPHY 지리(Geographic, 위도/경도) 지구 기반 데이터; 구/타원체 위의 거리와 면적. 경도와 위도(도 단위)로 좌표를 표현해요. 가장자리 보간(edge interpolation)은 항상 SPHERICAL이에요. 지리 SRID만 받으며, 가장 흔한 것은 4326(WGS 84)이에요.

SQL에서 GEOMETRYGEOGRAPHY 컬럼은 항상 명시적 SRID(또는 ANY)로 선언되어야 해요. 아래의 SQL의 타입 구문을 참고하세요. 명시적 SRID 없이 ST_GeomFromWKB(wkb)로 값을 만들면 값의 SRID는 0(미지정)이 되고, ST_GeogFromWKB(wkb)는 항상 SRID 4326의 값을 반환해요.

GEOMETRY vs GEOGRAPHY 언제 사용할까

다음과 같은 경우 GEOMETRY를 선택하세요:

  • 데이터가 로컬 또는 투영 좌표(예: 미터 단위의 공학/CAD, Web Mercator의 지도 타일)일 때.
  • 작거나 지역적인 영역에서 평면(planar) 연산이 필요할 때: 교차, 합집합, 클리핑, 포함, 오버레이에서 표면을 평평하게 다뤄도 괜찮을 때.
  • 정점 간격이 가깝거나 범위가 작아서 지구 곡률이 무시 가능할 때.

다음과 같은 경우 GEOGRAPHY를 선택하세요:

  • 데이터가 **전역(global)**이거나 큰 범위에 걸쳐 있을 때(예: 국가 경계, 전 세계 관심 지점).
  • 거리 또는 면적이 지구 곡률을 고려해야 할 때(예: 두 도시 사이의 최단 경로, 구 위의 다각형 면적).
  • 항공, 해상, 또는 전 지구적 이동 같이 대원(great-circle) 또는 측지(geodesic) 동작이 중요한 경우.

잘못된 타입을 사용하면 오해를 부르는 결과가 나올 수 있어요. 예를 들어 런던과 뉴욕 사이의 구 위 최단 경로는 캐나다를 가로지르지만, 평면 GEOMETRY는 그렇지 않은 경로를 제안할 수 있어요.

SQL의 타입 구문 (Type Syntax in SQL)

SQL에서 타입을 SRID 또는 ANY로 지정해야 해요:

  • 고정 SRID (컬럼의 모든 값이 하나의 SRID를 공유):
    • GEOMETRY(srid) — 예: GEOMETRY(4326), GEOMETRY(3857)
    • GEOGRAPHY(srid) — 예: GEOGRAPHY(4326)
  • 혼합 SRID (컬럼의 값들이 서로 다른 SRID를 가질 수 있음):
    • GEOMETRY(ANY)
    • GEOGRAPHY(ANY)

매개변수가 없는 GEOMETRY 또는 GEOGRAPHY((srid)(ANY) 없이)는 SQL에서 지원되지 않아요.

Geometry/Geography 컬럼으로 테이블 만들기 (Creating Tables with Geometry or Geography Columns)

-- Fixed SRID: all values must use the given SRID (e.g. WGS 84)
CREATE TABLE points (
  id BIGINT,
  pt GEOMETRY(4326)
);

CREATE TABLE locations (
  id BIGINT,
  loc GEOGRAPHY(4326)
);

-- Mixed SRID: each row can have a different SRID
CREATE TABLE mixed_geoms (
  id BIGINT,
  geom GEOMETRY(ANY)
);

Geometry/Geography 값 만들기 (Constructing Geometry and Geography Values)

값은 내장 함수를 사용해 **Well-Known Binary (WKB)**로부터 생성돼요. WKB는 공간 도형(점, 선, 다각형 등)을 위한 표준 바이너리 인코딩이에요. 형식에 대한 자세한 내용은 Well-known binary를 참고하세요.

WKB(바이너리)에서:

  • ST_GeomFromWKB(wkb) — 기본 SRID 0의 GEOMETRY를 반환해요.
  • ST_GeomFromWKB(wkb, srid) — 주어진 SRID의 GEOMETRY를 반환해요.
  • ST_GeogFromWKB(wkb) — SRID 4326의 GEOGRAPHY를 반환해요.

예제 (WKB의 점, 그리고 테이블에서 사용):

-- Point (1, 2) in WKB (little-endian point, 2D)
SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040');
SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 4326);
SELECT ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040');

INSERT INTO points (id, pt)
VALUES (1, ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 4326));

WKB 좌표 처리 (WKB coordinate handling)

WKB를 파싱할 때 Spark는 다음 규칙을 적용해요. 위반하면 파싱 오류가 발생해요.

  • 빈 점 (Empty points): Point 도형(MultiPoint 안의 점 포함)에 대해 NaN(숫자 아님) 좌표 값이 허용되며 빈 점(예: Well-Known Text의 POINT EMPTY)을 나타내요. LineStringPolygon(그리고 그 안의 점)은 좌표 값에 NaN을 허용하지 않아요.
  • 비점 좌표 (Non-point coordinates): LineString, Polygon 링, 그리고 그 구조의 일부인 점의 좌표 값은 **유한(finite)**해야 해요(NaN이 없고, 양/음의 무한대가 없어야 함).
  • 무한대 (Infinity): 양 또는 음의 무한대는 어떤 좌표 값에서도 허용되지 않아요.
  • Polygon 링 (Polygon rings): 각 링은 닫혀 있어야 하며(첫 점과 마지막 점이 같음) 최소 4개의 점이 있어야 해요. LineString은 최소 2개의 점이 있어야 해요.
  • GEOGRAPHY 경계: WKB가 GEOGRAPHY로 파싱될 때(예: ST_GeogFromWKB 사용), 경도는 [-180, 180](포함) 안에, 위도는 [-90, 90](포함) 안에 있어야 해요. GEOMETRY는 이런 경계를 강제하지 않아요.
  • 잘못된 WKB: null 또는 빈 입력, 잘린 바이트, 잘못된 도형 클래스 또는 바이트 순서, 기타 잘못 만들어진 WKB.

내장 지리공간 (ST) 함수 (Built-in Geospatial (ST) Functions)

Spark SQL은 GEOMETRY와 GEOGRAPHY 값을 다루기 위한 스칼라 함수를 제공해요. 전체 목록과 자세한 인자 설명, 예제는 Built-in Functions 페이지의 Geospatial ST Functions에서 볼 수 있어요. 현재 릴리스에서 제공되는 함수를 요약하면 다음과 같아요:

함수 (Function) 설명 (Description)
ST_AsBinary(geo[, endianness]) GEOMETRY 또는 GEOGRAPHY 값을 WKB(BINARY)로 반환해요. 선택적 endianness 인자는 리틀엔디언(기본)의 'NDR' 또는 빅엔디언의 'XDR'이에요.
ST_GeomFromWKB(wkb[, srid]) WKB를 파싱해 GEOMETRY를 반환해요. 선택적 srid 인자가 SRID를 설정해요. 생략하면 SRID는 0이에요.
ST_GeogFromWKB(wkb) WKB를 파싱해 SRID 4326의 GEOGRAPHY를 반환해요.
ST_Srid(geo) GEOMETRY 또는 GEOGRAPHY 값의 SRID를 반환해요(입력이 NULL이면 NULL).
ST_SetSrid(geo, srid) 주어진 SRID로 새 GEOMETRY 또는 GEOGRAPHY를 반환해요. 새 SRID는 값의 타입에 유효해야 해요.

예제:

SELECT hex(ST_AsBinary(ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040')));
-- 0101000000000000000000F03F0000000000000040

SELECT hex(ST_AsBinary(ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040'), 'XDR'));
-- 00000000013FF00000000000004000000000000000

SELECT ST_Srid(ST_GeogFromWKB(X'0101000000000000000000F03F0000000000000040'));
-- 4326

SELECT ST_Srid(ST_SetSrid(ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040'), 3857));
-- 3857

SRID와 저장된 값 (SRID and Stored Values)

  • 고정-SRID 컬럼: 컬럼의 모든 값은 컬럼 타입과 같은 SRID를 가져야 해요. 다른 SRID로 값을 삽입하면 GEO_ENCODER_SRID_MISMATCH_ERROR가 발생해요. ST_SetSrid를 사용해 값의 SRID를 컬럼과 일치하도록 바꾸세요.
  • 혼합-SRID 컬럼(GEOMETRY(ANY) 또는 GEOGRAPHY(ANY)): 행마다 서로 다른 SRID를 가질 수 있어요. 각 값은 여전히 타입에 유효한 SRID여야 해요. 유효하지 않은 SRID는 ST_INVALID_SRID_VALUE를 발생시켜요.
  • 저장 (Storage): Parquet, Delta, Iceberg는 컬럼당 고정 SRID로 geometry/geography를 저장해요. GEOMETRY(ANY)GEOGRAPHY(ANY)의 저장은 지원하지 않아요. 혼합-SRID 타입은 메모리 내/쿼리용으로만 존재해요.

지원되는 SRID (Supported SRIDs)

Spark는 PROJ 데이터베이스의 좌표계와 OGC 표준 오버라이드(override)를 결합한 사전 구축된 SRID 레지스트리를 포함해요. 이 레지스트리는 지리공간 데이터의 좌표계를 검증하고 제대로 처리할 수 있게 해줘요.

SRID 호환성 규칙:

  • GEOMETRY는 레지스트리에 있는 모든 SRID를 받아요(지리 + 투영 + SRID 0).
  • GEOGRAPHY는 지리 SRID(위도/경도 좌표계)만 받아요.

Spark 릴리스별 PROJ 버전 (PROJ Version by Spark Release)

Spark 버전 (Spark Version) PROJ 버전 (PROJ Version)
4.2.0 9.8.1

SRID 레지스트리는 위에 보이는 PROJ 버전에 고정되어 있고, 외부 데이터베이스와 실시간 동기화되지 않아요.

OGC 표준 오버라이드 (OGC Standard Overrides)

Spark는 PROJ 데이터베이스의 특정 SRID에 다음 OGC 표준 오버라이드를 적용해요:

SRID PROJ CRS 식별자 OGC CRS 식별자 설명 (Description)
4326 EPSG:4326 OGC:CRS84 WGS 84 (OGC 표준에 따른 경도/위도 순서)
4267 EPSG:4267 OGC:CRS27 NAD27
4269 EPSG:4269 OGC:CRS83 NAD83

자주 쓰이는 SRID (Commonly Used SRIDs)

SRID CRS 식별자 이름 (Name) CRS 타입 (CRS Type) 설명 (Description)
0 SRID:0 Unspecified Cartesian 정의된 CRS가 없는 좌표 (ST_GeomFromWKB(wkb)의 기본값)
4326 OGC:CRS84 WGS 84 Geographic World Geodetic System 1984 (경도/위도), GPS 좌표, 전역 데이터 (GEOGRAPHY의 기본값)
4267 OGC:CRS27 NAD27 Geographic North American Datum 1927
4269 OGC:CRS83 NAD83 Geographic North American Datum 1983
3857 EPSG:3857 Web Mercator Projected 웹 매핑 서비스에서 사용하는 Pseudo-Mercator 투영

참고:

  • GEOMETRY(0)은 고정 SRID 0을 의미해요. 행별로 혼합된 SRID는 GEOMETRY(ANY)를 사용하세요.
  • ParquetIceberg 지리공간 사양은 컬럼당 고정 SRID를 요구하므로, GEOMETRY(ANY)GEOGRAPHY(ANY)의 저장은 지원하지 않아요.

SRID 검증 (SRID Validation)

유효하지 않은 SRID (레지스트리에 없음):

SELECT ST_GeomFromWKB(X'0101000000000000000000F03F0000000000000040', 99999);
-- Throws [ST_INVALID_SRID_VALUE]

GEOGRAPHY 타입과 투영 SRID:

CREATE TABLE invalid_geo (id BIGINT, loc GEOGRAPHY(3857));
-- Throws [ST_INVALID_SRID_VALUE] (3857 is projected, not geographic)

데이터 타입 참조 (Data Types Reference)

지원되는 데이터 타입의 전체 목록과 Scala, Java, Python, SQL에서의 API 사용법은 Data Types를 참고하세요.

더 알아보기 (Learn more)