Overlay/Fill country boundaries with image in - python

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!

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):

Related

How to find the right cartopy projection?

I need to plot some data in Germany with cartopy. The data part of my plot works fine (so I deleted it for now). Unfortunately, the shape of the country is deformed due to the projection.
I am currently using the PlateCarree projection, but changing it to Orthographic or others created the same plot.
How to improve the shape?
Code:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from cartopy.io import shapereader
# get country borders
resolution = '10m'
category = 'cultural'
name = 'admin_0_countries'
shpfilename = shapereader.natural_earth(resolution, category, name)
df = geopandas.read_file(shpfilename)
poly = df.loc[df['ADMIN'] == 'Germany']['geometry'].values[0]
# plot
ax = plt.axes(projection=ccrs.PlateCarree())
ax.set_extent([5.8, 15.1, 47.25, 55.1],
crs=ccrs.PlateCarree())
ax.add_geometries(poly,
crs=ccrs.PlateCarree(),
facecolor='gainsboro',
edgecolor='slategray',
lw=0.1,
alpha=.8)
# save plot
save_path = 'germany.png'
plt.savefig(save_path, dpi=250, bbox_inches='tight', pad_inches=0.)
plt.close()
The solution is transforming the Geopandas Dataframe using the same projection as explained here
New output:
germany.png
New code:
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import geopandas
from cartopy.io import shapereader
# get country borders
resolution = "10m"
category = "cultural"
name = "admin_0_countries"
shpfilename = shapereader.natural_earth(resolution, category, name)
df = geopandas.read_file(shpfilename)
df_de = df.loc[df["ADMIN"] == "Germany"]
extent = [6., 14.8, 47.1, 55.1]
# plot
crs = ccrs.Orthographic(
central_longitude=(0.5 * (extent[0] + extent[1])),
central_latitude=(0.5 * (extent[2] + extent[3])),
)
crs_proj4 = crs.proj4_init
df_de.crs = "EPSG:4326"
df_ae = df_de.to_crs(crs_proj4)
fig, ax = plt.subplots(subplot_kw={"projection": crs})
ax.set_extent(extent)
ax.add_geometries(
df_ae["geometry"],
crs=crs,
facecolor="gainsboro",
edgecolor="slategray",
lw=0.1,
alpha=0.8,
)
# save plot
save_path = "germany.png"
plt.savefig(save_path, dpi=250, bbox_inches="tight", pad_inches=0.0)
plt.close()

Spatial interpolation of discrete points onto x/y coordinate mesh grid in Python

I'm still very new to programming and trying to create a contour plot of alkalinity across Hawaii using Cartopy. I will need to interpolate the point values called MODIFIED_TA against an x-y mesh grid but have not been able to figure out how to do this. The code I'm using is:
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import cartopy.crs as ccrs
import cartopy.mpl.ticker as cticker
import statistics
from scipy.interpolate import UnivariateSpline
import numpy as np
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import warnings
warnings.filterwarnings("ignore") # ignoring the warning prompts.
%matplotlib inline
fig = plt.figure(figsize=(15,15))
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree(central_longitude=-170))
landgreen = cfeature.NaturalEarthFeature('physical', 'land', '110m',
edgecolor='face', facecolor='green')
oceanaqua = cfeature.NaturalEarthFeature('physical', 'ocean', '110m',
edgecolor='face', facecolor='aqua')
ax.set_extent([-151.5, -162, 18, 24], ccrs.PlateCarree())
ax.set_title('TOTAL ALKALINITY')
ax.add_feature(landgreen)
ax.add_feature(cfeature.OCEAN, color = 'k')
ax.gridlines(draw_labels=True)
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.grid(linewidth=2, color='black', alpha=0.5, linestyle='--')
lons = all_data.LONGITUDE[:]
lats = all_data.LATITUDE[:]
alk = all_data.MODIFIED_TA[:]
x1,y1 = np.meshgrid(lons,lats)
z1,z2 = np.meshgrid(all_data.MODIFIED_TA,all_data.MODIFIED_TA)
plt.tricontourf(lons,lats,alk, transform=ccrs.PlateCarree(), cmap=cm.gist_rainbow)
plt.colorbar(shrink=0.5)
plt.title('$A_{T}$ VALUES', color = 'k', fontname='Times New Roman',size = 23)
plt.plot()
The result is nothing like what I was hoping for and again, I'm not sure how to interpolate this value so that it comes out as a smooth gradient across the x/y coordinate grid. Any help would be greatly appreciated!
See output here
It's hard to tell for sure without being able to see your data. I tried to create a MRE and it worked. I would start by seeing if this works.
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.mpl.ticker as cticker
import numpy as np
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree(central_longitude=-170))
ax.set_extent([-151.5, -162, 18, 24], ccrs.PlateCarree())
ax.add_feature(cfeature.OCEAN)
ax.gridlines(draw_labels=True)
lon_formatter = cticker.LongitudeFormatter()
lat_formatter = cticker.LatitudeFormatter()
ax.xaxis.set_major_formatter(lon_formatter)
ax.yaxis.set_major_formatter(lat_formatter)
ax.grid(linewidth=2, color='black', alpha=0.5, linestyle='--')
lons = np.random.random(80) * 7 - 160
lats = np.random.random(80) * 4 + 19
alk = np.cos(lons * 10 * np.pi / 180) * np.sin(lats * 20 / 180)
plt.plot(lons, lats, 'k.', transform = ccrs.PlateCarree())
plt.tricontourf(lons,lats,alk, transform=ccrs.PlateCarree(), alpha = 0.5)
plt.colorbar(shrink=0.5)
plt.title('$A_{T}$ VALUES', color = 'k', fontname='Times New Roman',size = 23)
If it does work, then what I'd look at would include:
What are the dimensions of all_data.LONGITUDE, all_data.LATITUDE, all_data.MOTIFIED_TA?
Are there duplicate values?
Does it work when you plot it outside of a projection?
If my example does not work, then it suggests there is something about your install, in which case update it if you can. If the problem still persists, perhaps there is a bug cartopy that needs reporting or a conflict with other packages.
Sorry, I cannot help further.

How to plot station names (text) from csv file to map

I have a plotted map with station locations (from a CSV) but I am finding it difficult to add text to the points using their station name referred to as "STATIONS" in the first column of the CSV file attached.
Below is the code compiled so far. Thanks.
'''
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as feature
import cartopy.io.shapereader as shapereader
from cartopy.mpl.ticker import LongitudeFormatter,
LatitudeFormatter
import pandas as pd
countries = shapereader.natural_earth(resolution='10m',
category='cultural',
name='admin_0_countries')
# Find the Nigeria boundary polygon.
for country in shapereader.Reader(countries).records():
if country.attributes['SU_A3'] == 'NGA':
nigeria = country.geometry
break
else:
raise ValueError('Unable to find the NGA boundary.')
plt.figure(figsize=(20, 10))
ax_map = plt.axes(projection=ccrs.PlateCarree())
ax_map.set_extent([-1, 19, -1, 17], ccrs.PlateCarree())
#ax_map.coastlines()
ax_map.add_feature(feature.COASTLINE, linewidth=.5)
ax_map.add_geometries([nigeria], ccrs.PlateCarree(), edgecolor='0.8',
facecolor='none')
grid_lines = ax_map.gridlines(draw_labels=True)
grid_lines.top_labels = False
grid_lines.right_labels = False
lon_formatter = LongitudeFormatter(zero_direction_label=True)
lat_formatter = LatitudeFormatter()
ax_map.xaxis.set_major_formatter(lon_formatter)
ax_map.yaxis.set_major_formatter(lat_formatter)
df = pd.read_csv("met_ngstation.csv")
plt.scatter(df['LONG'],df['LAT'],
color='red', marker='.', transform=ccrs.PlateCarree())
#plt.savefig('coastlines_ng.pdf')
#plt.savefig('coastlines_ng.png')
plt.show()
'''
Link to CSV file: https://drive.google.com/file/d/1_0b7z7WFfrs5eKY3t7-tykRoZ70Vqjkn/view?usp=sharing
You can use the function below to mark the stations on the plot.
def labelLocation(x, y, val, ax):
dt = pd.concat({'x': x, 'y': y, 'val': val}, axis=1)
for i, point in dt.iterrows():
ax.text(point['x'], point['y'], str(point['val']))
labelLocation(df['LONG'], df['LAT'], df['STATIONS'], plt)

