本文对应代码已上传至我的
Github
仓库https://github.com/CNFeffery/DataScienceStudyNotes
在上一篇文章中我们对geopandas
中的数据结构展开了较为全面的学习,其中涉及到面积长度等计算的过程中提到了具体的计算结果与所选择的投影坐标系关系密切,投影坐标系选择的不恰当会带来计算结果的偏差,直接关乎整个分析过程的有效与否。
作为基于geopandas的空间数据分析系列文章的第二篇,通过本文你将会学习到geopandas
中的坐标参考系管理。
在一个二维的平面中,我们可以使用如图1所示的坐标系统,通过坐标\((x_{0},y_{0})\)唯一确定点的位置:
以弧度制下度数为单位的地理坐标系(Geographic Coordinate Systems)帮助我们定位物体在地球球面上的具体位置以及绘制球体地图:
地理坐标系虽然解决了我们在地球球面上定位的问题,但纬度和经度位置没有使用统一的测量单位,因为经度不变的情况下,纬度每变化1单位因为是对固定弧长的映射,所以真实距离是固定不变的,纬度变化1度的真实距离恒等于:
\[ \begin{split} 2\times\pi\times地球极半径/360&\approx2\times3.1415926\times6356.9088/360\\&=110.95(千米) \end{split} \]
可是经度每变化1单位对应的真实距离要随着纬度的变化而变化,经度变化1度的真实距离为:
\[ \begin{split} (2\times\pi\times地球赤道半径/360)\times\cos(当地纬度)&\approx(2\times3.1415926\times6377.830/360)\times\cos(当地纬度)\\&=111.314\times\cos(当地纬度)(千米) \end{split} \]
这就导致我们既不能直接在地理坐标系下精确度量几何对象的长度、面积,也无法直接用地理坐标系在平面上绘制出几何对象真实的形状。为了解决上述问题,各种各样的投影坐标系(Projected Coordinate Systems)被开发出来(图4,其中右下角为地理坐标系,其余均为投影坐标系):
通过前文我们了解到什么是CRS,而在计算机系统中要使用CRS,需要将其文档化,下面我们来了解CRS两种常见的文档存储格式。
Proj4
字符串是一种识别空间或坐标参考系统的简洁方法,通过其定义的语法规则,将想要定义的CRS全部参数信息保存到一条字符串中。
Proj4
字符串包含了一种CRS全部元素信息,用+
连接每个元素定义部分,如下面的例子记录了横轴墨卡托北11区CRS对应的Proj4
字符串:
+proj=utm +zone=11 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0
它记录了如下信息:
proj=utm:声明投影方法为墨卡托
zone=11:声明对应北11区(因为这里是横轴墨卡托所以拥有独立分区,但并不是所有CRS都有分区,且在Proj4
中区号加S才为南半球分区如11S,否则默认为北半球分区)
datum=WGS84:声明基准面为WGS84(基准面是椭球体用来逼近某地区用的,因此各个国家> 都有各自的基准面。国内常用的基准面有:BEIJING1954,XIAN1980,WGS84等)
units=m:声明坐标系单位设置为米
ellps=WGS84:声明椭球面(如何计算地球的圆度)使用WGS84
上述例子记录了投影坐标系的Proj4
,下面我们再来看看地理坐标系对应的Proj4
,如下例:
+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
它记录了如下信息:
proj=longlat:声明这是一个地理坐标系
datum=WGS84:声明基准面为WGS84
ellps=WGS84:声明椭球面使用WGS84
与投影坐标系相比,没有单位units的信息,因为地理坐标系通常单位为十进制度数;而上述两个示例中都带有towgs84=0,0,0,这是一个转换因子,在需要进行数据转换时使用。
EPSG
(European Petroleum Survey Group)编码,使用4或5位数字编码来唯一确定已存在的一种CRS,可以在http://spatialreference.org/ref/epsg/中查看和搜索所有已知的EPSG
与CRS对应关系(图10):
QGIS
中查看:
EPSG
编码为2381。
至此,我们已经对CRS有了较为全面的了解,打好了基础,接下来我们来正式学习geopandas
中的坐标参考系管理,geopandas
调用pyproj
作为CRS管理的后端,因此所有可以被pyproj.CRS.from_user_input()
接受的合法输入同样可以被geopandas
识别,譬如针对上文所说的应用于重庆区域绘图的Xian 1980 / 3-degree Gauss-Kruger CM 108E:
import pyproj
pyproj.CRS.from_user_input('+proj=tmerc +lat_0=0 +lon_0=108 +k=1 +x_0=500000 +y_0=0 +ellps=IAU76 +units=m +no_defs')
pyproj.CRS.from_user_input(2381)
直接传入字符串格式的EPSG
亦可:
Proj4
信息,关键参数与前面
Proj4
一致,只是以
Proj4
形式传入时系统会视作创建未知
CRS一样,因此相对于官方
CRS缺少了一些无关紧要的其他信息:
在上一篇文章(数据科学学习手札74)基于geopandas的空间数据分析——数据结构篇中我们介绍了创建GeoSeries和GeoDataFrame的方法,实际上,现实的空间分析计算任务中,必须要为数据设置合适的CRS,在geopandas.GeoSeries()和geopandas.GeoDataFrame()中就包含参数crs
,下面我们举例说明,还是先用到geopandas
自带的世界国家地区数据,我们从中选择中国(坚持一个中国,我们将台湾地区组合进国土中):
import geopandas as gpd
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
# 利用name字段选择中国区域
china = world.loc[world['name'].isin(['China', 'Taiwan'])]
china
查看其crs
属性即为其对应CRS,为WGS84对应的EPSG:4326
,在当前的CRS下将其绘制出来:
to_crs()
将其再投影到
EPSG:2381
并进行绘制:
EPSG:2381
单位:米),接下来我们参考谷歌地图上点击出的重庆渝中区某地坐标:
GeoSeries
,尝试将其与
EPSG:2381
下的中国地图一同绘制:
from shapely import geometry
import matplotlib.pyplot as plt
cq = gpd.GeoSeries([geometry.Point([106.561203, 29.558078])],
crs='EPSG:4326')
fig, ax = plt.subplots()
china.to_crs(crs='EPSG:2381').plot(ax=ax, color='red', alpha=0.8)
cq.plot(ax=ax, color='orange', markersize=100, marker='x')
plt.xticks(rotation=20)
可以看出我们创建在重庆境内的点并没有绘制在正确的位置,接下来我们对cq
进行再投影,再尝试将其与EPSG:2381
下的中国绘制在一起:
EPSG:2380
为
CRS计算面积:
ESPG:4326
计算面积结果如下:
ESPG:2380
计算出的面积比较接近大家记忆中的960万平方公里。
以上就是本文的全部内容,如有笔误之处望斧正!
下一篇文章将会介绍geopandas
中的文件IO与基础地图制作,敬请期待。