简体   繁体   English

使用 geopandas 和 matplotlib 绘制地图

[英]Plotting a map using geopandas and matplotlib

I have small csv that has 6 coordinates from Birmingham England.我有来自英国伯明翰的 6 个坐标的小 csv。 I read the csv with pandas then transformed it into GeoPandas DataFrame changing my latitude and longitude columns with Shapely Points.我用 Pandas 读取了 csv,然后将其转换为 GeoPandas DataFrame,使用 Shapely Points 更改我的纬度和经度列。 I am now trying to plot my GeoDataframe and all I can see are the points.我现在正在尝试绘制我的 GeoDataframe,我能看到的只是点。 How do I get the Birmingham map represented as well?我如何同时表示伯明翰地图? A good documentation source on GeoPandas would be strongly appreciated too. GeoPandas 上的良好文档来源也将受到强烈赞赏。

from shapely.geometry import Point
import geopandas as gpd
import pandas as pd

df = pd.read_csv('SiteLocation.csv')
df['Coordinates'] = list(zip(df.LONG, df.LAT))
df['Coordinates'] = df['Coordinates'].apply(Point)
# Building the GeoDataframe 
geo_df = gpd.GeoDataFrame(df, geometry='Coordinates')
geo_df.plot()  

The GeoPandas documentation contains an example on how to add a background to a map ( https://geopandas.readthedocs.io/en/latest/gallery/plotting_basemap_background.html ), which is explained in more detail below. GeoPandas 文档包含一个关于如何向地图添加背景的示例 ( https://geopandas.readthedocs.io/en/latest/gallery/plotting_basemap_background.html ),下面有更详细的解释。


You will have to deal with tiles , that are (png) images served through a web server, with a URL like您将不得不处理tiles ,即通过网络服务器提供的(png)图像,其URL类似于

http://.../Z/X/Y.png , where Z is the zoom level, and X and Y identify the tile http://.../Z/X/Y.png ,其中 Z 是缩放级别, X 和 Y 标识图块

And geopandas's doc shows how to set tiles as backgrounds for your plots, fetching the correct ones and doing all the otherwise difficult job of spatial syncing, etc... geopandas 的文档展示了如何将图块设置为绘图的背景,获取正确的图块并完成所有其他困难的空间同步工作等......


Installation 安装

Assuming GeoPandas is already installed, you need the contextily package in addition.假设 GeoPandas 已经安装,你还需要contextily包。 If you are under windows, you may want to pick a look at How to install Contextily?如果你在 windows 下,你可能想看看如何安装 Contextily?

Use case用例

Create a python script and define the contextily helper function创建一个 python 脚本并定义上下文辅助函数

import contextily as ctx def add_basemap(ax, zoom, url='http://tile.stamen.com/terrain/tileZ/tileX/tileY.png'): xmin, xmax, ymin, ymax = ax.axis() basemap, extent = ctx.bounds2img(xmin, ymin, xmax, ymax, zoom=zoom, url=url) ax.imshow(basemap, extent=extent, interpolation='bilinear') # restore original x/y limits ax.axis((xmin, xmax, ymin, ymax))

and play和玩

import matplotlib.pyplot as plt from shapely.geometry import Point import geopandas as gpd import pandas as pd # Let's define our raw data, whose epsg is 4326 df = pd.DataFrame({ 'LAT' :[-22.266415, -20.684157], 'LONG' :[166.452764, 164.956089], }) df['coords'] = list(zip(df.LONG, df.LAT)) # ... turn them into geodataframe, and convert our # epsg into 3857, since web map tiles are typically # provided as such. geo_df = gpd.GeoDataFrame( df, crs ={'init': 'epsg:4326'}, geometry = df['coords'].apply(Point) ).to_crs(epsg=3857) # ... and make the plot ax = geo_df.plot( figsize= (5, 5), alpha = 1 ) add_basemap(ax, zoom=10) ax.set_axis_off() plt.title('Kaledonia : From Hienghène to Nouméa') plt.show()

在此处输入图片说明


Note: you can play with the zoom to find the good resolution for the map. 注意:您可以使用zoom来为地图找到合适的分辨率。 Eg/Ie : 例如/即:

在此处输入图片说明

... and such resolutions implicitly call for changing the x/y limits. ...这样的决议暗中要求更改 x/y 限制。

Try df.unary_union .试试df.unary_union The function will aggregate points into a single geometry.该函数将点聚合成单个几何图形。 Jupyter Notebook can plot it Jupyter Notebook可以绘制它

Just want to add the use case concerning zooming whereby the basemap is updated according to the new xlim and ylim coordinates.只想添加有关缩放的用例,即根据新的xlimylim坐标更新底图。 A solution I have come up with is:我想出的一个解决方案是:

  • First set callbacks on the ax that can detect xlim_changed and ylim_changed首先在可以检测xlim_changedylim_changedax上设置回调
  • Once both have been detected as changed get the new plot_area calling ax.get_xlim() and ax.get_ylim()一旦两者都被检测为更改,获取新的plot_area调用ax.get_xlim()ax.get_ylim()
  • Then clear the ax and re-plot the basemap and any other data然后清除ax并重新绘制底图和任何其他数据

Example for a world map showing the capitals.显示首都的世界地图示例。 You notice when you zoom in the resolution of the map is being updated.当您放大地图的分辨率时,您会注意到正在更新。

import geopandas as gpd
import matplotlib.pyplot as plt
import contextily as ctx


figsize = (12, 10)
osm_url = 'http://tile.stamen.com/terrain/{z}/{x}/{y}.png'
EPSG_OSM = 3857
EPSG_WGS84 = 4326

class MapTools:
    def __init__(self):
        self.cities = gpd.read_file(
            gpd.datasets.get_path('naturalearth_cities'))
        self.cities.crs = EPSG_WGS84
        self.cities = self.convert_to_osm(self.cities)

        self.fig, self.ax = plt.subplots(nrows=1, ncols=1, figsize=figsize)
        self.callbacks_connect()

        # get extent of the map for all cities
        self.cities.plot(ax=self.ax)
        self.plot_area = self.ax.axis()

    def convert_to_osm(self, df):
        return df.to_crs(epsg=EPSG_OSM)

    def callbacks_connect(self):
        self.zoomcallx = self.ax.callbacks.connect(
            'xlim_changed', self.on_limx_change)
        self.zoomcally = self.ax.callbacks.connect(
            'ylim_changed', self.on_limy_change)

        self.x_called = False
        self.y_called = False

    def callbacks_disconnect(self):
        self.ax.callbacks.disconnect(self.zoomcallx)
        self.ax.callbacks.disconnect(self.zoomcally)

    def on_limx_change(self, _):
        self.x_called = True
        if self.y_called:
            self.on_lim_change()

    def on_limy_change(self, _):
        self.y_called = True
        if self.x_called:
            self.on_lim_change()

    def on_lim_change(self):
        xlim = self.ax.get_xlim()
        ylim = self.ax.get_ylim()
        self.plot_area = (*xlim, *ylim)
        self.blit_map()

    def add_base_map_osm(self):
        if abs(self.plot_area[1] - self.plot_area[0]) < 100:
            zoom = 13

        else:
            zoom = 'auto'

        try:
            basemap, extent = ctx.bounds2img(
                self.plot_area[0], self.plot_area[2],
                self.plot_area[1], self.plot_area[3],
                zoom=zoom,
                url=osm_url,)
            self.ax.imshow(basemap, extent=extent, interpolation='bilinear')

        except Exception as e:
            print(f'unable to load map: {e}')

    def blit_map(self):
        self.ax.cla()
        self.callbacks_disconnect()
        cities = self.cities.cx[
            self.plot_area[0]:self.plot_area[1],
            self.plot_area[2]:self.plot_area[3]]
        cities.plot(ax=self.ax, color='red', markersize=3)

        print('*'*80)
        print(self.plot_area)
        print(f'{len(cities)} cities in plot area')

        self.add_base_map_osm()
        self.callbacks_connect()

    @staticmethod
    def show():
        plt.show()


def main():
    map_tools = MapTools()
    map_tools.show()

if __name__ == '__main__':
    main()

Runs on Linux Python3.8 with following pip installs使用以下 pip 安装在 Linux Python3.8 上运行

affine==2.3.0
attrs==19.3.0
autopep8==1.4.4
Cartopy==0.17.0
certifi==2019.11.28
chardet==3.0.4
Click==7.0
click-plugins==1.1.1
cligj==0.5.0
contextily==1.0rc2
cycler==0.10.0
descartes==1.1.0
Fiona==1.8.11
geographiclib==1.50
geopandas==0.6.2
geopy==1.20.0
idna==2.8
joblib==0.14.0
kiwisolver==1.1.0
matplotlib==3.1.2
mercantile==1.1.2
more-itertools==8.0.0
munch==2.5.0
numpy==1.17.4
packaging==19.2
pandas==0.25.3
Pillow==6.2.1
pluggy==0.13.1
py==1.8.0
pycodestyle==2.5.0
pyparsing==2.4.5
pyproj==2.4.1
pyshp==2.1.0
pytest==5.3.1
python-dateutil==2.8.1
pytz==2019.3
rasterio==1.1.1
requests==2.22.0
Rtree==0.9.1
Shapely==1.6.4.post2
six==1.13.0
snuggs==1.4.7
urllib3==1.25.7
wcwidth==0.1.7

Note especially requirement for contextily==1.0rc2请注意对contextily==1.0rc2特别要求

On windows I use Conda (P3.7.3) and don't forget to set the User variables:在 Windows 上,我使用 Conda (P3.7.3) 并且不要忘记设置用户变量:

GDAL c:\\Users\\<username>\\Anaconda3\\envs\\<your environment>\\Library\\share\\gdal

PROJLIB c:\\Users\\<username>\\Anaconda3\\envs\\<your environment>\\Library\\share

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM