I am plotting a city boundary (geopandas dataframe) to which I added a basemap using contextily.
I would like to apply opacity to the region of the map outside of the city limits.
The below example shows the opposite of the desired effect, as the opacity should be applied everywhere except whithin the city limits.
import osmnx as ox
import geopandas as gpd
import contextily as cx
berlin = ox.geocode_to_gdf('Berlin,Germany')
fig, ax = plt.subplots(1, 1, figsize=(10,10))
_ = ax.axis('off')
berlin.plot(ax=ax,
color='white',
edgecolor='black',
alpha=.7,
)
# basemap
cx.add_basemap(ax,crs=berlin.crs,)
plt.savefig('stackoverflow_question.png',
dpi=100,
bbox_inches='tight',
)
Plot showing opposite of desired result:
You can create a new polygon that is a buffer on the total bounds of your geometry minus your geometry
import osmnx as ox
import geopandas as gpd
import contextily as cx
import matplotlib.pyplot as plt
from shapely.geometry import box
berlin = ox.geocode_to_gdf("Berlin,Germany")
notberlin = gpd.GeoSeries(
[
box(*box(*berlin.total_bounds).buffer(0.1).bounds).difference(
berlin["geometry"].values[0]
)
],
crs=berlin.crs,
)
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
_ = ax.axis("off")
notberlin.plot(
ax=ax,
color="white",
edgecolor="black",
alpha=0.7,
)
# basemap
cx.add_basemap(
ax,
crs=berlin.crs,
)
# plt.savefig('stackoverflow_question.png',
# dpi=100,
# bbox_inches='tight',
# )
Related
I am trying to highlight minimum values of each row using the same color:
For instance, the first row minimum is 0.3. I want to highlight it with blue color. Similarly, for the second row, 0.042 and so on.
Here's the code.
import numpy as np
import seaborn as sns
import matplotlib.pylab as plt
from matplotlib.patches import Rectangle
Pe = np.random.rand(5,5)
annot=True
fig, ax1 = plt.subplots(1)
ax1 = sns.heatmap(Pe, linewidth=0.5,ax=ax1,annot=annot)
You could loop through the rows, find the index of the minimum, and draw a rectangle there. Setting clip_on=False prevents that the rectangles would be clipped by the border.
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
Pe = np.random.rand(5, 5)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 4))
sns.set_style('white')
sns.heatmap(Pe, linewidth=0.5, annot=True, ax=ax1)
for ind, row in enumerate(Pe):
min_col = np.argmin(row)
ax1.add_patch(plt.Rectangle((min_col, ind), 1, 1, fc='none', ec='skyblue', lw=5, clip_on=False))
sns.heatmap(Pe, mask=Pe != Pe.min(axis=1, keepdims=True), annot=True, lw=2, linecolor='black', clip_on=False,
cmap=ListedColormap(['skyblue']), cbar=False, ax=ax2)
plt.tight_layout()
plt.show()
PS: To create animations, the Celluloid library is a lightweight option:
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import seaborn as sns
import numpy as np
from celluloid import Camera
Pe = np.random.rand(5, 5)
fig, ax1 = plt.subplots()
camera = Camera(fig)
sns.set_style('white')
row_array = np.arange(Pe.shape[0]).reshape(-1, 1)
for row in range(Pe.shape[0]):
sns.heatmap(Pe, mask=(Pe != Pe.min(axis=1, keepdims=True)) | (row < row_array),
annot=True, lw=2, linecolor='black', clip_on=False,
cmap=ListedColormap(['skyblue']), cbar=False, ax=ax1)
camera.snap()
animation = camera.animate(interval=800)
animation.save('animation.gif')
plt.show()
For more complicated animations, matplotlib's animation API can be considered.
At the moment, I have a plot of a certain geographic area that is coming from cartopy and I have a geometry object plotted from Geopandas, but no matter what I do I can't get them to combine into one plot. The axes are the same for both and I can actually get the axes of the geometry object onto the geographic area, but then my shape disappears.
Here is the relevant section of code:
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.io.img_tiles as cimgt
import geopandas as gpd
from matplotlib import pyplot as plt
# Plotting the trade area graph
trade_geo_df = trade_area_response.json()['data']
trade_geo_df = gpd.GeoDataFrame(trade_geo_df)
trade_geo_df.loc[:, 'coordinates'] = trade_geo_df.coordinates.map(lambda x: x[0])
trade_geo_df['geometry'] = trade_geo_df.coordinates.apply(shapely.geometry.Polygon)
trade_geo_df['geometry'].plot()
plt.plot(placer_lng, placer_lat, markersize=2, marker='o', color='red') # Just a red dot
fig = plt.figure()
stamen_terrain = cimgt.Stamen('terrain-background')
# Limit the extent of the map to a small longitude/latitude range
ax = fig.add_subplot(1, 1, 1, projection=stamen_terrain.crs)
ax.set_extent([-89, -86, 41, 43], crs=ccrs.Geodetic())
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.ylabels_right = False
gl.xlabels_top = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 10, 'color': 'gray'}
gl.ylabel_style = {'size': 10, 'color': 'gray'}
ax.add_image(stamen_terrain, 8)
ax.plot(placer_lng, placer_lat, markersize=2, marker='o', color='red', transform=ccrs.Geodetic()) # Just another red dot
plt.show()
For more information, the following is the trade_geo_df['geometry'] that is mentioned in the code block:
Out[4]:
0 POLYGON ((-87.94035 41.93909, -87.93965 41.939...
1 POLYGON ((-87.88849 42.01734, -87.88676 42.016...
2 POLYGON ((-87.92825 42.02102, -87.92652 42.020...
3 POLYGON ((-87.86428 42.04548, -87.86255 42.045...
4 POLYGON ((-87.86947 42.05987, -87.86774 42.059...
5 POLYGON ((-87.87466 42.08422, -87.87293 42.084...
6 POLYGON ((-88.03025 42.10923, -88.02852 42.109...
7 POLYGON ((-88.01296 42.10972, -88.01123 42.109...
8 POLYGON ((-87.90750 42.12355, -87.90577 42.123...
9 POLYGON ((-88.01296 42.13131, -88.01123 42.130...
Name: geometry, dtype: geometry
And finally these are the two figures that come up. They have the same axes and I'm just trying to get them to line up on one figure (red dot is in the same location in both, but I know there's something I'm missing.
I've tried setting the axes for the geometry object to be the same as the geographic image, turning them into subplots of the same figure, switching the zorder and putting them in the same figure, etc. but nothing seems to work. I don't have much experience with plotting so any help would be appreciated.
I rearranged your lines of code to their proper places, added/corrected projection data where necessary. The unrelevant lines are commented out. Finally, here is the resulting code that you can try.
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
import cartopy.io.img_tiles as cimgt
import geopandas as gpd
from matplotlib import pyplot as plt
import cartopy.crs as ccrs
stamen_terrain = cimgt.Stamen('terrain-background')
fig, ax = plt.subplots(figsize=(8,6), subplot_kw={'projection': stamen_terrain.crs})
ax.set_extent([-89, -86, 41, 43], crs=ccrs.PlateCarree())
# Plotting the trade area graph
#trade_geo_df = trade_area_response.json()['data']
#trade_geo_df = gpd.GeoDataFrame(trade_geo_df)
#trade_geo_df.loc[:, 'coordinates'] = trade_geo_df.coordinates.map(lambda x: x[0])
#trade_geo_df['geometry'] = trade_geo_df.coordinates.apply(shapely.geometry.Polygon)
#trade_geo_df['geometry'].plot(ax=ax)
ax.plot(-87, 42, markersize=20, marker='o', color='red', transform=ccrs.PlateCarree(), zorder=100) # Just a red dot
gl = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=2, color='gray', alpha=0.5, linestyle='--')
gl.ylabels_right = False
gl.xlabels_top = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
gl.xlabel_style = {'size': 10, 'color': 'gray'}
gl.ylabel_style = {'size': 10, 'color': 'gray'}
ax.add_image(stamen_terrain, 8)
#ax.plot(placer_lng, placer_lat, markersize=2, marker='o', color='red', transform=ccrs.Geodetic()) # Just another red dot
plt.show()
Output:-
I am trying to build an animated heatmap using celluloid. The x & y axis and color scale are the same but my code returns the weird output below.
My code uses seaborn, numpy, pandas, and celluloid and is simplified below:
from celluloid import Camera
## Set up celluloid
fig = plt.figure(figsize=(12, 9))
camera = Camera(fig)
## Loop to create figures
for item in range(len(df)):
row = df.iloc[item]
row = np.array(list(row))
## Create df from row
shape = (8,12)
df_row = pd.DataFrame(row.reshape(shape))
## Build seaborn heatmap
ax = sns.heatmap(df_row, cmap="Greys", annot=False, vmin=0, vmax=1)
ax.set_title(item)
ax.xaxis.tick_top()
for tick in ax.get_yticklabels():
tick.set_rotation(0)
## Snap Celluloid
camera.snap()
anim = camera.animate(interval=500)
anim.save("animation.mp4")
The problem is that seaborn constantly creates a new colorbar. To solve it, a fixed ax for the colorbar needs to be created at the start of the code.
Here is the general setup, using celluloid's Camera. If you leave out cbar_ax=cbar_ax you'll see the strange behavior of an endless caravan of colorbars.
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from celluloid import Camera
fig, (ax, cbar_ax) = plt.subplots(ncols=2, figsize=(12, 9), gridspec_kw={'width_ratios': [10, 1]})
camera = Camera(fig)
for _ in range(20):
sns.heatmap(np.random.rand(8, 12), cmap="magma", annot=False, vmin=0, vmax=1,
ax=ax, cbar_ax=cbar_ax)
ax.xaxis.tick_top()
ax.tick_params(axis='y', labelrotation=0)
camera.snap()
anim = camera.animate(interval=500)
anim.save("animation.mp4")
The critical changes to your code would be:
replace fig = plt.figure(...) by fig, (ax, cbar_ax) = plt.subplots(...)
call sns.heatmap with ax=ax, cbar_ax=cbar_ax
I use different colors and patterns to show three counties on the PA map. The Centre County is represented by the slash lines using hatch='\\'. But I got difficulties to show such pattern on the legend.
I kind of know that this is not going to work, but I tried Line2D([0],[0],color='white',hatch='\\',lw=4,label='Centre County'), and got errors saying something like "hatch is not an attribute".
%matplotlib inline
import geopandas as gpd
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
fig, ax = plt.subplots(1,figsize=(8,8))
pa.plot(ax=ax,color='white',edgecolor='grey')
centre.plot(ax=ax,color='white',hatch='\\\\\\\\',edgecolor='black')
pike.plot(ax=ax,color='grey')
perry.plot(ax=ax,color='red')
LegendElement = [
Line2D([0],[0],color='red',lw=4,label='Perry County'),
Line2D([0],[0],color='grey',lw=4,label='Pike County'),
Line2D([0],[0],color='white',lw=4,label='Centre County')
]
ax.legend(handles=LegendElement,loc='upper right')
When you create polygons, the property facecolor defines the fill color. And to create correct legend for polygon features, mpatches.Patch is needed.
Here is the code that demonstrates how to use facecolor, and mpatches.Patch:
import geopandas as gpd
import matplotlib.pyplot as plt
#from matplotlib.lines import Line2D
import matplotlib.patches as mpatches
from cartopy import crs as ccrs
#fig, ax = plt.subplots(1,figsize=(8,8))
fig, ax = plt.subplots(figsize=(9,9), subplot_kw={'projection': ccrs.PlateCarree()})
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
# cities = gpd.read_file(gpd.datasets.get_path('naturalearth_cities'))
asia = world[(world.continent == "Asia")] #take Asia countries
asia.plot(ax=ax, color="lightgreen")
china = asia[(asia.name == "China")]
india = asia[(asia.name == "India")]
saudi = asia[(asia.name == "Saudi Arabia")]
ax.add_geometries(china['geometry'], crs=ccrs.PlateCarree(), \
facecolor='w', hatch='\\\\\\\\', edgecolor='k', label='China')
ax.add_geometries(india['geometry'], crs=ccrs.PlateCarree(), \
color='grey', label='India')
ax.add_geometries(saudi['geometry'], crs=ccrs.PlateCarree(), \
color='red', label='Saudi Arabia')
LegendElement = [
mpatches.Patch(facecolor='w', hatch='\\\\\\\\', edgecolor='k', label='China'),
mpatches.Patch(color='grey', label='India'),
mpatches.Patch(color='red', label='Saudi Arabia')
]
ax.legend(handles = LegendElement, loc='upper right')
plt.show()
The output plot looks like this:
It seems like some of the methods that work for matplotlib 2D might not be working for matplotlib 3D. I'm not sure.
I'd like to remove the tick marks from all axes, and extend the edge color from the bottom and sides to the top as well. The farthest I have gotten is being able to draw the ticks as white, which looks bad as they are rendered on top of the edge lines.
Below is a big chunk of self-contained code that results in the following image. Any help is much appreciated!
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
mpl.rcParams['ytick.color'] = 'white'
#mpl.rcParams['ytick.left'] = False
sample = np.random.random_integers(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = Axes3D(fig)
#ax.w_xaxis.set_tick_params(color='white')
#ax.axes.tick_params
ax.axes.tick_params(bottom=False, color='blue')
##['size', 'width', 'color', 'tickdir', 'pad', 'labelsize',
##'labelcolor', 'zorder', 'gridOn', 'tick1On', 'tick2On',
##'label1On', 'label2On', 'length', 'direction', 'left', 'bottom',
##'right', 'top', 'labelleft', 'labelbottom',
##'labelright', 'labeltop', 'labelrotation']
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
ax.tick_params(color='red')
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
frame1.axes.zaxis.set_ticklabels([])
#frame1.axes.yaxis.set_tick_params(color='white')
To answer the first bit of the question, about tick removal,
it's probably easiest to just disable the tick lines:
for line in ax.xaxis.get_ticklines():
line.set_visible(False)
for line in ax.yaxis.get_ticklines():
line.set_visible(False)
for line in ax.zaxis.get_ticklines():
line.set_visible(False)
E.g.:
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt
from matplotlib import animation
from mpl_toolkits.mplot3d import Axes3D
sample = np.random.random_integers(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = Axes3D(fig)
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
ax = plt.gca()
ax.xaxis.set_ticklabels([])
ax.yaxis.set_ticklabels([])
ax.zaxis.set_ticklabels([])
for line in ax.xaxis.get_ticklines():
line.set_visible(False)
for line in ax.yaxis.get_ticklines():
line.set_visible(False)
for line in ax.zaxis.get_ticklines():
line.set_visible(False)
For newer versions (e.g. matplotlib 3.5.1) a lot of formatting can be done via mpl_toolkits.mplot3d.axis3d._axinfo:
import numpy as np
from matplotlib import pyplot as plt
sample = np.random.randint(low=1,high=5, size=(10,3))
# Create a figure and a 3D Axes
fig = plt.figure(figsize=(5,5))
ax = fig.add_subplot(projection='3d')
colors = np.mean(sample[:, :], axis=1)
ax.scatter(sample[:,0], sample[:,1], sample[:,2],
marker='o', s=20, c=colors, alpha=1)
for axis in [ax.xaxis, ax.yaxis, ax.zaxis]:
axis.set_ticklabels([])
axis._axinfo['axisline']['linewidth'] = 1
axis._axinfo['axisline']['color'] = (0, 0, 0)
axis._axinfo['grid']['linewidth'] = 0.5
axis._axinfo['grid']['linestyle'] = "-"
axis._axinfo['grid']['color'] = (0, 0, 0)
axis._axinfo['tick']['inward_factor'] = 0.0
axis._axinfo['tick']['outward_factor'] = 0.0
axis.set_pane_color((0.95, 0.95, 0.95))
plt.show()