Stop faking SDF for POI icons, fix the resulting blur
The badge and category glyphs were registered as SDF images (a single alpha shape MapLibre tints at render time via icon-color) purely so one shared sprite could be recolored per category. But SDF rendering assumes the source actually encodes a distance field — a smooth multi-pixel gradient at every edge — and these were just normally antialiased rasters (a sharp ~1px transition). MapLibre's SDF antialiasing pass softened every edge as a result, visibly blurrier than the base map's own icons, worst on the small category glyph. The glyph color never varies (always white), so it never needed runtime tinting at all: _registerCategoryGlyphs now bakes #fff directly into the raster and registers one plain image per category. The badge color does vary (arbitrary per-file hex), but a real POI export only has a handful of distinct category colors, so _registerBadgeColors rasterizes one sharp image per distinct color actually present in the points being shown, cached by color, instead of one shared image tinted at render time.
This commit is contained in:
1 parent
7de8428b05
commit
6f7d332993
2 files changed
+94
-40
No files matched your search
+39
-21
@@ -225,32 +225,50 @@ like a base map style's own small circular POI icons, blending in
|
||||
instead of reading as "your data". A rounded square rather than a
|
||||
pin/teardrop: it matches how the National Park Service itself frames
|
||||
these exact category symbols (below) on its own maps, so the two look
|
||||
native together instead of like mismatched icon styles. It's colored
|
||||
via `["coalesce", ["get", "color"], TRACK_COLOR]` (falling back to the
|
||||
plain track color for a waypoint with no category, e.g. a hand-placed
|
||||
GNSS pin) and — unlike a pin, which has to anchor at its tip — centered
|
||||
directly on the coordinate with plain default anchoring, which also
|
||||
means the category-glyph layer on top needs no offset math: both
|
||||
layers just share the same center anchor at `[0, 0]`. A small curated
|
||||
icon set (`public/js/icons.js`'s `POI_ICONS`, covering the categories
|
||||
p4n2gpx actually uses, not an exhaustive OsmAnd icon set) draws a white
|
||||
glyph on top of the badge for any waypoint whose `icon` matches a known
|
||||
key (filtered out entirely for anything else, so an unrecognized icon
|
||||
key just falls back to a plain colored badge rather than a
|
||||
broken/missing image warning). Six of the seven glyphs are the
|
||||
native together instead of like mismatched icon styles. `_addLayers`
|
||||
defaults a point's `color` to the plain track color right on the
|
||||
feature (`feature.properties.color ||= TRACK_COLOR`) before it ever
|
||||
reaches a layer, rather than a `coalesce` in a style expression — see
|
||||
below for why. Unlike a pin, which has to anchor at its tip, the badge
|
||||
is centered directly on the coordinate with plain default anchoring,
|
||||
which also means the category-glyph layer on top needs no offset math:
|
||||
both layers just share the same center anchor at `[0, 0]`. A small
|
||||
curated icon set (`public/js/icons.js`'s `POI_ICONS`, covering the
|
||||
categories p4n2gpx actually uses, not an exhaustive OsmAnd icon set)
|
||||
draws a white glyph on top of the badge for any waypoint whose `icon`
|
||||
matches a known key (filtered out entirely for anything else, so an
|
||||
unrecognized icon key just falls back to a plain colored badge rather
|
||||
than a broken/missing image warning). Six of the seven glyphs are the
|
||||
National Park Service's own map symbols
|
||||
(`github.com/nationalparkservice/symbol-library`, BSD-licensed,
|
||||
originally forked from Mapbox's Maki project) — a professional,
|
||||
purpose-built icon set beats hand-drawing "fuel pump"/"picnic table"
|
||||
glyphs from scratch; `forest` has no NPS equivalent (that set covers
|
||||
facilities, not land cover) and stays hand-drawn. Both the badge and
|
||||
the category glyphs are rasterized once per style load
|
||||
(`_registerPoiIcons`, an SVG → `<img>` → `<canvas>` → `ImageData` round
|
||||
trip, since these need to be real raster images for `map.addImage`) and
|
||||
registered as SDF images — a single alpha shape MapLibre tints at
|
||||
render time via `icon-color` rather than a fixed-color image. The
|
||||
points source itself
|
||||
is created with
|
||||
facilities, not land cover) and stays hand-drawn.
|
||||
+
|
||||
Both the badge and the glyphs are rasterized from their source SVGs
|
||||
(an `<img>` → `<canvas>` → `ImageData` round trip, since `map.addImage`
|
||||
needs a real raster image) as plain, non-SDF images with the actual
|
||||
target color already baked into the raster — not SDF images (a single
|
||||
alpha shape MapLibre tints at render time via `icon-color`) as this
|
||||
first shipped. SDF assumes the source encodes a genuine distance field
|
||||
(a smooth multi-pixel gradient at every edge); handed a normally
|
||||
antialiased raster (a sharp ~1px transition) instead, MapLibre's SDF
|
||||
antialiasing pass made every glyph and badge visibly softer than the
|
||||
base map's own icons, worst at the glyph's small icon-size. The glyphs
|
||||
are always white, so `_registerCategoryGlyphs` just bakes `#fff` into
|
||||
each one and registers a single fixed image per category, once per
|
||||
style load — no per-render tinting needed at all. The badge's color is
|
||||
per-file data though (an arbitrary hex value from the GPX), so it can't
|
||||
be one fixed image; `_registerBadgeColors` instead rasterizes one sharp
|
||||
image per *distinct* color actually present in the points being shown
|
||||
(`badge-<color>` as the image id, `["concat", "badge-", ["get",
|
||||
"color"]]` as the layer's `icon-image`) — cheap in practice, since a
|
||||
real POI export only ever has a handful of distinct category colors,
|
||||
and results are cached by color so a second file reusing a color
|
||||
already seen registers nothing new.
|
||||
+
|
||||
The points source itself is created with
|
||||
`cluster: true`; nearby points collapse into a stepped-radius,
|
||||
sequential-blue circle sized by `point_count` below `clusterMaxZoom`,
|
||||
clicking one zooms to its `getClusterExpansionZoom()`, and this is
|
||||
|
||||
Reference in new issue
Block a user