Smoothing a shapefile output - Basemap python - python

I'm working with a shapefile. I have no issues whatsoever reading it in, plotting it, and making the map pretty-looking. However, when I plot it (after reprojecting it to the correct EPSG using QGIS), the edges are all jagged (as shown below). Is there a way to smooth it using Python?
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
import numpy as np
#insert code for basemap setup m = Basemap(...)
m.arcgisimage(service = 'ESRI_StreetMap_World_2D', xpixels = 1000, verbose = True)
states_info = m.readshapefile('shapefiles/states', 'states')
spc_info = m.readshapefile('shapefiles/corrected_epsg', 'spc', drawbounds = False)
patches = []
ax = plt.gca()
for info, shape in zip(m.spc_info, m.spc):
x, y = zip(*shape)
if info['DN'] == 2:
color = '#80c580'
zorder = 2
patches.append( Polygon(np.array(shape), True))
if info['DN'] == 5:
color = '#f7f780'
zorder = 3
patches.append( Polygon(np.array(shape), True))
ax.add_collection(PatchCollection(patches, facecolor= color, zorder=zorder, alpha = 0.7))
Source for these shapefiles.

This question's answers explain how the Shapely Package has a Simplify method based on the Douglas-Puecker algorithm.

Related

How to keep the same pixel size with scatter_density function in a zoomed subplot

I'm trying to create a zoomed subplot of the "scatter_density" figure, with the same pixel size.
So far, I can't get same pixel sizes : subplot1 and subplot 2 have different density pixel sizes, imposing the same DPI to both figure (10) (cf. figure)
figure : FIP density
I share a part of of my code (without the data import section). There's a lot of extra code, that I did not remove to stay coherent with the figure.
from __future__ import division
from scipy import *
from pylab import *
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import math as m
import numpy as np
import bisect
import matplotlib.patches as patches
import mpl_scatter_density
DPI_sub1=10
DPI_sub2=10
fig = plt.figure(figsize=(12.0, 8.0))
plt.subplots_adjust(bottom = 0., left = 0, top = 1., right = 1)
sub1 = fig.add_subplot(2,1,1,projection='scatter_density')
density=sub1.scatter_density(gpMAX_S_Hydro, gpTau_oct, cmap=white_viridis, dpi=DPI_sub1,label=legend_GYR)
fig.colorbar(density, label='$<FIP>_{GYR02}$ par pixel')
sub1.scatter(xCS, yCS, color = 'red',label='$<FIP>_{Critique}$')
sub1.plot(xSB,ySB,'--', label='$CR_{SB}$',color='darkred')
sub1.set_xlim(Xmin_sub1, Xmax_sub1)
sub1.set_ylim(Ymin_sub1, Ymax_sub1)
sub1.grid(axis = 'y',linewidth=0.2)
sub1.set_ylabel('$\u03C4_{oct,a}$', labelpad = 15)
sub1.legend(loc='upper left')
string='$CS_{SB}$ = '+str('%.2f' % CS_SB_GYR02)
plt.text(71, 100, string, size=LEGEND_SIZE)
sub3 = fig.add_subplot(2,1,2,projection='scatter_density')
density=sub3.scatter_density(gpMAX_S_Hydro, gpTau_oct, cmap=white_viridis,dpi=DPI_sub2,label=legend_GYR)
fig.colorbar(density, label='$<FIP>_{GYR02}$ par pixel')
sub3.plot(xSB,ySB,'--', label='$CR_{SB}$', color='darkred')
lab='$<FIP>_{GYRd20}$'
sub3.set_xlim(Xmin_sub3, Xmax_sub3)
sub3.set_ylim(Ymin_sub3, Ymax_sub3)
sub3.set_xlabel('$\sigma_{H,max}$ (MPa)', labelpad = 15)
sub3.set_ylabel('$\u03C4_{oct,a}$', labelpad = 15)
sub3.grid(axis = 'y',linewidth=0.2)
sub3.fill_between((Xmin_sub1,Xmax_sub1), Ymin_sub1, Ymax_sub1, facecolor=zoomcolor, alpha=0.2)
con1 = patches.ConnectionPatch(xyA=(Xmin_sub1, Ymin_sub1), coordsA=sub1.transData, xyB=(Xmin_sub1,Ymax_sub1), coordsB=sub3.transData, color = zoomcolor)
fig.add_artist(con1)
con2 = patches.ConnectionPatch(xyA=(Xmax_sub1, Ymin_sub1), coordsA=sub1.transData, xyB=(Xmax_sub1, Ymax_sub1), coordsB=sub3.transData, color = zoomcolor)
fig.add_artist(con2)
I'm looking for a plot (subplot1) with the same pixel size as subplot2, and thus to have the same range of scaling bar in both figures.
I tried to work with pixels=figure size (inches) * dpi, but I did not found any solution.
Does anyone have any idea ?
Thank you, Marie.

