惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Privacy International News Feed
Hacker News - Newest:
Hacker News - Newest: "LLM"
Application and Cybersecurity Blog
Application and Cybersecurity Blog
N
News and Events Feed by Topic
Hacker News: Ask HN
Hacker News: Ask HN
V2EX - 技术
V2EX - 技术
L
LINUX DO - 最新话题
Google Online Security Blog
Google Online Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Stack Overflow Blog
Stack Overflow Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Proofpoint News Feed
A
Arctic Wolf
Forbes - Security
Forbes - Security
Spread Privacy
Spread Privacy
Security Latest
Security Latest
C
CXSECURITY Database RSS Feed - CXSecurity.com
C
CERT Recently Published Vulnerability Notes
Latest news
Latest news
AWS News Blog
AWS News Blog
M
MIT News - Artificial intelligence
GbyAI
GbyAI
V
Visual Studio Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
Help Net Security
Help Net Security
博客园 - Franky
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
S
Schneier on Security
S
SegmentFault 最新的问题
Microsoft Security Blog
Microsoft Security Blog
Y
Y Combinator Blog
PCI Perspectives
PCI Perspectives
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
P
Palo Alto Networks Blog
SecWiki News
SecWiki News
TaoSecurity Blog
TaoSecurity Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
K
Kaspersky official blog
G
Google Developers Blog
H
Hacker News: Front Page
T
Tailwind CSS Blog
aimingoo的专栏
aimingoo的专栏
博客园 - 叶小钗
T
Troy Hunt's Blog
F
Full Disclosure
T
Threat Research - Cisco Blogs

博客园 - 我才是银古

第16章:常见问题、排错与最佳实践 第15章:扩展生态、MCAD 与外部集成 第12章:实战案例:机械结构与 3D 打印零件 第14章:构建、测试、调试与贡献流程 第13章:OpenSCAD 源码架构与核心执行流程 第11章:预览、渲染、网格精度与性能优化 第09章:列表推导、递归与算法建模 第08章:参数化零件库与复用设计 第10章:导入导出、命令行与自动化 第06章:CSG 布尔建模方法 第07章:二维图形、拉伸、旋转与投影 第05章:基础几何、坐标系与变换 第04章:参数、变量、函数、模块与作用域 OpenSCAD 教程目录 第03章:OpenSCAD 语言基础 第02章:安装、环境配置与开发工作流 第01章:OpenSCAD 项目全景与学习路线 第02章:源码获取、编译与开发环境配置 第01章:OCCT项目全景与学习路线 第18章:二次开发实战与综合案例 第18章:综合实战案例 第17章:数据交换与协同 第16章:源码架构与二次开发 第15章:插件与自定义工作台开发 第14章:Python脚本宏与自动化 第13章:FEM仿真分析 第12章:CAM数控加工 第11章:SurfaceMesh与逆向工程 第10章:Draft二维绘图与BIM建筑 第09章:工程图TechDraw 第07章:参数化表达式与Spreadsheet 第08章:装配设计Assembly 第06章:Part工作台与几何内核 第05章:PartDesign实体特征建模 第04章:草图Sketcher约束建模 第02章:安装版本与工作环境配置 第03章:界面工作台与基础操作 第01章:项目全景与学习路线 第十二章:插件开发、研究功能与最佳实践 第十章:定时任务与自动化(Cron) 第七章:技能、记忆与自学习闭环 第八章:MCP 集成与上下文文件 第六章:工具系统与终端后端 第五章:模型供应商与配置体系 Hermes Agent 教程目录 第十一章:语音、视觉、浏览器与子代理协作 第四章:CLI/TUI 与会话管理 第十二章:学习路线、实战方案与最佳实践 第十一章:源码结构、开发调试与插件开发 第十章:自动化、远程访问、日志与排障 第九章:Control UI、节点、Canvas 与语音能力 第七章:工具、技能、插件与能力扩展 第八章:安全模型、访问控制与沙箱实践 第六章:Agent 工作区、会话与多智能体路由 第五章:多通道消息接入与聊天平台配置 第四章:配置体系、模型接入与认证管理 第三章:Gateway 架构、协议与运行机制 第二章:安装、环境准备与快速上手 第一章:OpenClaw 项目概览与核心定位 oh-my-openagent 教程目录 09-命令模型回退与配置参考 10-实战案例最佳实践与故障排除 05-工作模式-Ultrawork-Prometheus-Atlas 08-Hooks与MCP系统 06-Category与Skill系统 07-核心工具链 04-智能体全景详解 03-安装与环境配置 02-整体架构与多模型编排机制 01-项目简介与核心理念 01-项目概览与学习路线 02-安装部署与工具适配 03-Skill机制与using-superpowers 05-TDD系统化调试与完成前验证 04-需求澄清方案设计与计划编写 07-并行智能体子智能体与Git-Worktree 第六章:代码审查、反馈处理与分支收尾 08-中国特色Skills与本土团队落地 09-MCP构建工作流执行与自定义Skill 第23章:FreeCAD-Python-API Clipper2 C# 源码解读教程 第19章:PolyTree 多边形树结构 第20章:实际应用与最佳实践 第18章:Minkowski 和与差 第17章:RectClip 矩形裁剪优化 第16章:ClipperOffset 偏移类详解 第15章:填充规则详解 第14章:布尔运算执行流程 第13章:ClipperD 浮点裁剪类 第11章:OutRec 与 OutPt 输出结构 第9章:Active 活动边结构 第10章:Vertex 顶点与 LocalMinima 局部极小值 第12章:Clipper64 裁剪类详解 第7章:高精度运算与128位整数 第8章:ClipperBase 基类详解 第5章:枚举类型与常量定义 第6章:InternalClipper 内部工具类 第2章:核心数据结构 - Point64、PointD 第3章:路径与多边形表示 - Path64、PathD、Paths64、PathsD 第4章:矩形边界 - Rect64、RectD
第20章:仿射变换与坐标操作
我才是银古 · 2026-06-23 · via 博客园 - 我才是银古

