Python中的地理空间数据--交互式可视化

485 阅读8分钟

发表于2021年12月31日最后更新2022年1月11日

地理空间数据是关于在地球表面有位置的物体、事件或现象的数据。地理空间数据结合了位置信息(通常是地球上的坐标)、属性信息(有关物体、事件或现象的特征)。
Kristin Stock, Hans Guesgen, in Automating Open Source Intelligence, 2016

地理空间数据是空间数据科学的核心组成部分,是数据科学的一个子集。位置、距离和空间互动是SDS中的核心内容,用专门的方法和软件进行处理,以分析、可视化并从空间数据中学习。

在本教程中,你将学习如何使用Python和Folium库来处理地理空间数据,并在迭代的小册子地图上将其可视化。

Folium是一个强大的库,它结合了Python在数据处理方面的优势和Leaflet.js在制图方面的优势。它的易用性允许你创建交互式地图,只需几行代码就能将数据填充进去。

要求

首先,让我们准备好工具!

在写这篇文章的时候,我使用的是最新的版本。

Python: 3.9
Folium: 0.12.1
JupyterLab: 3.2.5

你可以使用Google ColabKaggle Kernels,那里已经为你安装了Folium,如果你在本地使用JupyterLab,你可以用下面的命令轻松安装Folium。

pip install folium

获取数据

在这个研讨会上,我们将使用来自HIFLD开放数据门户的数据集来处理美国的医院地点,这些数据集是在公共领域许可下的。

  • 资料来源。 HIFLD| 从这里或这里下载。
  • **内容。**美国的医院。(地点和其他信息)。
  • **记录。**7596.
  • 最后更新时间:2020年12月8日。
  • **许可。**公共领域。

源数据有多种格式(pdf、表格、网页等),它包括各种各样的有用功能。但是为了这个研讨会的目的,我们将使用.csv 格式,我们只关注较少的列。

ADDRESS: string
STATE: string
TYPE: string
STATUS: boolean
POPULATION: integer
LATITUDE: decimal
LONGITUDE: decimal

成对的(LATITUDE,LONGITUDE)用于在地图上放置位置,而其他列如STATE,TYPESTATUS 用于过滤,最后ADDRESSPOPULATION 作为元数据用于定制地图上的标记。(如果你自己练习本实验室的代码,你可以包括更多的列,如果你想在地图上应用进一步的定制。)

现在,让我们开始编码吧

让我们首先定义一些有用的常量,比如包含我们的目标列名的列表WORKING_COLS ,文件路径FILE_PATH ,以及我们正在处理的州名STATE

FILE_PATH = "__PATH__TO__CSV__FILE__"
WORKING_COLS = ["ADDRESS", "STATE", "TYPE", "STATUS", "POPULATION", "LATITUDE", "LONGITUDE"]
STATE = "CA"

然后,加载数据,只保留上面列出的给定状态的列。

hosp_df = pd.read_csv(FILE_PATH)
hosp_df = hosp_df.loc[hosp_df["STATE"] == STATE, WORKING_COLS]

下面是加载后的数据的样子。

在我们开始使用这些数据之前,让我们先探索一下,看看我们是否需要对其进行清理或应用一些预处理。

缺失值

首先,我们可以检查是否有任何缺失值(NaN ),这可以通过hosp_df.isna().sum().sum() 来验证,这意味着没有缺失值,0

数字值

为了检查数值的一致性,可以使用数据框架的POPULATIONLATITUDELONGITUDE 等数值特征的一些有用的统计数据,describe 方法hosp_df.describe()

值得注意的是,population 列有一些负值,因为min=-999 ,很明显,人口不可能是负值,所以必须通过调整数值来解决0 ,或者放弃含有负值的行。

hosp_df = hosp_df[hosp_df["POPULATION"] >= 0]

有限的值

STATUS 列包含两个唯一的值"CLOSED""OPEN" ,这可以通过hosp_df["STATUS"].unique() 验证。

绘制交互式地图

Folium提供了很多有用的功能来绘制和创建交互式地图,我们将从绘制一个基本的地图开始,然后用我们的数据和高级功能来定制它。

用Folium绘制一个基本的地图

Folium提供了folium.Map() ,它接受location 参数为一个包含一对经纬度的列表,并围绕给定的位置生成地图,为了使生成的地图自动围绕我们的数据居中,我们可以传递数据中的经纬度的平均值。

m=folium.Map(
    location=[hosp_df["LATITUDE"].mean(), hosp_df["LONGITUDE"].mean()],
    zoom_start=6)
m

生成的地图是互动的,你可以使用左上角的按钮或鼠标滚轮来放大和缩小。

在地图上添加瓦片

Folium的默认磁贴集是OpenStreetMap,对于不同的表现形式,我们可以添加不同的磁贴层,如Stamen Terrain、Stamen Water Color、CartoDB Positron等等。每个瓦片组都是用来显示地图的不同特征。

在Folium中可以使用类folium.TileLayer ,在一张地图上添加多个瓦片层,并使用图层控制面板folium.LayerControl ,在它们之间交互切换。

m=folium.Map(
    location=[hosp_df["LATITUDE"].mean(), hosp_df["LONGITUDE"].mean()],
    zoom_start=6)
folium.TileLayer('cartodbdark_matter').add_to(m)
folium.TileLayer('cartodbpositron').add_to(m)
folium.TileLayer('Stamen Terrain').add_to(m)
folium.TileLayer('Stamen Toner').add_to(m)
folium.TileLayer('Stamen Water Color').add_to(m)
folium.LayerControl().add_to(m)
m

