Plotting a map using geopandas and matplotlib

后端 未结 3 1170

I have small csv that has 6 coordinates from Birmingham England. I read the csv with pandas then transformed it into GeoPandas DataFrame changing my latitude and longitude colu

3条回答
  •  粉色の甜心
    2021-02-09 15:18

    Just want to add the use case concerning zooming whereby the basemap is updated according to the new xlim and ylim coordinates. A solution I have come up with is:

    • First set callbacks on the ax that can detect xlim_changed and ylim_changed
    • Once both have been detected as changed get the new plot_area calling ax.get_xlim() and ax.get_ylim()
    • Then clear the ax and re-plot the basemap and any other data

    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

    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

    On windows I use Conda (P3.7.3) and don't forget to set the User variables:

    GDAL c:\Users\\Anaconda3\envs\\Library\share\gdal

    PROJLIB c:\Users\\Anaconda3\envs\\Library\share

提交回复
热议问题