mirror of
https://github.com/wassname/geopandas.git
synced 2026-10-03 12:31:14 +08:00
ENH: scaling y-axis for plots in geographic CRS (#1290)
This commit is contained in:
1 parent
b78ef4eecc
commit
bbf06a753c
2 files changed
+90
-3
No files matched your search
+45
-3
@@ -282,7 +282,9 @@ def _plot_point_collection(
|
||||
plot_point_collection = deprecated(_plot_point_collection)
|
||||
|
||||
|
||||
def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
|
||||
def plot_series(
|
||||
s, cmap=None, color=None, ax=None, figsize=None, aspect="auto", **style_kwds
|
||||
):
|
||||
"""
|
||||
Plot a GeoSeries.
|
||||
|
||||
@@ -309,6 +311,14 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
|
||||
figsize : pair of floats (default None)
|
||||
Size of the resulting matplotlib.figure.Figure. If the argument
|
||||
ax is given explicitly, figsize is ignored.
|
||||
aspect : 'auto', 'equal' or float (default 'auto')
|
||||
Set aspect of axis. If 'auto', the default aspect for map plots is 'equal'; if
|
||||
however data are not projected (coordinates are long/lat), the aspect is by
|
||||
default set to 1/cos(s_y * pi/180) with s_y the y coordinate of the middle of
|
||||
the GeoSeries (the mean of the y range of bounding box) so that a long/lat
|
||||
square appears square in the middle of the plot. This implies an
|
||||
Equirectangular projection. It can also be set manually (float) as the ratio
|
||||
of y-unit to x-unit.
|
||||
**style_kwds : dict
|
||||
Color options to be passed on to the actual plot function, such
|
||||
as ``edgecolor``, ``facecolor``, ``linewidth``, ``markersize``,
|
||||
@@ -344,7 +354,18 @@ def plot_series(s, cmap=None, color=None, ax=None, figsize=None, **style_kwds):
|
||||
|
||||
if ax is None:
|
||||
fig, ax = plt.subplots(figsize=figsize)
|
||||
ax.set_aspect("equal")
|
||||
|
||||
if aspect == "auto":
|
||||
if s.crs and s.crs.is_geographic:
|
||||
bounds = s.total_bounds
|
||||
y_coord = np.mean([bounds[1], bounds[3]])
|
||||
ax.set_aspect(1 / np.cos(y_coord * np.pi / 180))
|
||||
# formula ported from R package sp
|
||||
# https://github.com/edzer/sp/blob/master/R/mapasp.R
|
||||
else:
|
||||
ax.set_aspect("equal")
|
||||
else:
|
||||
ax.set_aspect(aspect)
|
||||
|
||||
if s.empty:
|
||||
warnings.warn(
|
||||
@@ -429,6 +450,7 @@ def plot_dataframe(
|
||||
legend_kwds=None,
|
||||
classification_kwds=None,
|
||||
missing_kwds=None,
|
||||
aspect="auto",
|
||||
**style_kwds
|
||||
):
|
||||
"""
|
||||
@@ -499,6 +521,14 @@ def plot_dataframe(
|
||||
to be passed on to geometries with missing values in addition to
|
||||
or overwriting other style kwds. If None, geometries with missing
|
||||
values are not plotted.
|
||||
aspect : 'auto', 'equal' or float (default 'auto')
|
||||
Set aspect of axis. If 'auto', the default aspect for map plots is 'equal'; if
|
||||
however data are not projected (coordinates are long/lat), the aspect is by
|
||||
default set to 1/cos(df_y * pi/180) with df_y the y coordinate of the middle of
|
||||
the GeoDataFrame (the mean of the y range of bounding box) so that a long/lat
|
||||
square appears square in the middle of the plot. This implies an
|
||||
Equirectangular projection. It can also be set manually (float) as the ratio
|
||||
of y-unit to x-unit.
|
||||
|
||||
**style_kwds : dict
|
||||
Style options to be passed on to the actual plot function, such
|
||||
@@ -543,7 +573,18 @@ def plot_dataframe(
|
||||
if cax is not None:
|
||||
raise ValueError("'ax' can not be None if 'cax' is not.")
|
||||
fig, ax = plt.subplots(figsize=figsize)
|
||||
ax.set_aspect("equal")
|
||||
|
||||
if aspect == "auto":
|
||||
if df.crs and df.crs.is_geographic:
|
||||
bounds = df.total_bounds
|
||||
y_coord = np.mean([bounds[1], bounds[3]])
|
||||
ax.set_aspect(1 / np.cos(y_coord * np.pi / 180))
|
||||
# formula ported from R package sp
|
||||
# https://github.com/edzer/sp/blob/master/R/mapasp.R
|
||||
else:
|
||||
ax.set_aspect("equal")
|
||||
else:
|
||||
ax.set_aspect(aspect)
|
||||
|
||||
if df.empty:
|
||||
warnings.warn(
|
||||
@@ -564,6 +605,7 @@ def plot_dataframe(
|
||||
ax=ax,
|
||||
figsize=figsize,
|
||||
markersize=markersize,
|
||||
aspect=aspect,
|
||||
**style_kwds
|
||||
)
|
||||
|
||||
|
||||
@@ -794,6 +794,51 @@ class TestNonuniformGeometryPlotting:
|
||||
ax = self.df.plot(alpha=[0.7, 0.2, 0.9])
|
||||
|
||||
|
||||
class TestGeographicAspect:
|
||||
def setup_class(self):
|
||||
pth = get_path("naturalearth_lowres")
|
||||
df = read_file(pth)
|
||||
self.north = df.loc[df.continent == "North America"]
|
||||
self.north_proj = self.north.to_crs("ESRI:102008")
|
||||
bounds = self.north.total_bounds
|
||||
y_coord = np.mean([bounds[1], bounds[3]])
|
||||
self.exp = 1 / np.cos(y_coord * np.pi / 180)
|
||||
|
||||
def test_auto(self):
|
||||
ax = self.north.geometry.plot()
|
||||
assert ax.get_aspect() == self.exp
|
||||
ax2 = self.north_proj.geometry.plot()
|
||||
assert ax2.get_aspect() in ["equal", 1.0]
|
||||
ax = self.north.plot()
|
||||
assert ax.get_aspect() == self.exp
|
||||
ax2 = self.north_proj.plot()
|
||||
assert ax2.get_aspect() in ["equal", 1.0]
|
||||
ax3 = self.north.plot("pop_est")
|
||||
assert ax3.get_aspect() == self.exp
|
||||
ax4 = self.north_proj.plot("pop_est")
|
||||
assert ax4.get_aspect() in ["equal", 1.0]
|
||||
|
||||
def test_manual(self):
|
||||
ax = self.north.geometry.plot(aspect="equal")
|
||||
assert ax.get_aspect() in ["equal", 1.0]
|
||||
ax2 = self.north.geometry.plot(aspect=0.5)
|
||||
assert ax2.get_aspect() == 0.5
|
||||
ax3 = self.north_proj.geometry.plot(aspect=0.5)
|
||||
assert ax3.get_aspect() == 0.5
|
||||
ax = self.north.plot(aspect="equal")
|
||||
assert ax.get_aspect() in ["equal", 1.0]
|
||||
ax2 = self.north.plot(aspect=0.5)
|
||||
assert ax2.get_aspect() == 0.5
|
||||
ax3 = self.north_proj.plot(aspect=0.5)
|
||||
assert ax3.get_aspect() == 0.5
|
||||
ax = self.north.plot("pop_est", aspect="equal")
|
||||
assert ax.get_aspect() in ["equal", 1.0]
|
||||
ax2 = self.north.plot("pop_est", aspect=0.5)
|
||||
assert ax2.get_aspect() == 0.5
|
||||
ax3 = self.north_proj.plot("pop_est", aspect=0.5)
|
||||
assert ax3.get_aspect() == 0.5
|
||||
|
||||
|
||||
class TestMapclassifyPlotting:
|
||||
@classmethod
|
||||
def setup_class(cls):
|
||||
|
||||
Reference in new issue
Block a user