Placing elements

The title, scale bar, north arrow, source line and legends all take a position. They understand the same forms, so a position written for one element works for any other. Without a position an element sits at its default place (a corner of the map for the scale bar and the north arrow, above the map for the title, below it for legends).

import mappyng as mp

m = (
    mp.Map(gdf, bbox=[90000, 6040000, 1280000, 7150000])
    + mp.Basemap()
    + mp.ScaleBar(length=100_000, position={"lon": 3.3, "lat": 42.95})
    + mp.NorthArrow(position={"x": "92%", "y": "8%"})
)

A complete example is in the gallery: Placing elements on the map.

Forms of position

Form

Meaning

(0.46, 0.9) or {"x": 0.46, "y": 0.9}

Fractions of the map area (the drawn extent, not the whole document): (0, 0) is its top-left corner and (1, 1) its bottom-right corner. Values outside [0, 1] place the element outside the map, for example y=1.05 for a line under it.

("46%", "90%") or {"x": "46%", "y": "90%"}

The same, written as percentages.

{"px": 120, "py": 80}

SVG pixels in the document.

{"geo": (x, y)}

A point in the map CRS: metres for a Lambert-93 map, degrees for a lon/lat map.

{"geo": (x, y), "crs": "EPSG:3035"}

A point in another CRS, reprojected to the map CRS.

{"lon": 4.0, "lat": 42.6}

A point in WGS 84 longitude/latitude, whatever the map CRS.

Forms can be mixed freely from one element to the next. Within a single position, write one form only: if several keys are present, geo is used first, then lon/lat, then px/py, and x/y last, so the others are ignored without a warning.

What the point designates

The point is the element’s anchor, which differs from one element to the next:

Element

Anchor

ScaleBar

The left end of the bar. It extends to the right, over the length of the bar.

NorthArrow

The tip of the arrow head. The label sits above it.

Source

The start of the text, aligned by text_anchor and turned by rotation.

Title

The baseline of the last line (the subtitle if there is one), aligned by loc: "left" starts at the point, "center" centres on it, "right" ends at it. offset_x and offset_y still apply.

Legends

The top-left corner of the legend, title included. The position goes in the legend options: Legend("Title", position={...}) or the legend dict of the layer.

Anchoring to a place

Geographic positions tie an element to a place rather than to a spot on the page, so it stays put if you change the document size or the bounding box. A typical use is a scale bar over the sea, clear of the coast:

mp.ScaleBar(length=100_000, position={"lon": 3.3, "lat": 42.95})

lon/lat are always WGS 84. To use another CRS, give it explicitly:

mp.NorthArrow(position={"geo": (3_773_000, 2_229_000), "crs": "EPSG:3035"})

Finding the right coordinates is easiest by trial: render, look, move. The resolve_position() function turns any position into SVG pixels if you need to compute a placement in code.

What can go wrong

A malformed or impossible position raises a LayoutError with a message saying why, instead of silently placing the element at a default spot.

Position

Problem

"centre", (0.5,), {"geo": 3}

Not an (x, y) pair or a recognised dict.

{"lon": 2.0}

lon and lat are both required.

{"x": "abc"}

Not a number or a percentage.

{"lon": 2.0, "lat": 48.0} on data with no CRS

A CRS is needed to convert geographic coordinates. Set one on the GeoDataFrame, or use {"geo": (x, y)} in the map’s own coordinates.

A point on the hidden side of an orthographic globe

The projection cannot represent it.

In YAML

Positions are plain values, so they survive to_yaml and from_yaml:

chrome:
  scale_bar:
    length: 100000
    position:
      lon: 3.3
      lat: 42.95
  title:
    text: Mortality
    position:
      x: 2%
      y: -1%

Only the main map is a target for positions: the scale bar, north arrow, source, title and legends belong to the main viewport. Cartouches and the zoom window have their own frame and are placed with Insets and Zoom.