第20章:仿射变换与坐标操作

仿射变换是几何学中的基本操作,能够对空间要素进行平移、旋转、缩放和倾斜等变换,同时保持直线和平行性不变。本章将全面介绍 GeoPandas 中仿射变换的实现方法,以及底层坐标的提取、修改和精度控制等操作。


20.1 仿射变换概述

20.1.1 什么是仿射变换

仿射变换(Affine Transformation)是线性变换加平移的组合,包含以下基本类型:

  • 平移(Translation):将几何体沿 X/Y 方向移动指定距离
  • 旋转(Rotation):将几何体绕某一点旋转指定角度
  • 缩放(Scaling):将几何体按比例放大或缩小
  • 倾斜(Skew):将几何体沿某一轴进行剪切变换
性质 说明
保持直线性 直线变换后仍为直线
保持平行性 平行线变换后仍然平行
保持比例关系 同一直线上点的比值不变
不保持角度 角度可能变化(纯旋转除外)

20.1.2 仿射变换矩阵

二维仿射变换用 6 个参数 [a, b, d, e, xoff, yoff] 表示,变换公式为:

x' = a * x + b * y + xoff
y' = d * x + e * y + yoff
参数 含义 恒等变换值
a X 方向缩放因子 1
b Y 对 X 的倾斜因子 0
d X 对 Y 的倾斜因子 0
e Y 方向缩放因子 1
xoff X 方向平移量 0
yoff Y 方向平移量 0

注意: 恒等变换(不做任何改变)对应的矩阵参数为 [1, 0, 0, 1, 0, 0]


20.2 GeoSeries.affine_transform() 方法

20.2.1 方法签名与参数

GeoSeries.affine_transform(matrix)
参数 类型 说明
matrix list/tuple 6 元素仿射矩阵 [a, b, d, e, xoff, yoff]

返回值为变换后的 GeoSeries

20.2.2 基础用法示例

import geopandas as gpd
from shapely.geometry import Polygon

gdf = gpd.GeoDataFrame(
    {"name": ["矩形A", "矩形B"]},
    geometry=[
        Polygon([(0, 0), (2, 0), (2, 1), (0, 1)]),
        Polygon([(3, 0), (5, 0), (5, 1), (3, 1)])
    ]
)

# 应用恒等变换
result = gdf.geometry.affine_transform([1, 0, 0, 1, 0, 0])
print(result)

输出:

0    POLYGON ((0 0, 2 0, 2 1, 0 1, 0 0))
1    POLYGON ((3 0, 5 0, 5 1, 3 1, 3 0))
dtype: geometry

20.3 平移变换(translate)

20.3.1 使用 affine_transform 实现平移

平移矩阵为 [1, 0, 0, 1, xoff, yoff]

import geopandas as gpd
from shapely.geometry import Polygon

gdf = gpd.GeoDataFrame(
    {"name": ["建筑物"]},
    geometry=[Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])]
)

# 向右平移 10,向上平移 5
gdf["translated"] = gdf.geometry.affine_transform([1, 0, 0, 1, 10, 5])
print(gdf["translated"].iloc[0])

输出:

POLYGON ((10 5, 14 5, 14 8, 10 8, 10 5))

20.3.2 使用 Shapely translate 函数

from shapely.affinity import translate

# 逐要素平移
gdf["translated"] = gdf.geometry.apply(
    lambda geom: translate(geom, xoff=10, yoff=5)
)

注意: affine_transform() 是向量化操作,性能优于逐要素 apply。数据量较大时应优先使用 affine_transform()


20.4 旋转变换(rotate)

20.4.1 旋转矩阵推导

绕原点逆时针旋转角度 θ 对应参数 [cos(θ), -sin(θ), sin(θ), cos(θ), 0, 0]

20.4.2 绕原点旋转

import math
import geopandas as gpd
from shapely.geometry import Polygon

square = Polygon([(1, 0), (2, 0), (2, 1), (1, 1)])
gdf = gpd.GeoDataFrame({"name": ["正方形"]}, geometry=[square])

# 绕原点逆时针旋转 90 度
theta = math.radians(90)
cos_t, sin_t = math.cos(theta), math.sin(theta)
gdf["rotated"] = gdf.geometry.affine_transform(
    [cos_t, -sin_t, sin_t, cos_t, 0, 0]
)

print("原始:", gdf.geometry.iloc[0])
print("旋转后:", gdf["rotated"].iloc[0])

输出:

原始: POLYGON ((1 0, 2 0, 2 1, 1 1, 1 0))
旋转后: POLYGON ((0 1, 0 2, -1 2, -1 1, 0 1))

20.4.3 绕任意点旋转

Shapely 的 rotate 函数可直接指定旋转中心:

from shapely.affinity import rotate
from shapely.geometry import Polygon
import geopandas as gpd

rect = Polygon([(0, 0), (4, 0), (4, 2), (0, 2)])
gdf = gpd.GeoDataFrame({"name": ["矩形"]}, geometry=[rect])

# 绕质心旋转 45 度
gdf["rotated_centroid"] = gdf.geometry.apply(
    lambda geom: rotate(geom, angle=45, origin='centroid')
)

# 绕指定点旋转
gdf["rotated_point"] = gdf.geometry.apply(
    lambda geom: rotate(geom, angle=45, origin=(2, 1))
)

print("绕质心旋转 45°:")
print(gdf["rotated_centroid"].iloc[0])

输出:

绕质心旋转 45°:
POLYGON ((0.5857864376269051 -0.4142135623730949, 3.414213562373095 -0.4142135623730951, 3.4142135623730954 2.414213562373095, 0.5857864376269053 2.4142135623730947, 0.5857864376269051 -0.4142135623730949))

注意: rotate()origin 参数支持 'center'(边界框中心)、'centroid'(质心)或 (x, y) 坐标元组。角度为逆时针方向,单位为度。


20.5 缩放变换(scale)

20.5.1 均匀缩放与非均匀缩放

缩放矩阵为 [sx, 0, 0, sy, 0, 0]

import geopandas as gpd
from shapely.geometry import Polygon

rect = Polygon([(0, 0), (2, 0), (2, 1), (0, 1)])
gdf = gpd.GeoDataFrame({"name": ["矩形"]}, geometry=[rect])

# 均匀缩放 2 倍
gdf["uniform"] = gdf.geometry.affine_transform([2, 0, 0, 2, 0, 0])

# 非均匀缩放:X 放大 3 倍,Y 放大 1.5 倍
gdf["nonuniform"] = gdf.geometry.affine_transform([3, 0, 0, 1.5, 0, 0])

