器→工具, 工具软件

Python地理数据可视化工具GeoPandas

钱魏Way · · 5,178 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

GeoPandas简介

GeoPandas是一个开源项目,旨在让使用Python进行地理空间数据分析变得更容易。它是在Pandas数据分析库的基础上构建的,用于处理地理空间数据。GeoPandas扩展了Pandas,使得可以直接使用空间数据(地理数据)。具体来说,GeoPandas允许对地理数据类型进行高效的操作,并提供了许多用于处理这些数据类型的方法和功能。

主要特性

  • 简便的数据结构:GeoPandas提供了两个主要的数据结构——GeoSeries和GeoDataFrame。这些数据结构继承自Pandas中的Series和DataFrame,但增加了空间操作的功能。
    • GeoSeries:可以视为一个向量,其中每个元素都是一个几何对象(如点、线、多边形)。
    • GeoDataFrame:则是一个表格,其中每一行表示一个地理空间数据对象,一列或多列存储几何对象,其他列则存储与之相关的属性数据。
  • 空间操作:GeoPandas依赖于Shapely库来进行几何操作,如计算面积、长度、距离、合并图形等。
    • 几何操作:包括计算交集、并集、差集和对称差等。
    • 空间关系:如测试对象之间的相交、触碰、包含关系等。
    • 空间索引:为加速空间查询,GeoPandas支持空间索引,类似于Pandas的索引功能。
  • 投影与坐标变换:GeoPandas可以读取和变换坐标系,支持对GeoDataFrame中的几何数据应用不同的投影。
  • 文件读写:GeoPandas可以使用Fiona库读写多种格式的空间数据文件,如GeoJSON、ESRI Shapefile、KML等。
  • 坐标参考系统(CRS):管理和转换地理坐标系统,基于PROJ库。
  • 可视化:GeoPandas也提供了数据可视化的功能,基于matplotlib库,可以快速地生成地图和其它的图表。

GeoSeries与GeoDataFrame

GeoSeries

GeoSeries是包含集合图形的序列,每个元素可以是单个形状或多个形状。Geopandas有三种基本的几何对象:

  • 点:Points/Multi-Points
  • 线:Lines/Multi-Lines
  • 面:Polygons/Multi-Polygons

GeoSeries中的所有条目不必具有相同的几何类型,如果是不同类型,部分操作可能会失败。

Geoseries类实现了Shapely对象的几乎所有属性和方法。在使用GeoSeries时,它将应用于序列中所有几何图形的每一个元素。二元操作可以在两个GeoSeries对象之间进行,这种情况下二元操作将应用于每一个元素。这两个序列将按匹配的索引进行对于操作。二元操作也可以应用于单个几何,此时二元操作将对该几何序列的每个元素进行。在以上两种情况下,操作将会返回Series或者GeoSeries对象。

Attributes属性:

  • area:返回一个Series,它包含GeoSeries中每个几何的面积。
  • bounds:返回一个DataFrame,它包含每个几何的边界,用列值minx,miny,maxx,maxy来表示。
  • total_bounds:返回一个元组,包含整个series边界的minx,miny,maxx,maxy值。包含在序列中的几何体的边界。
  • geom_type:返回一个字符串的Series,字符串指定每个对象的几何类型。
  • is_valid:返回一个布尔型的Series,如果几何体是有效的,该值就为True。

Basic Methods基本方法:

  • distance():返回一个Series,它包含与其他GeoSeries对象(每个元素)或几何对象的最小距离。
  • centroid:返回表示几何重心点的一个GeoSeries。
  • representative_point():返回所有点的一个GeoSeries(经简易计算),这些点必须保证在每个几何的内部。这个点不等于重心点。
  • to_crs():转换GeoSeries中的几何图形到不同的坐标参考系统。当前GeoSeries的crs属性必须被设置。crs属性需要被指定以用于输出,或是用字典形式或是用EPSG编码方式。
  • plot():进行GeoSeries中几何图形的绘制。

Relationship Tests关系测试:

  • geom_almost_equals():返回一个布尔型的Series对象,如果在指定的小数位精度下,每个对象所有点与其他对象大致相等,该值就为True。
  • contains():返回一个布尔型的Series,如果每个对象的内部包含其他对象的内部和边界,并且它们的边界不相接,该值为True。
  • intersects():返回一个布尔型的Series,如果每个对象的边界和内部以其它任何形式与其他对象相交,该值为True。

