I would like to make a Geopandas plot with a few dots. Often, my points are pretty much aligned, horizontally or vertically, which makes my plot to be very wide or narrow. Example:
df = gpd.GeoDataFrame([{"name": "a", "geom": shapely.geometry.Point(473600, 6660740)},
{"name": "b", "geom": shapely.geometry.Point(473580, 6660750)},
{"name": "c", "geom": shapely.geometry.Point(473470, 6660750)}
]).set_geometry("geom")
ax = df.plot()
This gives:
Adding a "figsize" does not change anything. Is there any way to force geopandas/matplotlib to show a more balanced plot, adding padding, without having to play manually with xlim/ylim ?
Thanks!
CodePudding user response:
Assuming there is no solution to tell GeoPandas to automatically (and smartly) adapt its margins to avoid unbalanced plots, here is my solution. Improvement welcome!
def set_optimal_limits(ax, df):
"""
Adapt xlim/ylim to a GeoDataFrame point plot to avoid plot to be too wide when
points are horizontally aligned, and to narrow when points are vertically aligned
Usage :
ax = df.plot()
set_optimal_limits(ax, df)
Parameters
----------
ax: AxesSubplot
plot to resize
df: GeoDataFrame
data to be plotted
Returns
-------
None
"""
plot_ratio = 1.5 # optimal ratio between "one horizontal degree" and "one vertical degree". It depends of the CRS.
# For "polar" CRS, it may also depend of the place on the globe
margins = 1.1 # Avoid having dots on edges of the plot
# Compute dimension of the data
xmin, ymin, xmax, ymax = df.total_bounds
height = (ymax - ymin)
width = (xmax - xmin)
opt_height = max(height, width / plot_ratio)
opt_width = max(width , height*plot_ratio)
# If plot is too narrow, increase xmin. If plot is too wide, increase ylim
if opt_height > height :
ymid = (ymax ymin)/2
mid_height = opt_height * margins / 2
ax.set_ylim(ymid - mid_height, ymid mid_height)
if opt_width > width:
xmid = (xmax xmin)/2
mid_width = opt_width* margins/2
ax.set_xlim(xmid - mid_width, xmid mid_width)
With the example above, it gives the following figure :

CodePudding user response:
You can add margins on either of axes with matplotlib.
df = geopandas.GeoDataFrame([{"name": "a", "geom": shapely.geometry.Point(473600, 6660740)},
{"name": "b", "geom": shapely.geometry.Point(473580, 6660750)},
{"name": "c", "geom": shapely.geometry.Point(473470, 6660750)}
]).set_geometry("geom")
ax = df.plot()
plt.margins(y=1)
The second solution is to specify xlim and ylim based on total bounds.
df = geopandas.GeoDataFrame([{"name": "a", "geom": shapely.geometry.Point(473600, 6660740)},
{"name": "b", "geom": shapely.geometry.Point(473580, 6660750)},
{"name": "c", "geom": shapely.geometry.Point(473470, 6660750)}
]).set_geometry("geom")
margin_y = .5 # margin on y axis as a ratio of y extent
margin_x = .5 # margin on x axis as a ratio of y extent
xmin, ymin, xmax, ymax = df.total_bounds
y_margin = (ymax - ymin) * margin_y
x_margin = (xmax - xmin) * margin_x
ax = df.plot()
ax.set_xlim(xmin - x_margin, xmax x_margin)
ax.set_ylim(ymin - y_margin, ymax y_margin)
You can play with various options of that to the padding you like.