print("原始面积:", gdf.geometry.iloc[0].area)
print("均匀 2x 面积:", gdf["uniform"].iloc[0].area)
print("非均匀面积:", gdf["nonuniform"].iloc[0].area)

输出:

原始面积: 2.0
均匀 2x 面积: 8.0
非均匀面积: 9.0

20.5.2 使用 Shapely scale 函数

from shapely.affinity import scale

# 以质心为中心放大 2 倍
gdf["scaled"] = gdf.geometry.apply(
    lambda geom: scale(geom, xfact=2, yfact=2, origin='centroid')
)
print(gdf["scaled"].iloc[0])

输出:

POLYGON ((-1 -0.5, 3 -0.5, 3 1.5, -1 1.5, -1 -0.5))

注意: affine_transform 的缩放中心始终是原点。若要绕其他点缩放,可使用 Shapely 的 scale 函数。


20.6 组合变换

20.6.1 变换顺序的重要性

仿射变换不可交换,顺序不同结果通常也不同:

import math
import geopandas as gpd
from shapely.geometry import Point

gdf = gpd.GeoDataFrame({"name": ["点A"]}, geometry=[Point(1, 0)])
theta = math.radians(90)
cos_t, sin_t = math.cos(theta), math.sin(theta)

# 先旋转 90° 再平移 (5, 0)
r = gdf.geometry.affine_transform([cos_t, -sin_t, sin_t, cos_t, 0, 0])
result1 = r.affine_transform([1, 0, 0, 1, 5, 0])

# 先平移 (5, 0) 再旋转 90°
t = gdf.geometry.affine_transform([1, 0, 0, 1, 5, 0])
result2 = t.affine_transform([cos_t, -sin_t, sin_t, cos_t, 0, 0])

print("先旋转再平移:", result1.iloc[0])
print("先平移再旋转:", result2.iloc[0])

输出:

先旋转再平移: POINT (5 1)
先平移再旋转: POINT (0 6)

20.6.2 矩阵乘法实现组合变换

使用 NumPy 矩阵乘法将多个变换合并为一个矩阵:

import numpy as np
import geopandas as gpd
from shapely.geometry import Polygon
import math

def make_matrix(a, b, d, e, xoff, yoff):
    """6 参数转 3x3 仿射矩阵"""
    return np.array([[a, b, xoff], [d, e, yoff], [0, 0, 1]])

def to_params(m):
    """3x3 矩阵转 6 参数列表"""
    return [m[0,0], m[0,1], m[1,0], m[1,1], m[0,2], m[1,2]]

rect = Polygon([(0, 0), (2, 0), (2, 1), (0, 1)])
gdf = gpd.GeoDataFrame({"name": ["矩形"]}, geometry=[rect])

# 缩放 2 倍 -> 旋转 45° -> 平移 (10, 5)
scale_m = make_matrix(2, 0, 0, 2, 0, 0)
theta = math.radians(45)
rotate_m = make_matrix(
    math.cos(theta), -math.sin(theta),
    math.sin(theta), math.cos(theta), 0, 0
)
translate_m = make_matrix(1, 0, 0, 1, 10, 5)

# 从右到左应用:先缩放,再旋转,最后平移
combined = translate_m @ rotate_m @ scale_m
params = to_params(combined)
print("组合参数:", [round(p, 4) for p in params])

gdf["combined"] = gdf.geometry.affine_transform(params)
print("结果:", gdf["combined"].iloc[0])

输出:

组合参数: [1.4142, -1.4142, 1.4142, 1.4142, 10.0, 5.0]
结果: POLYGON ((10 5, 12.82842712474619 6.414213562373095, 11.414213562373096 7.828427124746191, 8.585786437626904 6.414213562373096, 10 5))

注意: 矩阵乘法顺序从右到左:translate @ rotate @ scale 表示先缩放、再旋转、最后平移。


20.7 坐标操作(Coordinate Manipulation)

20.7.1 提取坐标

import geopandas as gpd
from shapely.geometry import Point, LineString, Polygon

gdf = gpd.GeoDataFrame(
    {"name": ["点", "线", "面"]},
    geometry=[
        Point(1, 2),
        LineString([(0, 0), (1, 1), (2, 0)]),
        Polygon([(0, 0), (3, 0), (3, 2), (0, 2)])
    ]
)

# 提取所有坐标为 DataFrame
coords = gdf.get_coordinates()
print(coords)

输出:

     x    y
0  1.0  2.0
1  0.0  0.0
1  1.0  1.0
1  2.0  0.0
2  0.0  0.0
2  3.0  0.0
2  3.0  2.0
2  0.0  2.0
2  0.0  0.0

20.7.2 修改坐标

使用 Shapely 的 set_coordinates() 直接替换坐标:

import geopandas as gpd
from shapely.geometry import Polygon
from shapely import get_coordinates, set_coordinates

rect = Polygon([(0, 0), (4, 0), (4, 3), (0, 3)])
gdf = gpd.GeoDataFrame({"name": ["矩形"]}, geometry=[rect])

# 提取并修改坐标
coords = get_coordinates(gdf.geometry.values)
new_coords = coords.copy()
new_coords[:, 0] += 100  # X 偏移
new_coords[:, 1] += 200  # Y 偏移

# 设置新坐标
new_geoms = set_coordinates(gdf.geometry.values, new_coords)
gdf["shifted"] = gpd.GeoSeries(new_geoms)
print(gdf["shifted"].iloc[0])

输出:

POLYGON ((100 200, 104 200, 104 203, 100 203, 100 200))

20.7.3 坐标精度控制

import geopandas as gpd
import numpy as np
from shapely.geometry import Point
from shapely import get_coordinates, set_coordinates

points = [
    Point(116.39128456789, 39.90735123456),
    Point(121.47365234567, 31.23041567890)
]
gdf = gpd.GeoDataFrame({"city": ["北京", "上海"]}, geometry=points)

# 四舍五入到 6 位小数(约 0.1 米精度)
coords = get_coordinates(gdf.geometry.values)
rounded = np.round(coords, decimals=6)
new_geoms = set_coordinates(gdf.geometry.values.copy(), rounded)
gdf["rounded"] = gpd.GeoSeries(new_geoms)

print("原始:", get_coordinates(gdf.geometry.values))
print("精度控制后:", get_coordinates(gdf["rounded"].values))

输出:

原始: [[116.39128457  39.90735123]
 [121.47365235  31.23041568]]
精度控制后: [[116.391285  39.907351]
 [121.473652  31.230416]]

注意: 经纬度保留 6 位小数约对应 0.1 米精度,通常满足大部分 GIS 应用需求。


20.8 坐标转换与 CRS 变换的区别

20.8.1 仿射变换 vs CRS 投影变换

对比维度 仿射变换 CRS 投影变换
数学基础 线性代数(矩阵运算) 大地测量学(椭球体投影)
变换性质 保持直线和平行性 可能引入变形
参数来源 自定义矩阵参数 标准 CRS 定义(EPSG 等)
适用场景 本地坐标调整、图形变换 地理坐标与投影坐标转换
使用方法 affine_transform() to_crs()

20.8.2 何时使用仿射变换

场景 推荐方法 原因
WGS84 转 Web 墨卡托 to_crs() 标准投影变换
CAD 本地坐标对齐到 GIS 仿射变换 需要平移、旋转、缩放对齐
建筑平面图定位 仿射变换 局部坐标到地理坐标的映射
不同投影间转换 to_crs() 涉及椭球体参数和投影公式
图形镜像翻转 仿射变换 纯几何操作
批量坐标偏移修正 仿射变换 简单的平移操作

20.9 实际应用案例

20.9.1 CAD 数据对齐到地理坐标

import math
import geopandas as gpd
from shapely.geometry import Polygon

# CAD 本地坐标系中的建筑轮廓
cad_buildings = gpd.GeoDataFrame(
    {"name": ["教学楼", "图书馆", "食堂"]},
    geometry=[
        Polygon([(100, 200), (150, 200), (150, 230), (100, 230)]),
        Polygon([(200, 300), (260, 300), (260, 340), (200, 340)]),
        Polygon([(50, 350), (90, 350), (90, 380), (50, 380)])
    ]
)

