지리공간
지리공간 (Geometry/Geography) 타입 (Geospatial Types)
Spark SQL이 공간 데이터를 위해 지원하는 GEOMETRY와 GEOGRAPHY 타입을 설명하는 문서예요. 두 타입의 차이점, SQL에서의 선언 구문, WKB로 값을 만드는 방법, 그리고 다루는 내장 함수까지 다뤄요. 어떤 타입을 써야 하는지 판단하는 기준도 알기 쉽게 정리돼 있어요.
출처: 문서
본문
Spark SQL은 Open Geospatial Consortium (OGC) Simple Feature Access 사양에 정의된 대로 공간 데이터를 위한 GEOMETRY와 GEOGRAPHY 타입을 지원해요. 런타임에 값은 **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에서 GEOMETRY와 GEOGRAPHY 컬럼은 항상 명시적 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)을 나타내요. LineString과 Polygon(그리고 그 안의 점)은 좌표 값에 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)를 사용하세요.- Parquet과 Iceberg 지리공간 사양은 컬럼당 고정 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)
- 아파치 스파크 SQL 참조 (원문)
- Data Types — 지원되는 데이터 타입 전체
- Built-in Functions — Geospatial ST 함수를 포함한 내장 함수
- Well-known binary (Wikipedia) — WKB 형식에 대한 설명