Tim
Tim

Reputation: 583

How do you display the scale in meters, the north arrow and the axes in latitude and longitude on a map with Geopandas?

With reference to this issue, is it possible to have the scale bar (projected in meters, so 3857 for example) with the x,y axes in latitude, longitude projection (4326) and the north arrow?

I don't see a turnkey solution to do this with geopandas. While this seems to be basic settings for map display with GIS. Is there a technical reason for this?

import geopandas as gpd
from matplotlib_scalebar.scalebar import ScaleBar
import matplotlib.pyplot as plt

df = gpd.read_file(gpd.datasets.get_path('nybb'))
ax = df.to_crs(4326).plot()
ax.add_artist(ScaleBar(1)) #how add ScaleBar for df in 3857? 
plt.show()

Upvotes: 1

Views: 2518

Answers (2)

moss_xyz
moss_xyz

Reputation: 29

For those coming across this question via a search engine of some sort: this is all possible with a package that I have recently developed called matplotlib-map-utils.

North Arrows: It features a dedicated function (north_arrow()) and class (NorthArrow) for creating these with tons of customization features (check out docs\howto_north_arrow in the GitHub repo to see details). It also will account for rotation of a projected coordinate system, if one is passed as an argument.

# Importing
from matplotlib_map_utils.core.north_arrow import NorthArrow, north_arrow
# Setting up a plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Adding a north arrow to the upper-right corner of the axis, 
# in the same projection as whatever geodata you plotted
north_arrow(ax=ax, location="upper right", rotation={"crs":3857, "reference":"center"})

rendered north arrow on a plot

Scale Bars: Similarly, it has a dedicated function (scale_bar()) and class (ScaleBar) for creating these, also with tons of customization features (check out docs\howto_scale_bar in the GitHub repo to see details).

# Importing
from matplotlib_map_utils.core.scale_bar import ScaleBar, scale_bar
# Setting up a plot
fig, ax = matplotlib.pyplot.subplots(1,1, figsize=(5,5), dpi=150)
# Adding a scale bar to the upper-right corner of the axis, 
# in the same projection as whatever geodata you plotted
# Here, this scale bar will have the "boxes" style
scale_bar(ax=ax, location="upper right", style="boxes", bar={"projection":3857})

rendered scale bar on a plot

Crucially, to the question of OP, if an unprojected coordinate system is passed (such as 4326), you can pass in a desired unit and it will calculate the great-circle distance for you, automatically. This won't be as accurate as a projected CRS, but it would allow the OP to maintain the desired lat/lng on the plot axes while displaying units in metres (or miles, or kilometres, or whatever).

Upvotes: 0

jylls
jylls

Reputation: 4705

From this, it looks like you have to compute the great circle distance between two locations A and B with coordinates A=[longitudeA,latitudeA] and B=[longitudeA+1,latitudeA], at the latitude you are interested in (in your case ~40.7°). To compute the great circle distance you can use the 'haversine_distances' from sklearn (here) and multiply it by the radius of the earth 6371000 to get the distance in meters. Once you get this distance dx, you can just pass it to your scalebar with ScaleBar(dx=dx,units="m").

So overall, the code looks like that:

import numpy as np
import geopandas as gpd
from matplotlib_scalebar.scalebar import ScaleBar
import matplotlib.pyplot as plt
from sklearn.metrics.pairwise import haversine_distances

df = gpd.read_file(gpd.datasets.get_path('nybb'))
ax = df.to_crs(4326).plot()
A=[-74.5*np.pi/180.,40.7*np.pi/180.] #Latitude of interest here 40.7 deg, longitude -74.5
B=[-73.5*np.pi/180.,40.7*np.pi/180.] ##Latitude of interest here 40.7 deg, longitude -74.5+1
dx=(6371000)*haversine_distances([A,B])[0,1]
ax.add_artist(ScaleBar(dx=dx,units="m")) 
plt.show()

And the output gives:

enter image description here

Upvotes: 2

Related Questions