# 变换参数:缩放 0.00001,旋转 15°,平移到 (116.3, 39.9) 附近
theta = math.radians(15)
s = 0.00001
a = s * math.cos(theta)
b = -s * math.sin(theta)
d = s * math.sin(theta)
e = s * math.cos(theta)

transform_matrix = [a, b, d, e, 116.30, 39.90]
cad_buildings["geo_geom"] = cad_buildings.geometry.affine_transform(transform_matrix)

print("变换后地理坐标:")
for _, row in cad_buildings.iterrows():
    c = row["geo_geom"].centroid
    print(f"  {row['name']}: ({c.x:.6f}, {c.y:.6f})")

输出:

变换后地理坐标:
  教学楼: (116.300652, 39.902400)
  图书馆: (116.301392, 39.903651)
  食堂: (116.299721, 39.903706)

20.9.2 建筑平面图旋转与定位

import math
import numpy as np
import geopandas as gpd
from shapely.geometry import Polygon

# 建筑平面图(本地坐标,单位:米)
rooms = gpd.GeoDataFrame({
    "room": ["大厅", "办公室A", "办公室B", "会议室"],
    "geometry": [
        Polygon([(0, 0), (10, 0), (10, 8), (0, 8)]),
        Polygon([(10, 0), (16, 0), (16, 4), (10, 4)]),
        Polygon([(10, 4), (16, 4), (16, 8), (10, 8)]),
        Polygon([(0, 8), (16, 8), (16, 12), (0, 12)])
    ]
})

def make_matrix(a, b, d, e, xoff, yoff):
    return np.array([[a, b, xoff], [d, e, yoff], [0, 0, 1]])

def to_params(m):
    return [m[0,0], m[0,1], m[1,0], m[1,1], m[0,2], m[1,2]]

# 旋转 30° + 平移到投影坐标 (500000, 4400000)
theta = math.radians(30)
rotate_m = make_matrix(
    math.cos(theta), -math.sin(theta),
    math.sin(theta), math.cos(theta), 0, 0
)
translate_m = make_matrix(1, 0, 0, 1, 500000, 4400000)

combined = translate_m @ rotate_m
rooms["map_geom"] = rooms.geometry.affine_transform(to_params(combined))

print("平面图已定位到地图坐标系:")
for _, row in rooms.iterrows():
    c = row["map_geom"].centroid
    print(f"  {row['room']}: ({c.x:.2f}, {c.y:.2f})")
print(f"\n总面积: {rooms.geometry.unary_union.area:.1f} 平方米")

输出:

平面图已定位到地图坐标系:
  大厅: (500004.34, 4400006.83)
  办公室A: (500012.25, 4400003.60)
  办公室B: (500010.25, 4400007.06)
  会议室: (500003.07, 4400014.93)

总面积: 192.0 平方米

20.10 本章小结

主题 方法/函数 关键要点
仿射变换基础 [a, b, d, e, xoff, yoff] 线性变换 + 平移的组合
向量化变换 GeoSeries.affine_transform() 高性能批量变换
平移变换 [1, 0, 0, 1, xoff, yoff] 最简单的仿射变换
旋转变换 [cosθ, -sinθ, sinθ, cosθ, 0, 0] 注意旋转中心选择
缩放变换 [sx, 0, 0, sy, 0, 0] 均匀与非均匀缩放
组合变换 NumPy 矩阵乘法 顺序不可交换
坐标提取 get_coordinates() 返回 DataFrame/ndarray
坐标修改 set_coordinates() 直接替换坐标
精度控制 np.round() 6 位小数 ≈ 0.1 米
CRS 对比 to_crs() vs affine_transform() 投影变换与几何变换的区别

核心要诀:

  • 仿射变换保持直线和平行性,适用于局部坐标系调整和图形变换
  • affine_transform() 是向量化操作,大数据量时性能远优于逐要素 apply
  • 组合变换时矩阵乘法从右到左应用:最右边的变换最先执行
  • 绕任意点变换需组合平移操作,或直接使用 Shapely 的高级函数
  • 仿射变换与 CRS 投影变换有本质区别,不可混淆使用

掌握仿射变换与坐标操作后,可以灵活处理各种坐标系对齐和几何变换需求。下一章我们将进入数据可视化领域,学习 GeoPandas 的静态可视化方法,使用 Matplotlib 创建专业的地图图表。