Inscribing a smaller domain onto a cartopy map in Python

I've been working to make a visual for a poster regarding the physical domain that I am studying. I'm working with a nested domain, so I have 1 smaller domain inside a larger outer domain. I'm trying to create a cartopy plot that shows both the outer domain and inner domain. Ideally, the result would look something like this:
I'm really struggling with trying to get my smaller domain inscribed onto my map. I've attempted to make a Shapely LinearRing to show the inner domain, but it is not working. Here's the code I have created so far:
# Imports
import numpy as np
import sys, os
import matplotlib.pyplot as plt
%matplotlib inline
import netCDF4
from netCDF4 import Dataset
from matplotlib.cm import get_cmap
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from cartopy.feature import NaturalEarthFeature, COLORS
import metpy as mp
import metpy.calc as mpcalc
from metpy.calc import divergence, smooth_gaussian
from metpy.units import units
import xarray as xr
from wrf import getvar, interplevel, to_np, latlon_coords, get_cartopy, cartopy_xlim, cartopy_ylim, ALL_TIMES
from shapely.geometry.polygon import LinearRing
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
from shapely import geometry
# Get 1km lats/lons
lats1km = getvar(ds1, 'lat')
lons1km = getvar(ds1, 'lon')
lat1km_max = to_np(np.max(lats1km))
lat1km_min = to_np(np.min(lats1km))
lon1km_max = to_np(np.max(lons1km))
lon1km_min = to_np(np.min(lons1km))
# Get 3km lats/lons
lats3km = getvar(ds3, 'lat')
lons3km = getvar(ds3, 'lon')
lat3km_max = to_np(np.max(lats3km))
lat3km_min = to_np(np.min(lats3km))
lon3km_max = to_np(np.max(lons3km))
lon3km_min = to_np(np.min(lons3km))
domain = [lon3km_min, lat3km_min, lon3km_max, lat3km_max]
lons = [lat1km_min, lat1km_min, lat1km_max, lat1km_max]
lats = [lon1km_min, lon1km_max, lon1km_max, lon1km_min]
ring = LinearRing(list(zip(lons, lats)))
geom = geometry.box(minx=lon1km_min, miny=lat1km_min, maxx=lon1km_max, maxy=lat1km_max)
# Grab CRS
crs = get_cartopy(wrfin=ds1)
# Create figure and axes
fig = plt.figure(figsize=(20,10))
ax0 = fig.add_subplot(1, 1, 1, projection=crs)
ax0.set_extent([lon3km_min, lon3km_max, lat3km_min, lat3km_max])
ax0.add_geometries([ring], crs=crs, facecolor='blue', edgecolor='black')
ax0.add_geometries([geom], crs=crs, alpha=0.3)
plot_background(ax0)
This yields my outer domain, but not my inner domain:
What am I doing wrong, and what can I do to get my inner domain shown on the map? Thank you for the help! I really appreciate it!
NOTE: I have already attempted the solution in this link. I am still unable to visualize my polygon.
Have a look at EOmaps ! (I'm the dev) it provides simple functions to add static (or interactive) indicators such as projected rectangles or ellipses to cartopy plots in 1 line!
from eomaps import Maps
m = Maps()
m.add_coastlines()
props = dict(xy=(10, 45), xy_crs=4326, radius_crs=4326, shape="rectangles")
m.add_marker(**props, radius=3, fc=(0,1,0,.5), ec="r", lw=2)
m.add_marker(**props, radius=5, fc="none", ec="k")
m.add_marker(**props, radius=(15, 10), fc="none", ec="m", ls="--", lw=2)
m.figure.ax.set_extent((-15., 65., -5., 75.))

Fill oceans in basemap [duplicate]

This question already has answers here:
Plot only on continent in matplotlib
(5 answers)
Closed 5 years ago.
I am trying to plot 1x1 degree data on a matplotlib.Basemap, and I want to fill the ocean with white. However, in order for the boundaries of the ocean to follow the coastlines drawn by matplotlib, the resolution of the white ocean mask should be much higher than the resolution of my data.
After searching around for a long time I tried the two possible solutions:
(1) maskoceans() and is_land() functions, but since my data is lower resolution than the map drawn by basemap it does not look good on the edges. I do not want to interpolate my data to higher resolution either.
(2) m.drawlsmask(), but since zorder cannot be assigned the pcolormesh plot always overlays the mask.
This code
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.basemap as bm
#Make data
lon = np.arange(0,360,1)
lat = np.arange(-90,91,1)
data = np.random.rand(len(lat),len(lon))
#Draw map
plt.figure()
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=72, lon_0=319)
m.drawcoastlines(linewidth=1, color='white')
data, lon = bm.addcyclic(data,lon)
x,y = m(*np.meshgrid(lon,lat))
plt.pcolormesh(x,y,data)
plt.savefig('1.png',dpi=300)
Produces this image:
Adding m.fillcontinents(color='white') produces the following image, which is what I need but to fill the ocean and not the land.
Edit:
m.drawmapboundary(fill_color='lightblue') also fills over land and can therefore not be used.
The desired outcome is that the oceans are white, while what I plotted with plt.pcolormesh(x,y,data) shows up over the lands.
I found a much nicer solution to the problem which uses the polygons defined by the coastlines in the map to produce a matplotlib.PathPatch that overlays the ocean areas. This solution has a much better resolution and is much faster:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
from matplotlib.patches import Path, PathPatch
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##getting the limits of the map:
x0,x1 = ax.get_xlim()
y0,y1 = ax.get_ylim()
map_edges = np.array([[x0,y0],[x1,y0],[x1,y1],[x0,y1]])
##getting all polygons used to draw the coastlines of the map
polys = [p.boundary for p in m.landpolygons]
##combining with map edges
polys = [map_edges]+polys[:]
##creating a PathPatch
codes = [
[Path.MOVETO] + [Path.LINETO for p in p[1:]]
for p in polys
]
polys_lin = [v for p in polys for v in p]
codes_lin = [c for cs in codes for c in cs]
path = Path(polys_lin, codes_lin)
patch = PathPatch(path,facecolor='white', lw=0)
##masking the data:
ax.add_patch(patch)
plt.show()
The output looks like this:
Original solution:
You can use an array with greater resolution in basemap.maskoceans, such that the resolution fits the continent outlines. Afterwards, you can just invert the mask and plot the masked array on top of your data.
Somehow I only got basemap.maskoceans to work when I used the full range of the map (e.g. longitudes from -180 to 180 and latitudes from -90 to 90). Given that one needs quite a high resolution to make it look nice, the computation takes a while:
from matplotlib import pyplot as plt
from mpl_toolkits import basemap as bm
from matplotlib import colors
import numpy as np
import numpy.ma as ma
fig, ax = plt.subplots()
lon_0 = 319
lat_0 = 72
##some fake data
lons = np.linspace(lon_0-60,lon_0+60,10)
lats = np.linspace(lat_0-15,lat_0+15,5)
lon, lat = np.meshgrid(lons,lats)
TOPO = np.sin(np.pi*lon/180)*np.exp(lat/90)
m = bm.Basemap(resolution='i',projection='laea', width=1500000, height=2900000, lat_ts=60, lat_0=lat_0, lon_0=lon_0, ax = ax)
m.drawcoastlines(linewidth=0.5)
x,y = m(lon,lat)
pcol = ax.pcolormesh(x,y,TOPO)
##producing a mask -- seems to only work with full coordinate limits
lons2 = np.linspace(-180,180,10000)
lats2 = np.linspace(-90,90,5000)
lon2, lat2 = np.meshgrid(lons2,lats2)
x2,y2 = m(lon2,lat2)
pseudo_data = np.ones_like(lon2)
masked = bm.maskoceans(lon2,lat2,pseudo_data)
masked.mask = ~masked.mask
##plotting the mask
cmap = colors.ListedColormap(['w'])
pcol = ax.pcolormesh(x2,y2,masked, cmap=cmap)
plt.show()
The result looks like this:

