Files
geo_tools/docs/projection_guide.md
2026-03-04 17:07:07 +08:00

249 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# geo_tools.core.projection 使用说明
> 坐标系查询、坐标转换、投影推荐工具,基于 [pyproj](https://pyproj4.github.io/pyproj/)。
---
## 导入方式
```python
# 推荐:从顶层包导入
from geo_tools.core.projection import (
get_crs_info,
crs_to_epsg,
transform_coordinates,
transform_point,
suggest_projected_crs,
WGS84, CGCS2000, WEB_MERCATOR, CGCS2000_UTM_50N,
)
# 或直接通过 geo_tools 导入
import geo_tools
```
---
## CRS 常量
模块内置了中国地理信息处理中最常用的 CRS 快捷常量,可直接作为参数传入所有函数:
| 常量名 | EPSG | 说明 |
|--------|------|------|
| `WGS84` | `EPSG:4326` | WGS84 地理坐标系(经纬度,最通用) |
| `CGCS2000` | `EPSG:4490` | 中国国家大地坐标系 2000经纬度 |
| `WEB_MERCATOR` | `EPSG:3857` | Web Mercator 投影(网络地图常用,单位:米) |
| `CGCS2000_UTM_50N` | `EPSG:4508` | CGCS2000 / 3° 高斯-克吕格 50 带(单位:米) |
```python
from geo_tools.core.projection import WGS84, CGCS2000
# 直接用常量替代字符串
gdf = gdf.to_crs(CGCS2000)
```
---
## 函数说明
### `get_crs_info(crs_input)` — 查询 CRS 信息
返回坐标系的详细描述字典,方便快速了解一个未知 EPSG 的含义。
**参数**
- `crs_input`EPSG 代码字符串(如 `"EPSG:4523"`)、整数(如 `4523`)或 proj 字符串。
**返回值**`dict`
| 键 | 含义 |
|----|------|
| `name` | 坐标系名称 |
| `epsg` | EPSG 整数编号(无法识别时为 `None` |
| `unit` | 坐标单位(`degree` / `metre` |
| `is_geographic` | 是否为地理坐标系(经纬度) |
| `is_projected` | 是否为投影坐标系(平面直角) |
| `datum` | 基准面名称 |
```python
from geo_tools.core.projection import get_crs_info
# 查询读取到的 GDB 数据的 CRS 含义
info = get_crs_info("EPSG:4523")
print(info)
# {
# 'name': 'CGCS2000 / 3-degree Gauss-Kruger zone 45',
# 'epsg': 4523,
# 'unit': 'metre',
# 'is_geographic': False,
# 'is_projected': True,
# 'datum': 'China Geodetic Coordinate System 2000'
# }
# 直接传整数
info = get_crs_info(32650)
print(info["name"]) # WGS 84 / UTM zone 50N
```
---
### `crs_to_epsg(crs_input)` — 获取 EPSG 编号
将任意 CRS 描述转为整数 EPSG 编号,无法识别时返回 `None`(不抛异常)。
```python
from geo_tools.core.projection import crs_to_epsg
epsg = crs_to_epsg("EPSG:4490")
print(epsg) # 4490
epsg = crs_to_epsg("WGS 84")
print(epsg) # 4326
epsg = crs_to_epsg("invalid_crs")
print(epsg) # None
```
---
### `transform_coordinates(xs, ys, source_crs, target_crs)` — 批量坐标转换
将一组坐标点从源坐标系批量转换到目标坐标系,返回转换后的 `(xs, ys)` 列表。
**参数**
- `xs`X 坐标序列(地理 CRS 时为**经度**
- `ys`Y 坐标序列(地理 CRS 时为**纬度**
- `source_crs`:源坐标系
- `target_crs`:目标坐标系
- `always_xy`(关键字参数):强制按 (经度/X, 纬度/Y) 顺序处理,默认 `True`**建议不修改**
```python
from geo_tools.core.projection import transform_coordinates, WGS84, WEB_MERCATOR
# 将北京、上海、广州的 WGS84 经纬度转为 Web Mercator 米制坐标
lons = [116.4074, 121.4737, 113.2644]
lats = [39.9042, 31.2304, 23.1291]
xs, ys = transform_coordinates(lons, lats, WGS84, WEB_MERCATOR)
print(xs) # [12959618.8, 13521606.3, 12608870.0](单位:米)
print(ys) # [4859767.2, 3649094.2, 2641877.0]
# 国家坐标系转换CGCS2000 经纬度 → CGCS2000 3° 高斯带50带
from geo_tools.core.projection import CGCS2000, CGCS2000_UTM_50N
xs_proj, ys_proj = transform_coordinates(lons, lats, CGCS2000, CGCS2000_UTM_50N)
```
---
### `transform_point(x, y, source_crs, target_crs)` — 单点坐标转换
`transform_coordinates` 的单点版本,直接返回 `(x, y)` 元组。
```python
from geo_tools.core.projection import transform_point, WGS84, CGCS2000
# 单点WGS84 → CGCS2000两者数值非常接近差异在毫米级
x, y = transform_point(116.4074, 39.9042, WGS84, CGCS2000)
print(f"CGCS2000 坐标:经度={x:.6f}, 纬度={y:.6f}")
# 单点:经纬度 → 投影坐标(米)
from geo_tools.core.projection import WEB_MERCATOR
mx, my = transform_point(116.4074, 39.9042, WGS84, WEB_MERCATOR)
print(f"墨卡托坐标X={mx:.2f}m, Y={my:.2f}m")
```
---
### `suggest_projected_crs(lon, lat)` — 自动推荐投影 CRS
根据数据中心坐标WGS84 经纬度)自动推荐适合**面积/距离计算**的 UTM 投影带,避免在地理坐标系下计算面积出错。
**参数**
- `lon`中心经度WGS84
- `lat`中心纬度WGS84北半球为正
**返回值**EPSG 代码字符串,如 `"EPSG:32650"`
```python
from geo_tools.core.projection import suggest_projected_crs
# 云南马关县(约 104.4°E, 23.0°N
proj_crs = suggest_projected_crs(lon=104.4, lat=23.0)
print(proj_crs) # EPSG:32648 (WGS84 UTM zone 48N)
# 北京116.4°E, 39.9°N
proj_crs = suggest_projected_crs(lon=116.4, lat=39.9)
print(proj_crs) # EPSG:32650 (WGS84 UTM zone 50N)
# 实际场景:读取 GDB 后用推荐的投影计算面积
import geo_tools
gdf = geo_tools.read_gdb("data.gdb", layer="图斑")
cx, cy = gdf.geometry.unary_union.centroid.x, gdf.geometry.unary_union.centroid.y
# 如果数据是投影坐标系(单位:米),先转到地理坐标系再推荐
if gdf.crs.is_projected:
cx, cy = geo_tools.transform_point(cx, cy, gdf.crs, "EPSG:4326")
proj_crs = suggest_projected_crs(cx, cy)
gdf_proj = geo_tools.reproject(gdf, proj_crs) # 重投影
gdf_proj = geo_tools.add_area_column(gdf_proj) # 计算面积单位
```
---
## 常见场景示例
### 场景一:不认识数据的 CRS先查一下
```python
import geo_tools
gdf = geo_tools.read_gdb("临时数据库.gdb", layer="马关综合后图斑")
# 读取完成CRS=EPSG:4523
info = geo_tools.get_crs_info(gdf.crs)
print(info["name"]) # CGCS2000 / 3-degree Gauss-Kruger zone 45
print(info["unit"]) # metre投影坐标系单位是米
print(info["is_projected"]) # True
```
### 场景二:统一坐标系后叠置分析
```python
import geo_tools
from geo_tools.core.projection import CGCS2000
layer_a = geo_tools.read_gdb("a.gdb", layer="林地") # EPSG:4523
layer_b = geo_tools.read_vector("b.geojson") # EPSG:4326
# 统一到 CGCS2000 地理坐标系后再做叠置
layer_a = geo_tools.reproject(layer_a, CGCS2000)
layer_b = geo_tools.reproject(layer_b, CGCS2000)
result = geo_tools.overlay(layer_a, layer_b, how="intersection")
```
### 场景三:在地理坐标系数据上正确计算面积
```python
import geo_tools
from geo_tools.core.projection import suggest_projected_crs
gdf = geo_tools.read_vector("data.geojson") # EPSG:4326单位是度
# 自动推荐合适的投影
proj = suggest_projected_crs(lon=105.0, lat=25.0) # 云贵地区
gdf = geo_tools.add_area_column(gdf, projected_crs=proj)
print(gdf["area_m2"].describe())
```
---
## 注意事项
> [!WARNING]
> 在**地理坐标系**EPSG:4326 / 4490下直接调用 `geometry.area` 得到的是"平方度"**不是平方米**,面积计算会严重失真。始终用 `add_area_column()` 或先 `reproject()` 到投影坐标系后再计算。
> [!NOTE]
> `WGS84`EPSG:4326与 `CGCS2000`EPSG:4490的坐标数值差异极小通常 < 1 米),在普通精度的分析中可视为等价,但正式国家项目中必须使用 CGCS2000。