Skip to content

Draw marker labels into tiles as text, and add vertical labels - #23

Merged
wf9a5m75 merged 1 commit into
developfrom
feat/tile-labels
Nov 5, 2026
Merged

wf9a5m75 merged 1 commit into
developfrom
feat/tile-labels

Conversation

@wf9a5m75

@wf9a5m75 wf9a5m75 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Why

Tiles drew a labelled marker as one baked bitmap per text and position. The street-tree sample labels trees by species and side, so every relabel built a bitmap per combination on the main thread, and the map stalled while panning.

What

Labels are drawn into tiles as text

  • Atlas: tiles put only the body (dot or pin) in the atlas and draw the text after all bodies, on both the GPU and the CPU paths.
  • Shared bodies: icons that differ only in their label share one body bitmap.
  • Lazy baking: ColorDefaultIcon and ImageIcon compute their layout up front. The full bitmap is baked only when the icon is used as a native marker.
  • Pixel alignment: text starts on a whole pixel, so a label that crosses a seam lines up on both tiles.
  • Declutter: a labelled marker wins its declutter cell.
  • Hit testing: taps use the label-inclusive layout size without baking.
  • One layout: DefaultIconLayout has a general form that takes the body's rectangle. The pin and ImageIcon both use it. develop's "center a side label on the pin" fix is preserved as the pin's side centre.

ImageIcon labels

ImageIcon now has the same label options as ColorDefaultIcon: label, labelTextColor, labelTextSize, labelTypeFace, labelStrokeColor, labelPosition. They are appended with defaults, so existing calls are unchanged.

Vertical labels: labelWritingMode

  • Values follow CSS writing-mode: horizontal-tb (default) and vertical-rl / vertical-lr.
  • Vertical text uses CoreText's vertical glyph forms: katakana stand upright, ー becomes a vertical bar, and small kana sit in their vertical position.
  • Use case: markers packed along a horizontal street can be named, because a vertical label is one character wide.
  • Measuring and drawing for both the baked and the tile paths go through one typesetter (LabelTypesetter).

Labels no longer cut at a tile seam right after relabelling

  • Cause: a tile searched its surroundings by the largest overhang any tile had seen so far. Right after a relabel, a tile with no markers of its own could search with a value that had not grown yet, return empty, and leave the label reaching in from its neighbour cut at the seam.
  • Fix: labelled icons record their overhang when they are created, and tiles search by that value.
  • Rejected alternative: scanning all markers per tile version. Reading @Published state for 144k markers cost 0.7–1.4 s per pass, even in a Release build.

API

Additions only, all with defaults (see api/MapConductorCore.api.swift):

  • ColorDefaultIcon.labelWritingMode, defaultLabelWritingMode, and the matching init / copy parameters.
  • ImageIcon's label properties and labelWritingMode, plus the matching init / copy parameters.

Checked

On an iPad, with the street-tree sample (144k markers):

  • Labels: labels appear through the tile path and swap without a freeze.
  • Vertical labels: labels on horizontal streets render vertically.
  • Seams: with the tile grid shown, labels are no longer cut at a seam at zoom 17.5.

Not yet:

  • Native markers: the native-marker path (under minMarkerCount) with horizontal and vertical labels has not been checked on a device.
  • ImageDefaultIcon: it still bakes as before.
  • Unit tests: none added; the package's tests cannot run on the device or simulator here.

The same design is to follow on Android and JS.

Tiles drew a labelled marker as one baked bitmap per text and position, so
relabelling many markers (street trees named by species and side) built a
bitmap per combination on the main thread and the map stalled.

- Tiles now put only the body (dot, pin) in the atlas and draw the text after
  all bodies, on both the GPU and CPU paths. Bodies are shared between icons
  that differ only in their label. A labelled marker wins its declutter cell.
- ImageIcon gains the label options ColorDefaultIcon has. Both compute their
  layout up front and bake the bitmap only when used as a native marker.
- labelWritingMode (CSS writing-mode: horizontal-tb, vertical-rl) sets text
  vertically with CoreText's vertical glyph forms, so labels on markers packed
  along a horizontal street fit one character wide.
- A tile used to search its surroundings by the largest overhang it had seen;
  right after relabelling, a tile with no markers of its own returned empty
  and the label reaching in from the next tile was cut at the seam. Labelled
  icons now record their overhang when created, and tiles search by that.
@wf9a5m75
wf9a5m75 merged commit a899663 into develop Nov 5, 2026
2 checks passed
@wf9a5m75
wf9a5m75 deleted the feat/tile-labels branch November 5, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant