Search code examples
pythonmatplotlibgeospatialmatplotlib-basemapcartopy

Overlay/Fill country boundaries with image in


Is there a way to fill a country with an image similar to R solution using custom library here:

I have a solution where the face colour is filled for instance the below where Italy is blue. However, I would like to add the Italian flag. Is there a way in Python (I have not found much after searching) or is something like QGIS needed:

#create a map where I can load images in to fill the countries
import cartopy
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.io.shapereader as shpreader

flag = "italy.png" #this is a locally saved png.

plt.figure(figsize=(15, 15)) #size of plot
ax = plt.axes(projection=cartopy.crs.TransverseMercator(25))
ax.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=1)
ax.coastlines(resolution='110m') #simplifies the border lines
ax.add_feature(cartopy.feature.OCEAN, facecolor="#40e0d0") #colour of ocean
# ax.gridlines() #adds global grid lines
ax.set_extent ((-7.5, 50, 34, 69), cartopy.crs.PlateCarree()) #makes it european


shpfilename = shpreader.natural_earth(resolution='110m',
                                      category='cultural',
                                      name='admin_0_countries')

for country in shpreader.Reader(shpfilename).records():
    if country.attributes['NAME_LONG'] == "Italy":
        ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                          facecolor="blue",
                          #no attribute like this img= "fd",

                          label=country.attributes['NAME_LONG'])

plt.show()

Any help, much appreciated!


Solution

  • Here is a demo code that does what you need. As a matter of fact, cartopy logo uses this technique to create.

    import cartopy
    import cartopy.crs as ccrs
    import matplotlib.pyplot as plt
    import cartopy.io.shapereader as shpreader
    import matplotlib.patches as mpatches
    import numpy as np
    
    imdat1 = plt.imread('flag-of-italy.jpg', format='jpg') # use your flag
    
    plt.figure(figsize=(8, 8))
    ax = plt.axes(projection=cartopy.crs.TransverseMercator(25))
    ax.add_feature(cartopy.feature.BORDERS, linestyle='-', alpha=1)
    ax.coastlines(resolution='110m')
    ax.add_feature(cartopy.feature.OCEAN, facecolor="#40e0d0")
    # ax.gridlines() #adds global grid lines
    ax.set_extent ((-7.5, 50, 24, 69), cartopy.crs.PlateCarree())
    
    shpfilename = shpreader.natural_earth(resolution='110m',
                                          category='cultural',
                                          name='admin_0_countries')
    
    italy_ctry = None  #use this to grab italy's
    
    for country in shpreader.Reader(shpfilename).records():
        if country.attributes['NAME_LONG'] == "Italy":
            italy_ctry = country
            ax.add_geometries(country.geometry, ccrs.PlateCarree(),
                              facecolor="none",
                              alpha=0.7,
                              zorder=2,
                              label=country.attributes['NAME_LONG'])
    
    # create mpatch from `italy` geometry
    cg = italy_ctry.geometry
    cg2 = cg.simplify(0.02)
    
    if cg2.geometryType()=='MultiPolygon':
        # if == `Polygon`, dont need to loop
        for ea in cg2.geoms:
            cg2xy = ea.exterior.xy  # tuple of (x,y)
            xys = []
            for ea in zip(cg2xy[0], cg2xy[1]):
                #print(ea[0],ea[1])
                xys.append([ea[0],ea[1]])
    
            # add a patch
            poly = mpatches.Polygon(xys, closed=True, ec='r', \
                                    lw=2, fc='yellow', \
                                    transform=ccrs.PlateCarree(), \
                                    alpha=0.5, zorder=30)
    
            plate_carree_transform = ccrs.PlateCarree()._as_mpl_transform(ax)
    
            xtent1 = (6.519950, 17.122259, 35.783370, 47.962952)
            imdat2 = ax.imshow(imdat1, origin='upper', extent=xtent1, \
                transform=ccrs.PlateCarree(), \
                zorder=15, alpha=.9)
    
            ##imdat2 = ax.stock_img()  #for testing
            imdat2.set_clip_path(mpatches.Path(xys), transform=plate_carree_transform) 
        pass
    
    plt.show()
    

    The sample plot (varies with the flag in use):

    italy_wflag