Python Basemap Heatmap

I am trying to copy the method that was done on this page: https://makersportal.com/blog/2018/7/20/geographic-mapping-from-a-csv-file-using-python-and-basemap under "Mapping Interesting Data" to have a color bar associated with my data.
Right now I just get a plain map of South America, which is what I want as my background but there is no data included.
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='mill',
llcrnrlat = -30, #bottom
llcrnrlon = -120, #left
urcrnrlat = 20, #top
urcrnrlon = -50, #right
resolution='c')
m.drawcoastlines()
m.drawcountries()
# format colors for elevation range
SST_min = np.min(df5.DaasgardSST)
SST_max = np.max(df5.DaasgardSST)
cmap = plt.get_cmap('gist_earth')
normalize = matplotlib.colors.Normalize(vmin=SST_min, vmax=SST_max)
# plot SST with different colors
for i in range(0,len(df5.DaasgardSST)):
x,y = m(lon,lat)
color_interp = np.interp(df5,[SST_min,SST_max],[0,30])
plt.plot(x,y,marker='o',markersize=6,color=cmap(int(color_interp)))
# format the colorbar
cax, _ = matplotlib.colorbar.make_axes(ax)
cbar = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap,norm=normalize,label='Elevation')
plt.title('Title')
plt.show()

Contour data with cartopy

I am trying to plot some bathymetry data using cartopy. I slice out a section of the data and plot it over a Mercator projection. It produces a map that looks ok but I get the following error,
IllegalArgumentException: Invalid number of points in LinearRing found 3 - must be 0 or >= 4
Shell is not a LinearRing
Should I be worried about this? My code is below,
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.rcParams['contour.negative_linestyle'] = 'solid'
import cartopy.crs as ccrs
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.feature as cfeature
from netCDF4 import Dataset
# load data and slice out region of interest
_file = 'GEBCO_2014_2D.nc'
gebco = Dataset(_file, mode='r')
g_lons = gebco.variables['lon'][:]
g_lon_inds = np.where((g_lons>=-30) & (g_lons<=10))[0]
g_lons = g_lons[g_lon_inds]
g_lats = gebco.variables['lat'][:]
g_lat_inds = np.where((g_lats>=40) & (g_lats<=65))[0]
g_lats = g_lats[g_lat_inds]
d = gebco.variables['elevation'][g_lat_inds, g_lon_inds]
gebco.close()
# plot data
projection=ccrs.Mercator()
extent = [-30, 10, 40, 65]
fig = plt.figure(figsize=(13.3, 10))
ax = fig.add_subplot(111, projection=projection)
lon_labels = np.arange(-30, 20, 10)
lat_labels = np.arange(40, 75, 10)
gl = ax.gridlines(draw_labels=True, xlocs=lon_labels, ylocs=lat_labels)
gl.xlabels_top = gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
ax.set_extent(extent, crs=ccrs.PlateCarree())
coastline_10m = cfeature.NaturalEarthFeature('physical', 'coastline', '10m',
edgecolor='k', alpha=0.6,
facecolor=cfeature.COLORS['land'])
ax.add_feature(coastline_10m)
CS = plt.contour(g_lons, g_lats, d, [-1000,-150],
colors='k', alpha=0.4, linewidth=0.5, zorder=1,
transform=ccrs.PlateCarree())
plt.clabel(CS, inline=True, fontsize=10, fmt='%i')

Categories

Resources