Sedona 通过 ST_Transform 函数提供坐标参考系(CRS)转换功能。自 v1.9.0 起,Sedona 使用 proj4sedona 库 —— 一个纯 Java 实现,支持多种 CRS 输入格式以及基于网格的转换。
Sedona 支持以下用于指定源 CRS 与目标 CRS 的格式:
最常见的指定 CRS 的方式是使用形如 AUTHORITY:CODE 的权威机构代码。Sedona 使用 spatialreference.org 作为开源的 CRS 数据库,支持多个权威机构:
| Authority | 说明 | 示例 |
|---|---|---|
| EPSG | European Petroleum Survey Group | EPSG:4326, EPSG:3857 |
| ESRI | Esri 坐标系 | ESRI:102008, ESRI:54012 |
| IAU | International Astronomical Union(行星 CRS) | IAU:30100 |
| SR-ORG | 用户贡献的定义 | SR-ORG:6864 |
-- Transform from WGS84 (EPSG:4326) to Web Mercator (EPSG:3857) SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'EPSG:3857' ) AS transformed_point
输出:
POINT (-13627665.271218014 4548257.702387721)
-- Transform using ESRI authority code (North America Albers Equal Area Conic) SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'ESRI:102008' ) AS transformed_point
-- Transform from WGS84 to UTM Zone 10N (EPSG:32610) SELECT ST_Transform( ST_GeomFromText('POLYGON((-122.5 37.5, -122.5 38.0, -122.0 38.0, -122.0 37.5, -122.5 37.5))'), 'EPSG:4326', 'EPSG:32610' ) AS transformed_polygon
你可以在 spatialreference.org 或 EPSG.io 上浏览可用的 CRS 代码。
WKT1 是 OGC 提供的 CRS 定义的 Well-Known Text 格式。投影 CRS 以 PROJCS[...] 开头,地理 CRS 以 GEOGCS[...] 开头。
-- Transform using WKT1 format for target CRS SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'PROJCS["WGS 84 / Pseudo-Mercator", GEOGCS["WGS 84", DATUM["WGS_1984", SPHEROID["WGS 84",6378137,298.257223563]], PRIMEM["Greenwich",0], UNIT["degree",0.0174532925199433]], PROJECTION["Mercator_1SP"], PARAMETER["central_meridian",0], PARAMETER["scale_factor",1], PARAMETER["false_easting",0], PARAMETER["false_northing",0], UNIT["metre",1], AUTHORITY["EPSG","3857"]]' ) AS transformed_point
WKT2 是现代的 ISO 19162:2019 标准格式。投影 CRS 以 PROJCRS[...] 开头,地理 CRS 以 GEOGCRS[...] 开头。
-- Transform using WKT2 format for target CRS SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'PROJCRS["WGS 84 / UTM zone 10N", BASEGEOGCRS["WGS 84", DATUM["World Geodetic System 1984", ELLIPSOID["WGS 84",6378137,298.257223563]]], CONVERSION["UTM zone 10N", METHOD["Transverse Mercator"], PARAMETER["Latitude of natural origin",0], PARAMETER["Longitude of natural origin",-123], PARAMETER["Scale factor at natural origin",0.9996], PARAMETER["False easting",500000], PARAMETER["False northing",0]], CS[Cartesian,2], AXIS["easting",east], AXIS["northing",north], UNIT["metre",1], ID["EPSG",32610]]' ) AS transformed_point
PROJ 字符串提供一种使用投影参数紧凑地定义 CRS 的方式。它以 +proj= 开头。
-- Transform using PROJ string for UTM Zone 10N SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), '+proj=longlat +datum=WGS84 +no_defs', '+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs' ) AS transformed_point
-- Transform using PROJ string for Lambert Conformal Conic SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', '+proj=lcc +lat_1=33 +lat_2=45 +lat_0=39 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs' ) AS transformed_point
PROJJSON 是 CRS 的 JSON 表示形式,便于在基于 JSON 的工作流中使用。
-- Transform using PROJJSON for target CRS SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', '{ "type": "ProjectedCRS", "name": "WGS 84 / UTM zone 10N", "base_crs": { "name": "WGS 84", "datum": { "type": "GeodeticReferenceFrame", "name": "World Geodetic System 1984", "ellipsoid": { "name": "WGS 84", "semi_major_axis": 6378137, "inverse_flattening": 298.257223563 } }, "coordinate_system": { "subtype": "ellipsoidal", "axis": [ {"name": "Longitude", "abbreviation": "lon", "direction": "east", "unit": "degree"}, {"name": "Latitude", "abbreviation": "lat", "direction": "north", "unit": "degree"} ] } }, "conversion": { "name": "UTM zone 10N", "method": {"name": "Transverse Mercator"}, "parameters": [ {"name": "Latitude of natural origin", "value": 0, "unit": "degree"}, {"name": "Longitude of natural origin", "value": -123, "unit": "degree"}, {"name": "Scale factor at natural origin", "value": 0.9996}, {"name": "False easting", "value": 500000, "unit": "metre"}, {"name": "False northing", "value": 0, "unit": "metre"} ] }, "coordinate_system": { "subtype": "Cartesian", "axis": [ {"name": "Easting", "abbreviation": "E", "direction": "east", "unit": "metre"}, {"name": "Northing", "abbreviation": "N", "direction": "north", "unit": "metre"} ] }, "id": {"authority": "EPSG", "code": 32610} }' ) AS transformed_point
自 v1.9.0 起,Sedona 支持从远程 HTTP 服务器解析 CRS 定义。当你需要使用内置数据库中不包含的自定义或内部 CRS 定义,或希望使用你自己的 CRS 定义服务时,这一功能非常有用。
配置后,URL 提供者会先于内置 CRS 数据库被查询。如果 URL 提供者返回了有效的 CRS 定义,则直接使用该定义。如果 URL 返回 404 或错误,Sedona 会回退到内置定义。
你可以将自定义 CRS 定义托管在任何 HTTP 可访问的位置。两种常见做法:
每个文件应包含一个 CRS 定义,其格式通过 spark.sedona.crs.url.format 指定(PROJJSON、PROJ 字符串、WKT1 或 WKT2)。
在创建 Sedona 会话时设置以下 Spark 配置属性:
config = ( SedonaContext.builder() .config("spark.sedona.crs.url.base", "https://crs.example.com") .config("spark.sedona.crs.url.pathTemplate", "/{authority}/{code}.json") .config("spark.sedona.crs.url.format", "projjson") .getOrCreate() ) sedona = SedonaContext.create(config)
在默认的路径模板下,解析 EPSG:4326 时会请求:
https://crs.example.com/epsg/4326.json
只有 spark.sedona.crs.url.base 是必需的。另外两个属性都有合理的默认值(/{authority}/{code}.json 与 projjson)。
| Format 取值 | 说明 | 内容示例 |
|---|---|---|
projjson | PROJJSON(默认) | {"type": "GeographicCRS", ...} |
proj | PROJ 字符串 | +proj=longlat +datum=WGS84 +no_defs |
wkt1 | OGC WKT1 | GEOGCS["WGS 84", ...] |
wkt2 | ISO 19162 WKT2 | GEOGCRS["WGS 84", ...] |
假设你有一个 GitHub 仓库 myorg/crs-definitions,结构如下:
crs-definitions/
epsg/
990001.proj
990002.proj
其中 epsg/990001.proj 包含一条 PROJ 字符串,例如:
+proj=merc +a=6378137 +b=6378137 +lat_ts=0 +lon_0=0 +x_0=0 +y_0=0 +k=1 +units=m +no_defs
将 Sedona 指向 GitHub raw 内容 URL:
config = ( SedonaContext.builder() .config( "spark.sedona.crs.url.base", "https://raw.githubusercontent.com/myorg/crs-definitions/main", ) .config("spark.sedona.crs.url.pathTemplate", "/epsg/{code}.proj") .config("spark.sedona.crs.url.format", "proj") .getOrCreate() ) sedona = SedonaContext.create(config) # Resolves EPSG:990001 from: # https://raw.githubusercontent.com/myorg/crs-definitions/main/epsg/990001.proj sedona.sql(""" SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'EPSG:990001' ) AS transformed_point """).show()
config = ( SedonaContext.builder() .config("spark.sedona.crs.url.base", "https://crs.mycompany.com") .config("spark.sedona.crs.url.pathTemplate", "/epsg/{code}.proj") .config("spark.sedona.crs.url.format", "proj") .getOrCreate() ) sedona = SedonaContext.create(config) # Now ST_Transform will try https://crs.mycompany.com/epsg/3857.proj # before falling back to built-in definitions sedona.sql(""" SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'EPSG:3857' ) AS transformed_point """).show()
URL 提供者对于不存在于任何公开数据库中的自定义或内部权威机构代码尤其有用。使用默认的路径模板 /{authority}/{code}.json 时,{authority} 占位符会被 CRS 字符串中的权威机构名称(小写)替换:
config = ( SedonaContext.builder() .config("spark.sedona.crs.url.base", "https://crs.mycompany.com") .config("spark.sedona.crs.url.format", "proj") .getOrCreate() ) sedona = SedonaContext.create(config) # Resolves MYORG:1001 from: # https://crs.mycompany.com/myorg/1001.json sedona.sql(""" SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), 'EPSG:4326', 'MYORG:1001' ) AS transformed_point """).show()
如果几何对象已设置了 SRID(例如通过 ST_SetSRID),可以省略源 CRS 参数。此时源 CRS 会从几何对象的 SRID 推导为 EPSG 代码:
config = ( SedonaContext.builder() .config("spark.sedona.crs.url.base", "https://crs.mycompany.com") .config("spark.sedona.crs.url.format", "proj") .getOrCreate() ) sedona = SedonaContext.create(config) # The source CRS is taken from the geometry's SRID (4326 → EPSG:4326). # Only the target CRS string is needed. sedona.sql(""" SELECT ST_Transform( ST_SetSRID(ST_GeomFromText('POINT(-122.4194 37.7749)'), 4326), 'EPSG:3857' ) AS transformed_point """).show()
要避免启用 URL 提供者,可以省略 spark.sedona.crs.url.base 或保持其为空字符串(默认值)。注意,一旦 URL 提供者已在某个 executor JVM 中注册,它在该 JVM 的生命周期内会一直保持激活。
参见:Configuration parameters 中 URL CRS 提供者的完整配置列表。
网格文件支持高精度的基准面(datum)转换,例如 NAD27 到 NAD83 或 OSGB36 到 ETRS89。Sedona 支持从多种来源加载网格文件。
可以在 PROJ 字符串中通过 +nadgrids 参数指定网格文件:
| 来源 | 格式 | 示例 |
|---|---|---|
| 本地文件 | 绝对路径 | +nadgrids=/path/to/grid.gsb |
| PROJ CDN | @ 前缀 | +nadgrids=@us_noaa_conus.tif |
| HTTPS URL | 完整 URL | +nadgrids=https://cdn.proj.org/us_noaa_conus.tif |
使用 @ 前缀时,网格文件会自动从 PROJ CDN 获取。
@ 前缀(可选):如果该网格不可用,转换会继续进行。当网格能提升精度但并非必需时使用。-- Transform NAD27 to NAD83 using PROJ CDN grid (optional) SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), '+proj=longlat +datum=NAD27 +no_defs +nadgrids=@us_noaa_conus.tif', 'EPSG:4269' ) AS transformed_point
-- Transform using mandatory grid file (error if not found) SELECT ST_Transform( ST_GeomFromText('POINT(-122.4194 37.7749)'), '+proj=longlat +datum=NAD27 +no_defs +nadgrids=us_noaa_conus.tif', 'EPSG:4269' ) AS transformed_point
-- Transform OSGB36 to ETRS89 using UK grid SELECT ST_Transform( ST_GeomFromText('POINT(-0.1276 51.5074)'), '+proj=longlat +datum=OSGB36 +nadgrids=@uk_os_OSTN15_NTv2_OSGBtoETRS.gsb +no_defs', 'EPSG:4258' ) AS transformed_point
Sedona 期望几何对象使用 经度/纬度(lon/lat) 顺序。如果你的数据是 lat/lon 顺序,请在转换前使用 ST_FlipCoordinates 交换坐标。
-- If your data is in lat/lon order, flip first SELECT ST_Transform( ST_FlipCoordinates(ST_GeomFromText('POINT(37.7749 -122.4194)')), 'EPSG:4326', 'EPSG:3857' ) AS transformed_point
Sedona 会自动处理 CRS 定义中的坐标顺序,确保源 CRS 与目标 CRS 在内部都使用 lon/lat 顺序。
如果几何对象已经设置了 SRID,可以省略源 CRS 参数:
-- Set SRID on geometry and transform using only target CRS SELECT ST_Transform( ST_SetSRID(ST_GeomFromText('POINT(-122.4194 37.7749)'), 4326), 'EPSG:3857' ) AS transformed_point