GeoDataFrame

GeoDataFrame是一个列表数据结构,它包含一个叫做包含geometry的列,这个geometry包含一个GeoSeries。

GeoPandas的安装与使用

GeoPandas的安装

GeoPandas是一个开源项目,它扩展了Pandas库以支持地理空间数据处理。安装GeoPandas通常涉及几个步骤,因为GeoPandas依赖于一些其他的库,这些库可能需要额外的系统依赖项。

  • Pandas:用于数据操作的基础库。
  • Shapely:用于处理几何对象的库。
  • rasterio:用于读写地理空间raster数据(如卫星图像)的库。
  • pyproj:用于坐标系统转换的库。
  • matplotlib和cartopy:用于地理空间数据的可视化。

以下是安装GeoPandas的几种常见方法:

使用pip安装

对于大多数用户来说,最简单的方法是通过pip安装GeoPandas。在命令行中运行以下命令:

pip install geopandas

这通常会安装GeoPandas及其大多数依赖项。然而,某些依赖项(如GDAL)可能需要系统级别的库。如果遇到问题,请尝试以下方法。

使用conda安装

如果你使用Conda管理你的Python环境(例如,通过Miniconda或Anaconda),你可以使用Conda-forge通道来安装GeoPandas和它的所有依赖项。这通常更容易处理复杂的依赖关系。

conda install -c conda-forge geopandas

安装依赖项

如果直接使用pip安装GeoPandas遇到问题,尤其是与GDAL相关的问题,你可能需要先手动安装GDAL。GDAL是一个用于地理空间数据转换的库,GeoPandas依赖于它来处理空间数据。

  • 在Ubuntu/Debian上安装GDAL:sudo apt-get install libgdal-dev
  • 在macOS上安装GDAL:brew install gdal
  • 在Windows上安装GDAL:在Windows上,你可能需要从GDAL官方网站下载并安装预编译的二进制文件,或者通过Conda安装(如上所述)。

验证安装

安装完成后,你可以通过以下方式验证GeoPandas是否已成功安装:

python -c "import geopandas; print(geopandas.__version__)"

GeoPandas的使用

数据的读写

使用 read_file() 可以导入任何的空间矢量数据,主要有 shp 和 GeoJson 两种。

geopandas.read_file(filename, bbox=None, mask=None, rows=None, **kwargs)
  • filename 文件路径
  • bbox 按照给定的条件筛选几何要素,仅加载与边框相交的数据
  • mask 过滤与给定条件相交要素
  • rows 要加载的行数
  • ignore_geometry=Ture 返回 dataframe
  • ignore_fields[],选择不加载这几列

GeoPandas 读取 .shp 文件

import geopandas as gpd
import matplotlib.pyplot as plt

# shp 必须与 shx 同处于一个文件夹内
# 数据来源:https://github.com/GaryBikini/ChinaAdminDivisonSHP
shapefile_path = './Province/province.shp'
gdf = gpd.read_file(shapefile_path)

print(gdf.head())

gdf.plot()
plt.title('Shapefile Data')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.show()

输出内容:

GeoPandas 读取 geojson 文件

import geopandas as gpd
import matplotlib.pyplot as plt

# 数据来源:天地图
file_path = './中国_省.geojson'
gdf = gpd.read_file(file_path)

print(gdf.head())

gdf.plot()
plt.title('Shapefile Data')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.show()

输出内容:

GeoPandas 读取自带数据集

GeoPandas 自带一些示例数据集,方便用户快速上手和测试功能。这些数据集包括地理边界、世界各国的几何信息等。从 GeoPandas 1.0 这些数据已经被移动到单独的geodatasets 包中。

geodatasets 包通过 Bunch 对象组织和管理数据集。这些 Bunch 对象将相关的数据集分组,使得访问和使用这些数据集更加便捷。

  • geoda Bunch 包含与 GeoDa 软件(空间数据分析工具)相关的数据集。这些数据集通常用于空间统计和地理分析。
  • ny Bunch 包含与纽约市相关的数据集,如纽约市的边界、区划和地理信息。这些数据集适合城市规划、交通分析等应用。
  • eea Bunch 包含与欧洲环境署(EEA)相关的数据集,这些数据集通常用于环境、生态和地理分析。
  • abs Bunch 包含与澳大利亚统计局(ABS)相关的数据集,这些数据集适用于人口普查、经济和社会数据的地理分析。
  • naturalearth Bunch 包含来自 NaturalEarth 的数据集,这是一个免费的地理数据集,提供广泛的地理信息,如国家边界、城市位置、河流等。