Python Matplotlib add Colorbar

i've got a problem using MatlobLib with "Custom" Shapes from a shapereader. Importing and viewing inserted faces works fine, but i'm not able to place a colorbar on my figure.
I already tried several ways from the tutorial, but im quite sure there is a smart solution for this problem.
maybe somebody can help me, my current code is attached below:
from formencode.national import pycountry
import itertools
from matplotlib import cm, pyplot
from matplotlib import
from mpl_toolkits.basemap import Basemap
from numpy.dual import norm
import cartopy.crs as ccrs
import cartopy.io.shapereader as shpreader
import matplotlib as mpl
import matplotlib.colors as colors
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
import numpy as np
def draw_map_for_non_normalized_data_with_alpha2_counrty_description(data, title=None):
m = Basemap()
ax = plt.axes(projection=ccrs.PlateCarree())
list = []
sum = 0
for key in data:
sum += data[key]
for key in data.keys():
new_val = (data[key]+0.00)/sum
list.append(new_val)
data[key] = new_val
#===========================================================================
# print str(min(list))
# print str(max(list))
#===========================================================================
cmap = mpl.cm.cool
colors = matplotlib.colors.Normalize(min(list)+0.0, max(list)+0.0)
labels = []
features = []
for country in shpreader.Reader(shapename).records():
a3_code = country.attributes["gu_a3"]
try :
a2_code = pycountry.countries.get(alpha3=a3_code).alpha2
except:
a2_code = ""
if a2_code in data:
val = data[a2_code]
color = cm.jet(norm(val))
print str(val) + " value for color: " + str(color)
labels.append(country.attributes['name_long'])
feat = ax.add_geometries(country.geometry, ccrs.PlateCarree(), facecolor=color, label=country.attributes['name_long'])
features.append(feat)
#ax.legend(features, labels, loc='upper right')
#===========================================================================
# fig = pyplot.figure(figsize=(8,3))
# ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
#===========================================================================
#cbar = m.colorbar(location='bottom')
cb1 = mpl.colorbar.ColorbarBase(ax, cmap=cmap,norm=colors,orientation='horizontal')
cb1.set_label('foo')
m.drawcoastlines()
m.drawcountries()
if title:
plt.title(title)
plt.show()
as you can see inside the code, i already tried several ways, but none of them worked for me.
maybe somebody has "the" hint for me.
thanks for help,
kind regards
As mentioned in the comments above, i would think twice about mixing Basemap and Cartopy, is there a specific reason to do so? Both are basically doing the same thing, extending Matplotlib with geographical plotting capabilities. Both are valid to use, they both have their pro's and con's.
In your example you have a Basemap axes m, a Cartopy axes ax and you are using the Pylab interface by using plt. which operates on the currently active axes. Perhaps it theoretically possible, but it seems prone to errors to me.
I cant modify your example to make it work, since the data is missing and your code is not valid Python, the indentation for the function is incorrect for example. But here is a Cartopy-only example showing how you can plot a Shapefile and use the same cmap/norm combination to add a colorbar to the axes.
One difference with your code is that you provide the axes containing the map to the ColorbarBase function, this should be a seperate axes specifically for the colorbar.
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import matplotlib as mpl
import cartopy.io.shapereader as shpreader
fig, ax = plt.subplots(figsize=(12,6),
subplot_kw={'projection': ccrs.PlateCarree()})
norm = mpl.colors.Normalize(vmin=0, vmax=1000000)
cmap = plt.cm.RdYlBu_r
for n, country in enumerate(shpreader.Reader(r'D:\ne_50m_admin_0_countries_lakes.shp').records()):
ax.add_geometries(country.geometry, ccrs.PlateCarree(),
facecolor=cmap(norm(country.attributes['gdp_md_est'])),
label=country.attributes['name'])
ax.set_title('gdp_md_est')
cax = fig.add_axes([0.95, 0.2, 0.02, 0.6])
cb = mpl.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, spacing='proportional')
cb.set_label('gdp_md_est')

