6

I created a map using geopandas, but I am unable to add a "North Arrow" on the map.

After creating the map, I have tried to add the "north arrow" using matplotlib.image module and tried different ways (see example below) but none of them provided a good result. I am looking for better code that can add a good "North Arrow to the map"

import matplotlib.image as img

from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, 
    AnnotationBbox

im=img.imread(r'C:\Users\jnisengw\Dropbox\2019\Data 
    Science\QGIS\north_arrow1.png')

imagebox = OffsetImage(im,zoom=0.27)

ab = AnnotationBbox(imagebox, (598500,4699000))

ax.add_artist(ab)

steven
  • 2,130
  • 19
  • 38
Regis
  • 61
  • 1
  • 3
  • We can't see your image. You need to post the image here (not a third-party link, Dropbox, etc.) – smci Sep 24 '19 at 22:38
  • similar technique as this https://stackoverflow.com/questions/34458251/plot-over-an-image-background-in-python/34459284#34459284 – Paul H Sep 25 '19 at 23:13
  • https://www.net-analysis.com/blog/cartopylayout.html – steven Sep 26 '19 at 03:21

3 Answers3

11

If you only need to add a simple arrow, you can also consider the annotate() method.

import geopandas as gpd
import matplotlib.pyplot as plt

gdf = gpd.read_file(gpd.datasets.get_path('nybb'))

fig, ax = plt.subplots(figsize=(6, 6))
gdf.plot(ax=ax)

x, y, arrow_length = 0.5, 0.5, 0.1
ax.annotate('N', xy=(x, y), xytext=(x, y-arrow_length),
            arrowprops=dict(facecolor='black', width=5, headwidth=15),
            ha='center', va='center', fontsize=20,
            xycoords=ax.transAxes)

enter image description here

Notes: When you pass xycoords=ax.transAxes, the x, y coordinate is normalized, and x, y = 0.5, 0.5 means you put the arrowhead in the middle of your map.

steven
  • 2,130
  • 19
  • 38
  • Will this respect the map projection though? I suppose only if xycoords is set to 'data'. – jasmit Jun 16 '23 at 15:26
  • `xycoords` only affects the position of the arrow and does not interact with the shape data. I believe if you use a cylindrical projection, north will remain north, regardless of CRS. However, if you rotate the map, of course, this is no longer the case. – steven Jun 17 '23 at 00:16
1

In case somebody still needs this...
EOmaps v3.1 now has a proper north-arrow and interacts nicely with geopandas!

from eomaps import Maps
m = Maps()
m.add_feature.preset.ocean()
m.add_compass(style="north arrow")

EOmaps - compass

raphael
  • 2,159
  • 17
  • 20
0

Another possibility is create a shapely polygon, and plot it into your matplotlib ax (there are some other solutions like this in this SO question).

Note: You must define the actual north orientation yourself.

enter image description hereenter image description here

Below is code to transform/scale the polygon.

import shapely.plotting
from shapely.geometry import Polygon

def plot_north_arrow( ax, xT=0, yT=0, scale=1 ):
    def t_s(t,xT,yT,s):
        x,y = t
        return (xT+(x*s),yT+(y*s))
    a = [(0, 5), (0, 1), (2, 0)]
    b = [(0, 5), (0, 1), (-2, 0)]
    t_pos = (0.25,6)
    t_pos_x,t_pos_y = t_s(t_pos,xT,yT,scale)
    polygon1 = Polygon( [t_s(t,xT,yT,scale) for t in a] )
    polygon2 = Polygon( [t_s(t,xT,yT,scale) for t in b] )
    shapely.plotting.plot_polygon(polygon1, add_points=False, ax=ax, color=None, facecolor='None', edgecolor='k', linewidth=2)
    shapely.plotting.plot_polygon(polygon2, add_points=False, ax=ax, color=None, facecolor='k', edgecolor='k', linewidth=None)
    ax.text(x=t_pos_x,y=t_pos_y,s='N', fontsize='medium',
            ha='center',
            va='center',weight='bold')
    
fig, ax = plt.subplots(figsize=(2,3))
#...
#... your mapping code...
#...
plot_north_arrow( ax, xT=10, yT=25, scale=2 )

# 
# -- Just for the sample plots.
_ = ax.set_xlim(0,50)
_ = ax.set_ylim(0,50)
ax.set_facecolor((1.0, 0.47, 0.42))

I believe Shapely.plotting was added at v2.0.1, so you'd require pip install shapely==2.0.1

pds
  • 2,242
  • 1
  • 22
  • 20