import geopandas as gpd
import geodatasets
import matplotlib.pyplot as plt

world_path = geodatasets.get_path('naturalearth.land')
gdf_world = gpd.read_file(world_path)
gdf_world.plot()

GeoPandas 读取 DataFrame 数据

将普通的 DataFrame 转换为 GeoDataFrame 是使用 GeoPandas 进行地理空间分析的一个重要步骤。通常,您会有一列包含地理空间信息(例如,经纬度)的 DataFrame,然后将其转换为 GeoDataFrame。

import pandas as pd
import geopandas as gpd

# 创建普通的 DataFrame
data = {
'city': ['New York', 'Los Angeles', 'Chicago'],
'latitude': [40.7128, 34.0522, 41.8781],
'longitude': [-74.0060, -118.2437, -87.6298]
}

df = pd.DataFrame(data)

# 使用 points_from_xy 生成几何点
geometry = gpd.points_from_xy(df['longitude'], df['latitude'])

# 创建 GeoDataFrame
gdf = gpd.GeoDataFrame(df, geometry=geometry)

# 设置坐标参考系统(CRS)
gdf.set_crs(epsg=4326, inplace=True)

print(gdf)

geopandas.points_from_xy 是一个非常方便的函数,用于从包含经纬度信息的列生成几何点。它简化了将普通的 DataFrame 转换为 GeoDataFrame 的过程。

GeoPandas 导出文件

另外也可以将 GeoDataFrame 导出为 ESRI shapefile、GeoJson、GeoPackage 等地理空间文件类型:

world.to_file("countries.shp")
world.to_file("countries.geojson", driver='GeoJSON')
world.to_file("package.gpkg", layer='countries', driver="GPKG")

数据可视化

GeoPandas 基于 matplotlib 库封装高级接口用于制作地图,可以生成多种类型的地理图,如地图、多边形图、散点图等。GeoSeries 或 GeoDataFrame 对象都提供了 plot 函数以进行对象可视化。与 GeoSeries 对象相比,GeoDataFrame 对象提供的 plot 函数在参数上更为复杂,也更为常用。

