Skip to content

[Feature]: Point labels try other positions around the point when the chosen one collides #1011

Open

Description

@jgregoriorincon

Before you start

What problem does this solve?

Step 1 of #995. A point label is tried at one position only, the one its label class chooses (Above Right, say). If that position collides with another label, the label is dropped, even when there is room on the other side of the point. On a busy map of towns or stations, many labels are left out that ArcGIS Pro and QGIS would place.

In the code, each feature and class gives one candidate (type Candidate, crates/render/src/lib.rs), already set at its position. place_labels places it if it fits and otherwise moves on.

The QGIS importer feels this too: QGIS's "around point" and "cartographic" placements are imported as Automatic (crates/map/src/qgis/labels.rs), because there is nothing to map their choice of position onto.

Proposed solution

An option on the label class, "Try other positions when it doesn't fit", off by default.

  • With it on, a point label tries the class's position first, then the others around the point, in QGIS's cartographic order: Above Right, Above Left, Below Right, Below Left, Right, Left, Above, Below (each tried once, the chosen one first). Automatic and Centered start at their own position, then go through the same list.
  • The first position that fits wins. If none fits, the label is unplaced, and View Unplaced shows it once, at the chosen position.
  • Lines and polygons are labelled as today: candidates along lines are step 2, inside and outside polygons step 4.
  • Off by default, because placement is greedy: a higher-priority label taking another position can take the spot a lower-priority label uses today. With the option off, a saved project looks exactly as it does now. The new setting is #[serde(default)] and left out when saved off, as in feat(labels): a halo's own colour and width, and letter spacing, as QGIS's buffer and spacing #964.

How it works:

  • The other positions travel inside the one candidate as alternatives, not as candidates of their own. The turned and tilted views and the extrusion and terrain lifts already move a label's icon along with its text; they move the alternatives the same way.
    • A label still counts once against the candidate caps (40,000 / 20,000).
    • The tilted view's "one candidate per key" rule still holds.
  • place_labels tries the label's position and then each alternative against the grid, in order. The sort stays stable, so the same map and view always give the same labels.
  • The label lock keeps which position won (Map::label_lock holds the key and the alternative's index), so a locked label doesn't jump when the map moves.
  • In the UI, a checkbox in the Labeling pane's Position tab (feat(labels): the Labeling pane in a tab per topic, the map's settings left to the ribbon #917), next to the position. No ribbon control (see How should the ribbon be organised? A look at what's there today #862).
  • An engine command to set it, as for the other class settings, so scripts and the Python API can use it.

Two PRs:

  1. Render: alternatives in the candidate, place_labels, the turned / tilted / lifted paths and the lock, with tests on place_labels straight from candidates. Nothing changes for users yet.
  2. Model and UI: the class setting, the order of positions, the engine command, the pane checkbox, and a test that an old project opens and labels the same.

Once it is in, the QGIS importer can map "around point" and "cartographic" to Automatic with the option on (@pick2510).

Tests: a point whose chosen position is blocked is placed at the next free one; with the option off, it is dropped as today; the same labels in a turned and a tilted view; a locked label stays at the position it won; an old project file gives the same labels.

Estimated scope

Medium: a broader feature that is feasible in a reasonable timeframe

Alternatives considered

  • Each position as a candidate of its own, with the same key. Simpler in place_labels, but eight candidates per point reach the 40,000 cap with 5,000 points and change which labels survive. They also collapse in tilted views, which keep one candidate per key, and need de-duplicating in View Unplaced and in the lock.
  • On by default. More labels on every map at once, but existing projects would change: labels placed today could move or disappear behind a label that took another position.
  • ArcGIS Pro's Standard engine priority matrix (a rank per position, 0 for never). More control, more UI. The fixed order covers the common case, and a matrix can replace the order later without changing the model of alternatives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions