Search code examples
pythonmatplotlibgeospatialgeopandas

geopandas map centering with countries


I am plotting data for ex-USSR on geopandas and it really does not look nice.

awkward img

I tried this code published early on 1, 2, but adds no help hence I don't need just map, I need to put data on it. And because I'm merging the 'world' (actually consisting only of ex-USSR countries) and coronavirus data "on 'Country'", then I need that dataframe with original country and adjusted polygons.

Key pieces of my code:

url = "https://opendata.arcgis.com/datasets/a21fdb46d23e4ef896f31475217cbb08_1.geojson"
world = gpd.read_file(url)
ex_ussr = ['Ukraine', 'Belarus', 'Kyrgyzstan', 'Azerbaijan', 'Tajikistan', 'Armenia', 'Georgia', 'Russia', 'Kazakhstan', 'Lithuania', 'Latvia', 'Estonia', 'Uzbekistan']
world = world[world['CNTRY_NAME'].isin(ex_ussr)]
df_world = pd.merge(df_covid, world, on='Country')
crs = {'init': 'epsg:4326'}
corona_gpd = gpd.GeoDataFrame(df_world, crs=crs, geometry='geometry')
f, ax = plt.subplots(1, 1, figsize=(30,5))
ax = corona_gpd.plot(column='New cases', cmap='rainbow', ax=ax, legend=True, legend_kwds={'label': 'New Cases by Country'})

Solution

  • This is a challenging question that I am happy to try. The following is a runnable code that will create a geodataframe of russia with good geometry -- the geometry that does not spread apart at the dateline.

    import numpy as np
    import matplotlib.pyplot as plt
    import geopandas as gpd
    
    #import cartopy.crs as ccrs
    #import cartopy
    
    from shapely.geometry import LineString, MultiPolygon, Polygon
    from shapely.ops import split
    from shapely.affinity import translate
    import geopandas
    
    def shift_geom(shift, gdataframe, plotQ=False):
        # this code is adapted from somewhere found in SO
        # *** will give credit here ***
        shift -= 180
        moved_map = []
        splitted_map = []
        border = LineString([(shift,90),(shift,-90)])
    
        for row in gdataframe["geometry"]:
            splitted_map.append(split(row, border))
        for element in splitted_map:
            items = list(element)
            for item in items:
                minx, miny, maxx, maxy = item.bounds
                if minx >= shift:
                    moved_map.append(translate(item, xoff=-180-shift))
                else:
                    moved_map.append(translate(item, xoff=180-shift))
    
        # got `moved_map` as the moved geometry            
        gdf = geopandas.GeoDataFrame({"geometry": moved_map})
        # can move back to original pos by rerun with -ve shift
    
        # can change crs here
        if plotQ:
            fig, ax = plt.subplots()
            gdf.plot(ax=ax)
            plt.show()
    
        return gdf
    
    
    world = geopandas.read_file(geopandas.datasets.get_path('naturalearth_lowres'))
    world = world[['name', 'continent', 'geometry', 'pop_est', 'gdp_md_est']]
    
    ex_ussr = ['Ukraine', 'Belarus', 'Kyrgyzstan', 'Azerbaijan', 'Tajikistan', 'Armenia', \
               'Georgia', 'Kazakhstan', 'Lithuania', 'Latvia', 'Estonia', 'Uzbekistan']
    
    # ex_ussr w/o russia
    ex_ussr_gdf = world[world['name'].isin(ex_ussr)]
    # russia only
    russia = world[ world['name']=='Russia' ]
    
    # manipulate russia's geometry
    rus_shift_90 = shift_geom(90, russia, False)        # Do not plot
    good_geom_rus = shift_geom(-90, rus_shift_90, True) # Plot it
    
    # a plot of new geometry appears
    

    good-russ

    # Create geodataframe with 1 row (Multi-polygon) using this geometry
    newrus_gdf = geopandas.GeoDataFrame( { "name": ["Russia"] , "new_geometry": [good_geom_rus.geometry.unary_union]}, \
                                 geometry="new_geometry", crs="EPSG:4326")
    # Merge `russia` with `newrus_gdf` to get everything in 1 dataframe
    russia_final = russia.merge(right=newrus_gdf , on="name")
    
    # Set the `new_geometry` from `newrus_gdf` as the geometry
    russia_final.set_geometry("new_geometry", drop=True, inplace=True)
    
    # plot all ex_ussr together = `russia_final` + `ex_ussr_gdf`
    rus_ax = russia_final.plot(color='brown')
    ex_ussr_gdf.plot(ax=rus_ax, color="green", ec="black", lw=0.3, alpha=0.75)
    

    final_exussr

    Edit

    To add russia_final to ex_ussr_gdf and plot the result, run this code:-

    ex_ussr_gdf = ex_ussr_gdf.append(russia_final, ignore_index=True)
    ex_ussr_gdf.plot(color="pink", ec="black", lw=0.3)
    

    new_exussr