GeoDataFrame.plot() 方法有许多参数可以用来定制绘图。以下是一些常用的参数:

  • column: str, np.array, 或 Series(默认值为 None)要绘制的数据框列的名称,np.array 或 pd.Series。如果使用 np.array 或 pd.Series,则它们的长度必须与数据框相同。这些值用于为图形着色。如果也设置了 color,则忽略此参数。
  • kind: str 要生成的图形类型。默认是创建地图(“geo”)。其他支持的 pandas 图形类型:
    • ‘line’:折线图
    • ‘bar’:垂直条形图
    • ‘barh’:水平条形图
    • ‘hist’:直方图
    • ‘box’:箱线图
    • ‘kde’:核密度估计图
    • ‘density’:同‘kde’
    • ‘area’:面积图
    • ‘pie’:饼图
    • ‘scatter’:散点图
    • ‘hexbin’:六边形箱图
  • cmap: str(默认值为 None)由 Matplotlib 认可的颜色映射名称。
  • color: str, np.array, 或 Series(默认值为 None)如果指定,所有对象将被均匀着色。
  • ax: matplotlib.pyplot.Artist(默认值为 None)用于绘制图形的轴线。
  • cax: matplotlib.pyplot.Artist(默认值为 None)用于绘制颜色映射图例的轴线。
  • categorical: bool(默认值为 False)如果为 False,颜色映射将反映所绘制列的数值。对于非数值列,此参数将自动设置为 True。
  • legend: bool(默认值为 False)绘制图例。如果未指定列或已指定颜色则会忽略。
  • scheme: str(默认值为 None)分级法名称(需要 mapclassify)。在底层将使用 MapClassifier 对象。支持所有 mapclassify 提供的分级法(例如‘BoxPlot’,‘EqualInterval’,‘FisherJenks’,‘FisherJenksSampled’,‘HeadTailBreaks’,‘JenksCaspall’,‘JenksCaspallForced’,‘JenksCaspallSampled’,‘MaxP’,‘MaximumBreaks’,‘NaturalBreaks’,‘Quantiles’,‘Percentiles’,‘StdMean’,‘UserDefined’)。可以通过 classification_kwds 传递参数。
  • k: int(默认值为 5)类别的数量(如果 scheme 为 None 则忽略)。
  • vmin: None 或 float(默认值为 None)颜色映射的最小值。如果为 None,则使用要绘制列的最小数据值。
  • vmax: None 或 float(默认值为 None)颜色映射的最大值。如果为 None,则使用要绘制列的最大数据值。
  • markersize: str, float 或序列(默认值为 None)仅适用于数据框中的点几何。如果是 str,将使用数据框中 markersize 指定的列的值来设置标记大小。否则,可以是应用于所有点的值,或者与点数相同长度的序列。
  • figsize: 整数元组(默认值为 None)生成的 Matplotlib 图形的尺寸。如果显式给出 axes 参数,则忽略 figsize。
  • legend_kwds: dict(默认值为 None)传递给 pyplot.legend() 或 matplotlib.pyplot.colorbar() 的关键字参数。当指定 scheme 时接受的其他关键字:
    • fmt: string 用于图例中类别边界的格式规范。例如,不需要小数点时:{“fmt”:”{:.0f}”}。
    • labels: 类似列表的对象用于覆盖自动生成标签的图例标签列表。需要有与类别数量(k)相同的元素数量。
    • interval: boolean(默认值为 False)控制 mapclassify 图例括号的选项。如果为 True,图例中将显示开闭区间括号。
  • categories: 类似列表的对象用于分类图的有序列表对象。
  • classification_kwds: dict(默认值为 None)传递给 mapclassify 的关键字参数。
  • missing_kwds: dict(默认值为 None)指定颜色选项(作为 style_kwds)的关键字参数,传递给具有缺失值的几何图形,以覆盖其他样式关键字参数。如果为 None,则不绘制具有缺失值的几何图形。
  • aspect: ‘auto’,‘equal’,None 或 float(默认值为‘auto’)设置轴的纵横比。如果为‘auto’,地图图的默认纵横比为‘equal’;但如果数据未投影(坐标为经纬度),则默认纵横比为 1/cos(df_y*pi/180),其中 df_y 为 GeoDataFrame 中点的 y 坐标(边界框 y 范围的中值),这样经纬度的正方形在图中间看起来是正方形。这意味着等矩形投影。如果为 None,轴的纵横比不会改变。也可以手动设置(浮点数),作为 y 单位与 x 单位的比例。
  • autolim: bool(默认值为 True)更新轴数据限制以包含新几何图形。
  • style_kwds: dict 传递给实际绘图函数的样式选项,例如 edgecolor, facecolor, linewidth, markersize, alpha。

GeoPandas 区域添加文字标签

当使用 GeoPandas 绘制区域数据时,有时期望给每天区域添加文字标签。GeoPandas 本身不提供此功能。主要问题是无法确定文字标签应该添加到哪里。幸运的是 GeoPandas 提供了2 个方法可以帮助我们来的文字标签的位置:

  • Centroid:返回每个多边形的中心点
  • representativepoint():返回多边形内的一个点,它保证在多边形的边界内,但不一定在中心

示例代码:

import geopandas as gpd
import adjustText as aT #https://github.com/Phlya/adjustText
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['KaiTi'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

china = gpd.read_file('https://geo.datav.aliyun.com/areas_v3/bound/100000_full.json').to_crs('EPSG:4573')
china["center"] = china["geometry"].centroid
#china["rep"] = china["geometry"].representative_point()
china_points = china.copy()
china_points.set_geometry("center", inplace=True)
#china_points.set_geometry("rep", inplace=True)
ax = china.plot(figsize=(15,12), color="whitesmoke", edgecolor="lightgrey", linewidth=0.5)
texts = []
for x, y, label in zip(china_points.geometry.x, china_points.geometry.y, china_points["name"]):
    texts.append(plt.text(x, y, label, fontsize=8))
aT.adjust_text(texts, force_points=0.3, force_text=0.8, expand_points=(1,1), expand_text=(1,1), arrowprops=dict(arrowstyle="-", color='grey', lw=0.5))
plt.show()

数据可视化实战:江苏2022年地级市GDP

import geopandas as gpd
import matplotlib.pyplot as plt
import pandas as pd

plt.rcParams['font.sans-serif'] = ['KaiTi'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False #用来正常显示负号

gdp = pd.read_csv("jiangsu-gdp-2022.csv")
gdp.head()

#读取江苏地图数据,数据来自DataV.GeoAtlas,将其投影到EPSG:4573
data = gpd.read_file('https://geo.datav.aliyun.com/areas_v3/bound/320000_full.json').to_crs('EPSG:4573')
#合并数据
data = data.join(gdp.set_index('地级市')["2022年GDP(亿元)"], on='name')
#修改列名
data.rename(columns={'2022年GDP(亿元)':'GDP'}, inplace=True)
data.head()

fig, ax = plt.subplots(figsize=(9,9))

#legend_kwds设置matplotlib的legend参数
data.plot(ax=ax, column='GDP', cmap='coolwarm', legend=True, legend_kwds={'label':"GDP(亿元)", 'shrink':0.5})
ax.axis('off')
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
    x = data.iloc[index].geometry.centroid.x
    y = data.iloc[index].geometry.centroid.y
    name = data.iloc[index]["name"]
    ax.text(x, y, name, ha="center", va="center")
plt.show()

背景地图叠加

contextily是一个Python库,它提供了一种简单的方法将背景地图(通常是Web瓦片地图,如OpenStreetMap、Stamen Maps、Mapbox等)添加到地理空间数据可视化中。使用contextily库可以使地理空间数据可视化更加生动、直观,同时可以提供更多的地理信息。contextily支持使用WGS84 (EPSG:4326)和Spheric Mercator (EPSG:3857)坐标系,在Web地图应用程序中,一般使用EPSG:3857(以米为单位)来显示瓦片地图,并使用EPSG:4326(以经纬度为单位)来标记瓦片地图上的位置。

示例代码:

data = gpd.read_file('https://geo.datav.aliyun.com/areas_v3/bound/320000_full.json').to_crs('EPSG:4573')
#合并数据
data = data.join(gdp.set_index('地级市')["2022年GDP(亿元)"], on='name')
#修改列名
data.rename(columns={'2022年GDP(亿元)':'GDP'}, inplace=True)
data.head()
fig, ax = plt.subplots(figsize=(9,9))
#legend_kwds设置matplotlib的legend参数
data.plot(ax=ax, column='GDP', cmap='coolwarm', legend=True, legend_kwds={'label':"GDP(亿元)", 'shrink':0.5})
ax.axis('off')
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
    x = data.iloc[index].geometry.centroid.x
    y = data.iloc[index].geometry.centroid.y
    name = data.iloc[index]["name"]
    ax.text(x, y, name, ha="center", va="center")
cx.add_basemap(ax, crs='EPSG:4573')
plt.show()

contextily内置了大量的瓦片服务提供商:

使用示例:

fig, ax = plt.subplots(figsize=(9,9))
#legend_kwds设置matplotlib的legend参数
data.plot(ax=ax, column='GDP', cmap='coolwarm', legend=True, legend_kwds={'label':"GDP(亿元)", 'shrink':0.5})
ax.axis('off')
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
    x = data.iloc[index].geometry.centroid.x
    y = data.iloc[index].geometry.centroid.y
    name = data.iloc[index]["name"]
    ax.text(x, y, name, ha="center", va="center")
cx.add_basemap(ax, crs='EPSG:4573', source=cx.providers.CartoDB.Voyager)
plt.show()

由于网络问题,不分地图服务无法使用,可以调整为国内的瓦片地图服务,示例如下:

fig, ax = plt.subplots(figsize=(9,9))
#legend_kwds设置matplotlib的legend参数
data.plot(ax=ax, column='GDP', cmap='coolwarm', legend=True, legend_kwds={'label':"GDP(亿元)", 'shrink':0.5})
ax.axis('off')
ax.set_title('江苏省地级市2022年GDP数据可视化', fontsize=24)
for index in data.index:
    x = data.iloc[index].geometry.centroid.x
    y = data.iloc[index].geometry.centroid.y
    name = data.iloc[index]["name"]
    ax.text(x, y, name, ha="center", va="center")
cx.add_basemap(ax, crs='EPSG:4573', source='http://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}')
plt.show()

参考链接:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注