Using Colormaps to set color of line in matplotlib

How does one set the color of a line in matplotlib with scalar values provided at run time using a colormap (say jet)? I tried a couple of different approaches here and I think I'm stumped. values[] is a storted array of scalars. curves are a set of 1-d arrays, and labels are an array of text strings. Each of the arrays have the same length.
fig = plt.figure()
ax = fig.add_subplot(111)
jet = colors.Colormap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
retLine, = ax.plot(line, color=colorVal)
#retLine.set_color()
lines.append(retLine)
ax.legend(lines, labels, loc='upper right')
ax.grid()
plt.show()
The error you are receiving is due to how you define jet. You are creating the base class Colormap with the name 'jet', but this is very different from getting the default definition of the 'jet' colormap. This base class should never be created directly, and only the subclasses should be instantiated.
What you've found with your example is a buggy behavior in Matplotlib. There should be a clearer error message generated when this code is run.
This is an updated version of your example:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cm as cmx
import numpy as np
# define some random data that emulates your indeded code:
NCURVES = 10
np.random.seed(101)
curves = [np.random.random(20) for i in range(NCURVES)]
values = range(NCURVES)
fig = plt.figure()
ax = fig.add_subplot(111)
# replace the next line
#jet = colors.Colormap('jet')
# with
jet = cm = plt.get_cmap('jet')
cNorm = colors.Normalize(vmin=0, vmax=values[-1])
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=jet)
print scalarMap.get_clim()
lines = []
for idx in range(len(curves)):
line = curves[idx]
colorVal = scalarMap.to_rgba(values[idx])
colorText = (
'color: (%4.2f,%4.2f,%4.2f)'%(colorVal[0],colorVal[1],colorVal[2])
)
retLine, = ax.plot(line,
color=colorVal,
label=colorText)
lines.append(retLine)
#added this to get the legend to work
handles,labels = ax.get_legend_handles_labels()
ax.legend(handles, labels, loc='upper right')
ax.grid()
plt.show()
Resulting in:
Using a ScalarMappable is an improvement over the approach presented in my related answer:
creating over 20 unique legend colors using matplotlib
I thought it would be beneficial to include what I consider to be a more simple method using numpy's linspace coupled with matplotlib's cm-type object. It's possible that the above solution is for an older version. I am using the python 3.4.3, matplotlib 1.4.3, and numpy 1.9.3., and my solution is as follows.
import matplotlib.pyplot as plt
from matplotlib import cm
from numpy import linspace
start = 0.0
stop = 1.0
number_of_lines= 1000
cm_subsection = linspace(start, stop, number_of_lines)
colors = [ cm.jet(x) for x in cm_subsection ]
for i, color in enumerate(colors):
plt.axhline(i, color=color)
plt.ylabel('Line Number')
plt.show()
This results in 1000 uniquely-colored lines that span the entire cm.jet colormap as pictured below. If you run this script you'll find that you can zoom in on the individual lines.
Now say I want my 1000 line colors to just span the greenish portion between lines 400 to 600. I simply change my start and stop values to 0.4 and 0.6 and this results in using only 20% of the cm.jet color map between 0.4 and 0.6.
So in a one line summary you can create a list of rgba colors from a matplotlib.cm colormap accordingly:
colors = [ cm.jet(x) for x in linspace(start, stop, number_of_lines) ]
In this case I use the commonly invoked map named jet but you can find the complete list of colormaps available in your matplotlib version by invoking:
>>> from matplotlib import cm
>>> dir(cm)
A combination of line styles, markers, and qualitative colors from matplotlib:
import itertools
import matplotlib as mpl
import matplotlib.pyplot as plt
N = 8*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
colormap = mpl.cm.Dark2.colors # Qualitative colormap
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, colormap)):
plt.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=4);
UPDATE: Supporting not only ListedColormap, but also LinearSegmentedColormap
import itertools
import matplotlib.pyplot as plt
Ncolors = 8
#colormap = plt.cm.Dark2# ListedColormap
colormap = plt.cm.viridis# LinearSegmentedColormap
Ncolors = min(colormap.N,Ncolors)
mapcolors = [colormap(int(x*colormap.N/Ncolors)) for x in range(Ncolors)]
N = Ncolors*4+10
l_styles = ['-','--','-.',':']
m_styles = ['','.','o','^','*']
fig,ax = plt.subplots(gridspec_kw=dict(right=0.6))
for i,(marker,linestyle,color) in zip(range(N),itertools.product(m_styles,l_styles, mapcolors)):
ax.plot([0,1,2],[0,2*i,2*i], color=color, linestyle=linestyle,marker=marker,label=i)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.,ncol=3,prop={'size': 8})
U may do as I have written from my deleted account (ban for new posts :( there was). Its rather simple and nice looking.
Im using 3-rd one of these 3 ones usually, also I wasny checking 1 and 2 version.
from matplotlib.pyplot import cm
import numpy as np
#variable n should be number of curves to plot (I skipped this earlier thinking that it is obvious when looking at picture - sorry my bad mistake xD): n=len(array_of_curves_to_plot)
#version 1:
color=cm.rainbow(np.linspace(0,1,n))
for i,c in zip(range(n),color):
ax1.plot(x, y,c=c)
#or version 2: - faster and better:
color=iter(cm.rainbow(np.linspace(0,1,n)))
c=next(color)
plt.plot(x,y,c=c)
#or version 3:
color=iter(cm.rainbow(np.linspace(0,1,n)))
for i in range(n):
c=next(color)
ax1.plot(x, y,c=c)
example of 3:
Ship RAO of Roll vs Ikeda damping in function of Roll amplitude A44

Categories