folium.LayerControl 在右上角提供了一个图标,可以弹出一个单选组,在不同的图层之间进行切换。

在地图上添加标记

标记在交互式地图中是很重要的,可以指定一个位置。Folium提供了folium.Marker 类来创建一个可以添加到地图上的特定位置的标记。

基本标记

可以通过将该过程作为lambda 函数传递给我们数据框架上的apply 方法,来绘制我们数据中的所有点。

m=folium.Map(
    location=[hosp_df["LATITUDE"].mean(), hosp_df["LONGITUDE"].mean()],
    zoom_start=8)

hosp_df.apply(
    lambda row: folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']]
        ).add_to(m),
    axis=1)
m

定制的标记

为了定制标记,可以将更多的参数传递给folium.Marker

  • popup:folium.Popupstr (可以是html) 点击标记时要显示的内容。
  • tooltip:folium.Tooltipstr (可以是html) 悬停在一个标记上时显示的内容。
  • icon:folium.CustomIcon,folium.Iconfolium.DivIcon - 用于渲染标记的Icon插件。

弹出窗口和工具提示的内容可以通过提供普通格式的文本或html块来定制,也可以通过向folium.CustomIconfolium.Iconfolium.DivIcon 等类中的一个提供给icon 参数来改变标记的外观。folium.Icon 类需要一个图标名称以及提供者的前缀("fa""glyphicon" ,这是默认的)和颜色名称或代码,字形图标的列表可以在这里找到。

m=folium.Map(
    location=[hosp_df['LATITUDE'].mean(), hosp_df['LONGITUDE'].mean()],
    zoom_start=8)

def get_icon(status):
  if status == "OPEN":
    return folium.Icon(icon='heart',
                       color='black',
                       icon_color='#2ecc71'
                       )
  else:
    return folium.Icon(icon='glyphicon-off',
                       color='red')

hosp_df.apply(
    lambda row: folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=row['ADDRESS'],
        tooltip='<h5>Click here for more info</h5>',
        icon=get_icon(row['STATUS']),
        ).add_to(m),
    axis=1)
m

泡泡图

为了在地图上表示数值,我们可以通过将圆圈半径与数据集中的数值绑定来绘制不同大小的圆圈,在我们的例子中,我们用一个半径与它的POPULATION 值成比例的圆圈来表示每个中心的覆盖人口。

folium.CircleMarker 类需要一个必要的参数radius ,此外还有更多的继承参数来定制其外观。

  • radius:number - 圆形标记的半径,单位是像素。(默认:10)
  • stroke:boolean - 是否沿路径绘制笔画。(默认:True)
  • color:str - 描边颜色。
  • weight:number - 描边的宽度,单位:像素。(默认:3)
  • opacity:number - 描边的不透明度,从01.0 。(默认:1.0)
  • fill:boolean - 是否用颜色填充路径。 (默认:True)
  • fill_color:str - 填充颜色。默认为color 参数的值。
  • fill_opacity:number - 填充的不透明度。(默认值:0.2)

**PS.**为了简单起见,我只是将人口值乘以一个系数1/20 。更可靠的方法是将人口值与特定的半径范围进行映射。

m=folium.Map(
    location=[hosp_df['LATITUDE'].mean(), hosp_df['LONGITUDE'].mean()],
    zoom_start=8)

def get_radius(pop):
  return int(pop / 20)

hosp_df.apply(
    lambda row: folium.CircleMarker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        radius=get_radius(row['POPULATION']),
        popup=row['ADDRESS'],
        tooltip='<h5>Click here for more info</h5>',
        stroke=True,
        weight=1,
        color="#3186cc",
        fill=True,
        fill_color="#3186cc",
        opacity=0.9,
        fill_opacity=0.3,
        ).add_to(m),
    axis=1)
m

标识物集群

当在一个密集的地图上工作时,为了避免许多附近的标记相互重叠而造成的混乱,使用标记群是很有用的。

Folium提供了一个简单的方法来设置标记集群,所以不是直接在地图上添加标记,而是将它们添加到一个folium.plugins.MarkerCluster ,然后再添加到地图上。

m=folium.Map(
    location=[hosp_df['LATITUDE'].mean(), hosp_df['LONGITUDE'].mean()],
    zoom_start=8)

cluster = MarkerCluster(name="Hospitals")

def get_icon(status):
  if status == "OPEN":
    return folium.Icon(icon='heart',
                       color='black',
                       icon_color='#2ecc71'
                       )
  else:
    return folium.Icon(icon='glyphicon-off',
                       color='red')

hosp_df.apply(
    lambda row: folium.Marker(
        location=[row['LATITUDE'], row['LONGITUDE']],
        popup=row['ADDRESS'],
        tooltip='<h5>Click here for more info</h5>',
        icon=get_icon(row['STATUS']),
        ).add_to(cluster),
    axis=1)
cluster.add_to(m)
m

当悬停在一个集群上时,会显示该集群所覆盖的区域范围。这个默认行为可以通过设置showCoverageOnHover 选项为false来省略,如下所示。

cluster = MarkerCluster(name="Hospitals", options={"showCoverageOnHover": False})

最后 !

Folium提供了更多的选项来发现和使用在交互式地图上可视化你的地理空间数据,所以值得阅读文档并为你进一步的项目获得实践经验。

你可以在这里找到Jupyter-Notebook来重现本教程中的结果。

阅读我的其他文章 🔥

资源