Fix meta-row color regression, show Services/Activities as icon tags
CI / check (push) Successful in 43s
CI / deploy (push) Successful in 15s

The gray color from the previous commit landed on .gpx-popup__meta (the
whole row, label and value both) instead of just .gpx-popup__meta-label —
only the label was supposed to be muted, the value should read as normal
text same as the description above it.

While in there: Services/Activities were showing park4night's own raw,
comma-separated codes ("animaux, point_eau, wc_public"), which don't mean
anything to a reader on their own. PARK4NIGHT_TAGS (icons.js) maps the
codes p4n2gpx's exports actually use to a human label and, where the
National Park Service's icon set (already used for POI_ICONS) has a
reasonable match, a small icon — formatTags (gpx.js) renders each as an
icon+label tag instead of raw text. A code not in that table just falls
back to its own raw text, so nothing is ever hidden.
This commit is contained in:
randy committed 2026-09-07 17:25:37 +02:00
1 parent 152a08f922
commit fd8852c0e1
4 files changed
+193 -20

No files matched your search

+16 -2
View File
@@ -348,12 +348,26 @@ single paragraph, it read as a wall of run-together text.
`formatDescription` pulls out whichever lines match a recognizable `formatDescription` pulls out whichever lines match a recognizable
`Label: value` shape (`Rating`/`Services`/`Activities`) or are a bare `Label: value` shape (`Rating`/`Services`/`Activities`) or are a bare
URL, and renders each kind with its own visual treatment (a bordered URL, and renders each kind with its own visual treatment (a bordered
"meta" block with bold labels for the former, an isolated link line for "meta" block with a muted label for the former, an isolated link line
the latter); everything else — the actual narrative, or a plain GPX for the latter); everything else — the actual narrative, or a plain GPX
file's `<desc>` with none of this structure at all — renders exactly as file's `<desc>` with none of this structure at all — renders exactly as
before, one paragraph with line breaks. So this only ever adds before, one paragraph with line breaks. So this only ever adds
structure on top of recognized lines; it never loses or hides text from structure on top of recognized lines; it never loses or hides text from
files that don't have any. files that don't have any.
+
`Services`/`Activities` get one further step (`formatMetaRow`,
`formatTags`): the value is park4night's own comma-separated codes
(`animaux, point_eau, wc_public`), which on their own told a reader
nothing — split on commas and looked up in `PARK4NIGHT_TAGS`
(`public/js/icons.js`), each renders as a small icon + human label tag
instead. That table covers the codes p4n2gpx's exports actually use
(not park4night's whole API), with a human label always and an icon
only where the icon set below has a reasonable match — most of the
icons are National Park Service symbols again (same source/license as
`POI_ICONS`), a few (bakery, wifi, vehicle wash, ...) have no NPS
equivalent and get a label with no icon. A code this table doesn't
cover at all just renders as its own raw text, same as before this
existed — nothing p4n2gpx might add later is silently dropped.
Track properties:: Every track feature (both formats) also carries Track properties:: Every track feature (both formats) also carries
`distance_meters`, `elevation_gain_meters`, `duration_secs`, `distance_meters`, `elevation_gain_meters`, `duration_secs`,
+27 -1
View File
@@ -458,7 +458,6 @@ html, body {
margin-top: 8px; margin-top: 8px;
padding-top: 6px; padding-top: 6px;
border-top: 1px solid #eee; border-top: 1px solid #eee;
color: #888;
font-size: 12px; font-size: 12px;
line-height: 1.5; line-height: 1.5;
} }
@@ -468,9 +467,36 @@ html, body {
} }
.gpx-popup__meta-label { .gpx-popup__meta-label {
color: #888;
margin-right: 4px; margin-right: 4px;
} }
.gpx-popup__tags {
display: flex;
flex-wrap: wrap;
gap: 4px 10px;
margin-top: 2px;
}
.gpx-popup__tag {
display: inline-flex;
align-items: center;
gap: 3px;
}
.gpx-popup__tag-icon {
display: flex;
width: 13px;
height: 13px;
color: #555;
flex-shrink: 0;
}
.gpx-popup__tag-icon svg {
width: 100%;
height: 100%;
}
.gpx-popup__link { .gpx-popup__link {
margin-top: 6px; margin-top: 6px;
font-size: 12px; font-size: 12px;
+29 -6
View File
@@ -8,6 +8,7 @@ import {
ROUTE_ICON, ROUTE_ICON,
MAP_MARKER_ICON, MAP_MARKER_ICON,
POI_ICONS, POI_ICONS,
PARK4NIGHT_TAGS,
} from "/js/icons.js"; } from "/js/icons.js";
import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js"; import { notifySidePanelOpen, onlyOneSidePanelOpen } from "/js/panels.js";
import { formatActivityType } from "/js/track_profile.js"; import { formatActivityType } from "/js/track_profile.js";
@@ -828,12 +829,7 @@ function formatDescription(description) {
const bodyText = body.join("\n").trim(); const bodyText = body.join("\n").trim();
if (bodyText) parts.push(`<p class="gpx-popup__description">${linkify(bodyText).replace(/\n/g, "<br>")}</p>`); if (bodyText) parts.push(`<p class="gpx-popup__description">${linkify(bodyText).replace(/\n/g, "<br>")}</p>`);
if (meta.length > 0) { if (meta.length > 0) {
const rows = meta const rows = meta.map(([, label, value]) => formatMetaRow(label, value)).join("");
.map(
([, label, value]) =>
`<div class="gpx-popup__meta-row"><span class="gpx-popup__meta-label">${escapeHtml(label)}</span> ${escapeHtml(value)}</div>`,
)
.join("");
parts.push(`<div class="gpx-popup__meta">${rows}</div>`); parts.push(`<div class="gpx-popup__meta">${rows}</div>`);
} }
links.forEach((url) => parts.push(`<p class="gpx-popup__link">${urlToLink(url)}</p>`)); links.forEach((url) => parts.push(`<p class="gpx-popup__link">${urlToLink(url)}</p>`));
@@ -841,6 +837,33 @@ function formatDescription(description) {
return parts.join(""); return parts.join("");
} }
// Services/Activities get rendered as icon+label tags (PARK4NIGHT_TAGS,
// icons.js) rather than their raw comma-separated park4night codes — "vtt,
// eaux_vives, peche" told a reader nothing on its own. Rating (or anything
// else matching DESCRIPTION_META_LINE) stays plain text; a park4night code
// this app hasn't catalogued yet just falls back to showing its raw text,
// same as before PARK4NIGHT_TAGS existed, so nothing is ever hidden.
function formatMetaRow(label, value) {
const isTagList = /^(services|activities)$/i.test(label);
const valueHtml = isTagList
? `<div class="gpx-popup__tags">${formatTags(value)}</div>`
: ` ${escapeHtml(value)}`;
return `<div class="gpx-popup__meta-row"><span class="gpx-popup__meta-label">${escapeHtml(label)}</span>${valueHtml}</div>`;
}
function formatTags(value) {
return value
.split(",")
.map((code) => code.trim())
.filter(Boolean)
.map((code) => {
const tag = PARK4NIGHT_TAGS[code];
const icon = tag?.icon ? `<span class="gpx-popup__tag-icon">${tag.icon}</span>` : "";
return `<span class="gpx-popup__tag">${icon}${escapeHtml(tag?.label ?? code)}</span>`;
})
.join("");
}
// Turns bare URLs in untrusted text into safe <a> links: the string is // Turns bare URLs in untrusted text into safe <a> links: the string is
// split on the URL pattern and every piece is escaped — including the URL // split on the URL pattern and every piece is escaped — including the URL
// itself, both as href and as link text — before any HTML is assembled, so // itself, both as href and as link text — before any HTML is assembled, so
+121 -11
View File
@@ -91,6 +91,21 @@ export const MAP_MARKER_ICON = `
// the map, where a hairline stroke disappears — and rasterized/used as SDF // the map, where a hairline stroke disappears — and rasterized/used as SDF
// map icons (see gpx.js), so only the alpha shape matters, not the actual // map icons (see gpx.js), so only the alpha shape matters, not the actual
// `currentColor` value. // `currentColor` value.
// src/standalone/bicycle-trail-black-22.svg — pulled out to its own constant
// since it's also used unrasterized, inline, as PARK4NIGHT_TAGS.vtt below.
const BICYCLE_TRAIL_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor">
<circle cx="13.9" cy="3" r="2"/>
<path d="M4.5,20C6.4,20,8,18.4,8,16.5S6.4,13,4.5,13S1,14.6,1,16.5S2.6,20,4.5,20z M0,16.5C0,14,2,12,4.5,12S9,14,9,16.5
S7,21,4.5,21S0,19,0,16.5z"/>
<path d="M17.5,20c1.9,0,3.5-1.6,3.5-3.5S19.4,13,17.5,13S14,14.6,14,16.5S15.6,20,17.5,20z M13,16.5c0-2.5,2-4.5,4.5-4.5
s4.5,2,4.5,4.5S20,21,17.5,21S13,19,13,16.5z"/>
<path d="M13.1,9.8c-0.2,0-0.5-0.1-0.7-0.4l-1.1-1.7L9.1,9.9l2.1,1.1c0.9,0.4,0.9,1,0.9,1v5c0,0.5-0.6,0.9-1.2,0.9S10,17.6,10,17v-4
l-3.8-1.9c-0.4-0.3-0.7-0.7-0.7-1.2C5.6,9.5,5.7,9.2,6,9l3.5-3.5C9.9,4.9,10.6,5,10.6,5c0.7,0,1.1,0.6,1.1,0.6l1.8,2.9h2.8
c1,0,1,1.3,0,1.3H13.1L13.1,9.8z"/>
</svg>`;
export const POI_ICONS = { export const POI_ICONS = {
// src/standalone/parking-black-22.svg // src/standalone/parking-black-22.svg
amenity_parking: ` amenity_parking: `
@@ -131,17 +146,112 @@ export const POI_ICONS = {
</svg>`, </svg>`,
// src/standalone/bicycle-trail-black-22.svg // src/standalone/bicycle-trail-black-22.svg
sport_cycling: ` sport_cycling: BICYCLE_TRAIL_ICON,
<svg viewBox="0 0 22 22" fill="currentColor"> };
<circle cx="13.9" cy="3" r="2"/>
<path d="M4.5,20C6.4,20,8,18.4,8,16.5S6.4,13,4.5,13S1,14.6,1,16.5S2.6,20,4.5,20z M0,16.5C0,14,2,12,4.5,12S9,14,9,16.5 // National Park Service map symbols (same source/license as POI_ICONS above)
S7,21,4.5,21S0,19,0,16.5z"/> // used as small inline glyphs next to a park4night waypoint's Services/
<path d="M17.5,20c1.9,0,3.5-1.6,3.5-3.5S19.4,13,17.5,13S14,14.6,14,16.5S15.6,20,17.5,20z M13,16.5c0-2.5,2-4.5,4.5-4.5 // Activities tags (see PARK4NIGHT_TAGS and formatDescription in
s4.5,2,4.5,4.5S20,21,17.5,21S13,19,13,16.5z"/> // public/js/gpx.js) — pulled into named constants first since a few codes
<path d="M13.1,9.8c-0.2,0-0.5-0.1-0.7-0.4l-1.1-1.7L9.1,9.9l2.1,1.1c0.9,0.4,0.9,1,0.9,1v5c0,0.5-0.6,0.9-1.2,0.9S10,17.6,10,17v-4 // share the same real-world facility (eau_noire/eau_usee are both a dump
l-3.8-1.9c-0.4-0.3-0.7-0.7-0.7-1.2C5.6,9.5,5.7,9.2,6,9l3.5-3.5C9.9,4.9,10.6,5,10.6,5c0.7,0,1.1,0.6,1.1,0.6l1.8,2.9h2.8 // station, gaz/gpl are both a propane refill, peche/peche_pied are both
c1,0,1,1.3,0,1.3H13.1L13.1,9.8z"/> // fishing) and BICYCLE_TRAIL_ICON is also POI_ICONS.sport_cycling above.
</svg>`, const PETS_ON_LEASH_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><polygon points="3,12 3,21 4,21 4,22 5,22 5,21 6,21 6,18 9,22 10,22 11,20 8,16 14,16 14,21 15,21 15,22 16,22 16,21 17,21 17,11 13,9 3,9 2,8 1,9 "/><polygon points="13,7 13,1 14,1 14,2 22,6 22,8 17,9 "/><polygon points="0,1 0,2.5 13,8.5 13,7 "/></g></svg>`;
const SHOWERS_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M4.9,12.8c-0.1,0.4,0.2,0.7,0.6,0.8s0.7-0.2,0.8-0.6c0.1-0.4-0.2-0.7-0.6-0.8C5.3,12.1,4.9,12.4,4.9,12.8z M5.5,11.2 c0.1-0.4-0.2-0.7-0.5-0.7S4.3,10.7,4.2,11c-0.1,0.4,0.2,0.7,0.5,0.7C5.1,11.8,5.4,11.6,5.5,11.2z M4.1,8.8c0.3,0,0.6,0.3,0.6,0.6 S4.4,10,4.1,10S3.5,9.7,3.5,9.4S3.8,8.8,4.1,8.8z M3.4,7C3.7,7,4,7.3,4,7.6S3.7,8.2,3.4,8.2S2.8,7.9,2.8,7.6S3.1,7,3.4,7z M2.7,5.4 c0.3,0,0.5,0.2,0.5,0.5S3,6.4,2.7,6.4S2.2,6.2,2.2,5.9S2.4,5.4,2.7,5.4z M2,3.6c0.3,0,0.5,0.2,0.5,0.5S2.3,4.6,2,4.6 S1.5,4.4,1.5,4.1S1.7,3.6,2,3.6z M7.4,10.3c0-0.4,0.3-0.7,0.7-0.6c0.4,0,0.7,0.3,0.6,0.7c0,0.4-0.3,0.7-0.7,0.6 C7.7,11,7.4,10.7,7.4,10.3z M7,8.3c0.3,0,0.6,0.3,0.6,0.6S7.3,9.5,7,9.5S6.4,9.2,6.4,8.9S6.7,8.3,7,8.3z M6,6.9 c0.3,0,0.6,0.3,0.6,0.6S6.3,8.1,6,8.1S5.4,7.8,5.4,7.5S5.7,6.9,6,6.9z M4.9,5.5c0.3,0,0.6,0.3,0.6,0.6S5.2,6.7,4.9,6.7 S4.3,6.4,4.3,6.1S4.6,5.5,4.9,5.5z M3.9,4.2c0.3,0,0.5,0.2,0.5,0.5S4.2,5.2,3.9,5.2S3.4,5,3.4,4.7S3.6,4.2,3.9,4.2z M2.8,2.8 c0.3,0,0.5,0.2,0.5,0.5S3.1,3.8,2.8,3.8S2.3,3.6,2.3,3.3S2.5,2.8,2.8,2.8z M9.3,7.4c-0.4,0-0.7-0.3-0.7-0.6c0-0.4,0.3-0.7,0.6-0.7 c0.4,0,0.7,0.3,0.7,0.6C9.9,7.1,9.6,7.4,9.3,7.4z M8.5,5.7c0,0.3-0.2,0.6-0.6,0.7S7.2,6.1,7.2,5.7S7.5,5.1,7.8,5 C8.1,5,8.4,5.3,8.5,5.7z M7,4.6c0,0.3-0.2,0.6-0.6,0.6C6.1,5.2,5.8,5,5.8,4.6C5.8,4.3,6.1,4,6.4,4C6.7,4,7,4.3,7,4.6z M5,3 c0.3,0,0.6,0.3,0.6,0.6S5.3,4.2,5,4.2S4.4,3.9,4.4,3.6S4.7,3,5,3z M3.6,2c0.3,0,0.5,0.2,0.5,0.5S3.9,3,3.6,3S3.1,2.8,3.1,2.5 S3.3,2,3.6,2z M4.4,1.2c0.3,0,0.5,0.2,0.5,0.5S4.7,2.2,4.4,2.2S3.9,2,3.9,1.7S4.1,1.2,4.4,1.2z M6.3,1.9c0.3,0,0.5,0.2,0.5,0.5 S6.6,2.9,6.3,2.9S5.8,2.7,5.8,2.4S6,1.9,6.3,1.9z M8.1,2.6c0.3,0,0.6,0.3,0.6,0.6S8.4,3.8,8.1,3.8c-0.3,0-0.6-0.3-0.6-0.6 S7.8,2.6,8.1,2.6z M9.9,3.3c0.3,0,0.6,0.3,0.6,0.6s-0.3,0.6-0.6,0.6S9.3,4.2,9.3,3.9S9.6,3.3,9.9,3.3z M11.7,4.1 c0.3,0,0.6,0.3,0.6,0.6S12,5.3,11.7,5.3S11.1,5,11.1,4.7S11.4,4.1,11.7,4.1z M0,1h1V0h4v1H4v1H3v1H2v1H1v1H0V1z M11.5,7 C12.9,7,14,8.1,14,9.5S12.9,12,11.5,12S9,10.9,9,9.5S10.1,7,11.5,7z M14,13v-1h1v-1h1v-1h1V9h1V8h1V7h1V6h1l1,1v1v1h-1v1h-1v1h-1v1 h-1v1h-1v1h-1v8H9v-4H5v-2h1v-1h1v-1h1v-1H14z M10,16h1v2h-1v1h2v-4h-2V16z"/></svg>`;
const SANITARY_DISPOSAL_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><path d="M20,10V2h-1V1H4v1H3v8h1v1h5V9h1V8h3v1h1v2h8v-1H20z M11,5H6L5,4l1-1h5l1,1L11,5z M17,5h-2l-1-1l1-1h2l1,1L17,5z"/><path d="M10,9h3v3h-3V9z M11,11h1v-1h-1V11z"/><polygon points="8,13 21,13 22,14 22,19 21,20 1,20 0,19 0,14 1,13 4,13 4,14 2,14 1,15 1,18 2,19 10,19 11,18 11,15 10,14 8,14 "/><polygon points="5.5,12 6.5,12 6.5,16 8,16 6,18 4,16 5.5,16 "/></g></svg>`;
const ELECTRICAL_HOOKUP_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M16,6.3V1.1C16,0.5,15.2,0,14.5,0C13.8,0,13,0.5,13,1.1v5.2H8.8V1.1C8.8,0.5,8.2,0,7.5,0C6.8,0,6,0.5,6,1.1v5.2H3.5 c0,0,0,1.4,0,2.2c0,3.1,2.5,5.7,5.7,6.4l0.3,0V22h2.9v-7.1l0.1,0c3.4-0.7,5.9-3.2,5.9-6.4c0-0.8,0-2.2,0-2.2H16z"/></svg>`;
const PROPANE_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M15,4V0H7v4C4.9,4.4,3,6.3,3,8.4v7c0,2.2,1.6,4,3.7,4.4V22h8.6v-2.2c2.1-0.4,3.7-2.2,3.7-4.4v-7C19,6.3,17.1,4.4,15,4z M12,1h2v3h-2V1z M8,1h2v3H8V1z M13,13.6c-0.5,0.8-2.2,1.7-1.7,3.5h-1.1c-0.8-0.9-3.5-3.9-1.3-6c2.2-2,2.5-3.7,1.7-4.5 C10.7,6.6,16.2,8.7,13,13.6z"/></svg>`;
const LAUNDRY_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M3,0v4h16V0H3z M6,3H4V1h2V3z M10,3H8V1h2V3z"/><path d="M3,5v17h16V5H3z M11,19c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S14.3,19,11,19z"/><path d="M6.5,11c-0.6-0.4-0.4,1.3-0.4,2c0,2.7,2.2,4.9,4.9,4.9s4.9-2.2,4.9-4.9c0-0.7,0.1-1.6-0.4-2C10.2,6.7,11.9,14.7,6.5,11z"/></svg>`;
const SWIMMING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><polygon points="0,16 1,16 3,17 4,17 6,16 7,16 9,17 10,17 12,16 13,16 15,17 16,17 18,16 19,16 21,17 22,17 22,19 21,19 19,18 18,18 16,19 15,19 13,18 12,18 10,19 9,19 7,18 6,18 4,19 3,19 1,18 0,18 "/><polygon points="6,14 7,14 9,15 10,15 12,14 13,14 15,15 16,15 17.5,14.2 13,7 18,6 20,5 20,4 18,3 10,4 9,5 9,6 11,10 3,15 4,15 "/><circle cx="19.5" cy="10.5" r="2.5"/></g></svg>`;
const DRINKING_WATER_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><polygon points="20,0 18,0 15,20 7,20 4,0 2,0 5.5,22 16.5,22 "/><path d="M15.9,2c-0.4,0.4-0.9,1-1.4,1c-0.7,0-1.3-0.8-1.7-1.4C12.5,2.2,11.7,3,11,3c-0.7,0-1.2-0.8-1.5-1.4C9.1,2.2,8.7,3,8,3 C7.5,3,6.8,2.3,6.4,2L6.2,0.1C6.5,0.6,7.3,1,8,1c0.7,0,1.1-0.4,1.5-1c0.4,0.5,0.8,1,1.5,1c0.7,0,1.5-0.4,1.8-1c0.4,0.5,1,1,1.7,1 c0.7,0,1.3-0.5,1.7-1L15.9,2z"/></svg>`;
const TRASH_DUMPSTER_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M13,7h-2c-1.1,0-1.1,2,0,2h3c0.2,0,1-2,1-2v14c0,0.5,0.5,1,1,1s1-0.4,1-1v-8h1v8c0,0.5,0.5,1,1,1s1-0.4,1-1V7h1v5 c0,1.1,0.5,1.1,1,0V6c0-1.2-1.6-1-3-1h-3c-0.8,0-1.6,0.4-2,1L13,7z M3,16c1.6,0,3,1.4,3,3v3h2l3.5-8.9v-2.9H1.2L1,16H3z M9.6,5.8 L9.6,5.8L9.2,6L9,5.6l0,0L8.6,5.8L8.4,5.4l0,0L8,5.6L7.8,5.2l0,0L7.4,5.4L7.2,5l0,0L6.8,5.2L6.6,4.7L5.4,8.2 C5.3,8.5,5.5,8.8,5.8,8.9l2,0.7C8,9.7,8.3,9.5,8.4,9.3L9.6,5.8z M7.9,2.3L7.3,1.8L0.7,9.7l0.5,0.5L7.9,2.3z M18,0c1.1,0,2,0.9,2,2 s-0.9,2-2,2s-2-0.9-2-2S16.9,0,18,0z M2.5,17C1.1,17,0,18.1,0,19.5S1.1,22,2.5,22S5,20.9,5,19.5S3.9,17,2.5,17z M2.5,21 C1.7,21,1,20.3,1,19.5S1.7,18,2.5,18S4,18.7,4,19.5S3.3,21,2.5,21z"/></svg>`;
const RESTROOMS_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><rect x="11" y="2" width="2" height="18"/><polygon points="21,7 21,6 20,6 19,6 18,6 17,6 16,6 16,7 15,7 15,8 15,13 16,13 16,8 17,8 17,20 18,20 18,14 19,14 19,20 20,20 20,8 21,8 21,13 22,13 22,8 22,7 "/><polygon points="8,7 6,6 3,6 1,7 1,8 0,12 1,12 2,8 3,8 3,9 1,15 3,15 3,20 4,20 4,15 5,15 5,20 6,20 6,15 8,15 6,9 6,8 7,8 8,12 9,12 8,8 "/><circle cx="4.5" cy="3.5" r="1.5"/><circle cx="18.5" cy="3.5" r="1.5"/></g></svg>`;
const RIVER_RAFTING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M20.4,17.4c-0.5,0.3-0.9,0.8-1.6,0.8c-0.7,0-1.1-0.5-1.6-0.8c-0.4-0.3-1-0.8-1.6-0.8c-0.6,0-1.2,0.4-1.6,0.8 c-0.3,0.3-0.9,0.8-1.6,0.8c-0.7,0-1.2-0.5-1.6-0.8c-0.4-0.3-0.8-0.8-1.6-0.8c-0.7,0-1.2,0.5-1.6,0.8c-0.4,0.3-0.8,0.8-1.6,0.8 c-0.7,0-1.2-0.5-1.6-0.8c-0.4-0.2-0.7-0.8-1.6-0.8c-0.8,0-1.2,0.5-1.6,0.8C1.2,17.7,0,18.2,0,18.2v1.6v1.6c0,0,1.2-0.5,1.6-0.8 c0.4-0.3,0.8-0.8,1.6-0.8c0.9,0,1.2,0.5,1.6,0.8c0.4,0.3,0.9,0.8,1.6,0.8c0.8,0,1.2-0.5,1.6-0.8c0.4-0.3,0.8-0.8,1.6-0.8 c0.8,0,1.2,0.5,1.6,0.8c0.4,0.3,0.9,0.8,1.6,0.8c0.7,0,1.2-0.5,1.6-0.8c0.4-0.3,1-0.8,1.6-0.8c0.6,0,1.2,0.5,1.6,0.8 c0.5,0.3,0.9,0.8,1.6,0.8c0.6,0,1-0.5,1.6-0.8c0.5-0.3,1.6-0.8,1.6-0.8v-1.6v-1.6C22,16.6,21,17.1,20.4,17.4z"/><path d="M3.1,15c0.9,0,1.2,0.5,1.6,0.8c0.4,0.3,0.9,0.8,1.6,0.8c0.8,0,1.2-0.5,1.6-0.8C8.2,15.6,8.7,15,9.4,15 c0.8,0,1.2,0.5,1.6,0.8c0.4,0.3,0.9,0.8,1.6,0.8c0.7,0,1.2-0.5,1.6-0.8c0.4-0.3,1-0.8,1.6-0.8c0.6,0,1.2,0.5,1.6,0.8 c0.1,0.1,0.2,0.2,0.3,0.2l1.4-0.4c0,0,3,0,3-2.9c0-0.1,0-0.2,0-0.3c-1.9,1.3-6.8,1.6-7.4,1.6H0v2.6c0,0,1.2-0.5,1.6-0.8 C2,15.5,2.3,15,3.1,15z"/><path d="M11.1,2.2l-0.2,0.5C10.7,3,10.4,3.2,10,3.2H9.4C9.8,3.7,10.3,4,10.9,4c1,0,1.8-0.8,1.8-1.8V2.1L11.1,2.2 C11.1,2.1,11.1,2.1,11.1,2.2z"/><path d="M10,2.8c0.2,0,0.4-0.1,0.4-0.3L10.7,2c0.1-0.2,0.3-0.3,0.5-0.3H13c-0.2-1-1.1-1.7-2.1-1.7C9.7,0,8.7,1,8.7,2.2 c0,0.2,0,0.4,0.1,0.6H10z"/><g><path d="M9.4,10C9,10,8.7,9.7,8.6,9.3c0-0.4,0.3-0.8,0.7-0.8L15,8.2c0.5,0,0.8,0.3,0.8,0.7c0,0.4-0.3,0.8-0.7,0.8L9.4,10 C9.5,10,9.4,10,9.4,10z"/></g><path d="M17.2,10.9c-0.3,0.1-0.8,0.3-1.3,0.4l-0.7,0.1c-2,0.3-4.6,0.3-5.8,0.3c0,0,0,0,0,0L9.8,10l0,0l0.6-1.9c0,0,0,0,0,0L10.7,7 L11,6.1l3.4-0.2c0.4,0,0.8-0.4,0.7-0.8c0-0.4-0.3-0.8-0.8-0.7L8.7,4.6c-0.4,0-2.1,3.3-2.1,3.3l-1.2,3.7H0v1.8h14.5 c1.7-0.1,6.1-0.5,7.3-1.6C20.4,10.3,19.2,10,17.2,10.9z"/><g><path d="M15.9,14.6c-0.2,0-0.4-0.1-0.4-0.3L14,4.9c0-0.2,0.1-0.4,0.3-0.5c0.2,0,0.4,0.1,0.5,0.3l1.6,9.4 C16.4,14.4,16.2,14.6,15.9,14.6C16,14.6,16,14.6,15.9,14.6z"/></g></svg>`;
const CLIMBING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M2.5,9.4c-0.1,0.2-0.2,0.4-0.2,0.7c0,0.1,0,0.2,0,0.4L2.4,16l-1.9,4.1c-0.1,0.1-0.1,0.3-0.1,0.4c-0.1,0.7,0.4,1.3,1,1.4 c0.5,0.1,1-0.2,1.3-0.6l2.1-4.5c0-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.2l0-3.4l3.2,1.4l0.5,3.4c0.1,0.5,0.5,0.9,1,1 c0.7,0.1,1.3-0.4,1.4-1c0-0.1,0-0.2,0-0.3l-0.6-4.1c-0.1-0.4-0.3-0.7-0.7-0.9L6.8,11l1.8-3.2l0.9,1.2C9.7,9,9.8,9.1,10,9.2l3.5,1 c0.5,0.1,0.9-0.1,1.1-0.6c0.2-0.5,0-1.1-0.5-1.3c0,0,0,0,0,0l-3-0.9L8.7,4.7C8.3,4.3,7.9,4.1,7.3,4C6.4,3.8,5.5,4.3,5.1,5L2.5,9.4z" /><path d="M12.5,17.4l-0.2,1.7l-6,1.5l0.1,1H21c0.5,0,0.9-0.4,0.9-0.9l0-19.7l-2-0.5l-1.2,5.2l-1.9,0.8l-1.7,4.8l1.6,3.7l-0.5,1.4 L12.5,17.4z"/><ellipse transform="matrix(0.9871 -0.1602 0.1602 0.9871 -0.1902 1.3795)" cx="8.5" cy="1.9" rx="1.9" ry="1.9"/><path d="M4.7,4c0.1-0.3,0.1-0.6-0.2-0.7L3.6,2.8C3.4,2.6,3.1,2.7,2.9,3L0.1,7.8C0,8,0.1,8.4,0.3,8.5L1.2,9C1.5,9.2,1.8,9.1,2,8.8 L4.7,4z"/></svg>`;
const PLAYGROUND_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M4.4,11.7c1.2,0,2.2-1,2.2-2.2c0-1.2-1-2.2-2.2-2.2c-1.2,0-2.2,1-2.2,2.2 C2.2,10.8,3.2,11.7,4.4,11.7z M17,15.4c0.1-0.1,0.2,0,0.3,0l0.4,0.6c0.1,0.1,0,0.2-0.1,0.3l-3,2.1c-0.1,0.1-0.2,0-0.3,0l-0.4-0.6 c-0.1-0.1,0-0.2,0.1-0.3L17,15.4z M13.5,12.7l-1.2-2l-0.6,0.4l1.2,2L13.5,12.7z M10.7,7.9L6,0L5.1,0l4.9,8.4L10.7,7.9z M15.1,12.1 c0.3-0.2,0.6-0.3,0.9-0.3l4.3,0c0.8,0,1.5,0.7,1.5,1.5c0,0.8-0.7,1.5-1.5,1.5l-3.6,0l-4.6,3.2c-1.6,1.1-2.6-0.1-2.6-0.1l-3.6-3.5 c-0.6-0.7-0.5-2,0.3-2.6l5-3.5c0.5-0.4,1.2-0.3,1.7,0.1c0.4,0.5,0.4,1.2-0.1,1.6l-3.2,2.3l2.3,2.2L15.1,12.1z"/></svg>`;
const MOTOR_BIKE_TRAIL_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><path d="M12.1,16.9c-0.3,0.3-0.7,0.5-1.1,0.5V12l4-1v2L12.1,16.9z"/><path d="M9,5H8V4.1c0,0-0.9,0.2-1.2,0.5l-3,4.6C3.7,9.3,3.7,9.6,3.7,9.8c0,0.5,0.2,0.9,0.6,1.1l4,2.2V17c0,0.5,0.4,1,1,1 c0.5,0,0.8-0.4,0.8-1v-4.7c0,0,0.3-0.8-0.5-1.2L7,9.8L8.8,7l1,1.5C10,8.8,10.2,9,10.5,9H13c1,0,1-1.4,0-1.4h-2l-2-3V5z"/><path d="M19.9,10.7l0.1,0.2c0,0.1-0.1,0.2-0.2,0.2h-0.1c-0.4-0.2-0.7-0.3-1.2-0.3c-0.3,0-0.7,0.1-1.2,0.5l-0.4-0.9 c0.5-0.2,1-0.4,1.5-0.4C19,10,19.4,10.3,19.9,10.7z"/><path d="M3.2,11.4L7,13.7v0.9L2.6,12c-0.3-0.2-0.8-0.3-1.1-0.3c-0.5,0-0.3,0.1-0.7,0.3H0.7c-0.1,0-0.2-0.1-0.2-0.2l0.1-0.2 C1.1,11.2,1,11,1.8,11C2.2,11,2.6,11.1,3.2,11.4L3.2,11.4z"/><path d="M3.5,19.9c1.3,0,2.4-1.1,2.4-2.5s-1.1-2.5-2.4-2.5S1.1,16,1.1,17.4S2.2,19.9,3.5,19.9z M0,17.4c0-2,1.6-3.6,3.5-3.6 S7,15.4,7,17.4S5.4,21,3.5,21S0,19.4,0,17.4z"/><path d="M18.4,19.9c1.4,0,2.5-1.1,2.5-2.5s-1.1-2.5-2.5-2.5s-2.5,1.1-2.5,2.5S17.1,19.9,18.4,19.9z M14.8,17.4c0-2,1.6-3.6,3.6-3.6 s3.6,1.6,3.6,3.6S20.4,21,18.4,21S14.8,19.4,14.8,17.4z"/><path d="M17.6,13L16,9.8C15.8,9.4,15.2,9,14.7,9h-1.1c-0.2,0-0.4,0.3-0.4,0.6s0.2,0.4,0.4,0.4h1.1c0.2,0,0.4,0.1,0.5,0.3l1.6,2.7 H17.6z"/><path d="M12,3h0.9C13,2.8,13,2.7,13,2.5C13,1.1,11.9,0,10.5,0S8,1.1,8,2.5C8,3.1,8.2,3.6,8.5,4H12V3z"/><circle cx="10.5" cy="3.5" r="1.5"/></g></svg>`;
const FISHING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g><path d="M6.1,13.6L6,13.8L6.1,14l3.9,1c0.2,0.1,0.2,1,0,1H6.1L6,16.1v0.1c4.3,3.4,9.2,4.8,15,4.8V9C11,9,6.1,13.6,6.1,13.6z M13,14 h-1v-1h1V14z"/><path d="M19,1c-3.3,0-5.7,1.5-9.3,4.5c-0.1,0-0.4-0.1-0.4-0.1c-0.6,0-1,0.5-1,1c0,0,0,0.2,0.1,0.3C8.1,6.8,5.3,9.5,5.3,9.5L3,12 c-0.2,0.2-0.7,0-1,0s-0.6,0-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8S1,10.3,1.3,10c0,0,0.2-0.1,0.3-0.2c0,0.2,0.1,0.9,0.1,0.9L4,8 L1.5,8.9C0.9,9.1,0.7,9.4,0.6,9.4c-0.8,0.7-0.9,1.8-0.1,2.9C2,12.9,1.4,12.8,3,13l1-1l1.8-1.8c0,0,2.9-2.8,3-2.8 c0.1,0,0.4,0.1,0.4,0.1c0.6,0,1-0.5,1-1c0,0,0-0.2,0-0.3C13.7,3.3,15.9,2,19,2c0.6,0,3,0,3,0V1H19z"/></g></svg>`;
const SCENIC_VIEWPOINT_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><g transform="translate(4.000000, 0.000000)"><path d="M3.2,11c0,1.2-0.9,2.2-2.1,2.2c-1.2,0-2.1-1-2.1-2.2 c0-1.2,0.9-2.2,2.1-2.2C2.2,8.8,3.2,9.8,3.2,11"/><path d="M4.6,10.4v1.3l10.6,0.8v-3L4.6,10.4"/><path d="M2.3,7.5l1.1,0.7L7.7,2L5.3,0.5L2.3,7.5"/><path d="M4.4,9.8l7.9-3.8l-1.4-2.6L3.8,8.6L4.4,9.8z"/><path d="M2.3,14.5l1.1-0.7l4.3,6.2l-2.4,1.5L2.3,14.5"/><path d="M3.8,13.4l0.6-1.2l7.9,3.8l-1.4,2.6L3.8,13.4"/></g></svg>`;
const WALKING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><path d="M10.4,3.6c1,0,1.8-0.8,1.7-1.8c0-1-0.8-1.8-1.8-1.7c-1,0-1.8,0.8-1.7,1.8C8.6,2.8,9.5,3.6,10.4,3.6z"/><path d="M16.5,9.5L13.9,8l-2.2-3.4c-0.3-0.4-0.9-0.8-1.5-0.7C9.7,4,9.3,4.2,9,4.4L5.8,7.7C5.7,7.8,5.6,8,5.6,8.1l-0.4,3.6 c0,0,0,0.1,0,0.1c0,0.4,0.4,0.8,0.8,0.8c0.4,0,0.7-0.4,0.8-0.8l0.3-3.1l1.1-1.1l-1,8.6l-1.9,4.1c-0.1,0.1-0.1,0.3-0.1,0.5 c0,0.7,0.6,1.2,1.2,1.2c0.5,0,0.9-0.3,1.1-0.7l2-4.4c0-0.1,0.2-0.4,0.2-0.5l0.2-2.1l2.1,6.9c0.2,0.5,0.6,0.8,1.2,0.8 c0.7,0,1.2-0.6,1.2-1.2c0-0.1,0-0.1,0-0.2L11.5,11l0.4-3.1l0.8,1.3c0.1,0.1,0.1,0.2,0.2,0.2l2.8,1.6c0.1,0,0.2,0.1,0.4,0.1 c0.4,0,0.8-0.4,0.8-0.8C16.9,9.8,16.7,9.6,16.5,9.5z"/></svg>`;
const GUIDED_TOURS_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><polygon points="19,7 19,6 14,6 14,7 13,7 13,8 13,13 14,13 14,8 15,8 15,20 16,20 16,14 17,14 17,20 18,20 18,8 19,8 20,8 20,7 "/><circle cx="16.5" cy="3.5" r="1.5"/><polygon points="4,7 4,6 1,6 1,7 0,7 0,8 0,13 1,13 1,20 2,20 2,14 3,14 3,20 4,20 4,13 5,13 5,7 "/><circle cx="2.5" cy="3.5" r="1.5"/><polygon points="10,7 10,6 7,6 7,7 6,7 6,8 6,13 7,13 7,20 8,20 8,14 9,14 9,20 10,20 10,13 11,13 11,7 "/><circle cx="8.5" cy="3.5" r="1.5"/><rect x="21" y="4" width="1" height="4"/><rect x="19" y="7" width="2" height="1"/></svg>`;
const WIND_SURFING_ICON = `
<svg viewBox="0 0 22 22" fill="currentColor"><polygon points="21,20 19,19 18,19 16,20 15,20 13,19 12,19 10,20 9,20 8,20 8,19.5 7,19 6,19 4,20 3,20 1,19 1,21 3,22 4,22 6,21 7,21 9,22 10,22 12,21 13,21 15,22 16,22 18,21 19,21 21,22 22,22 22,20 "/><path d="M10.7,17.5l-2.5-2.4C8.1,15,7.9,15,7.7,15H5l-0.4-1H4v-1h0.2L4,12.4h1.4c0,0,0.3,0,0.4-0.1l2.3-1c0.2-0.1,0.3-0.3,0.3-0.5 c0-0.4-0.3-0.7-0.6-0.7c-0.1,0-0.2,0-0.2,0l-2.1,1h-3c-0.1,0-0.2,0-0.4,0.1V12h-1C1,12.1,1,12.2,1,12.4c0,0.1,0,0.2,0,0.3l1.4,3.2 c0.2,0.5,0.7,0.8,1.2,0.8h3.7l2.2,2.1c0.3,0.3,0.9,0.3,1.2,0C11.1,18.4,11.1,17.8,10.7,17.5z"/><path d="M7,0l7.1,18l7.9-4.9C21.5,5.7,13.4,0.8,7,0z M18,10c-1.5,0-6,0-6,0V9c0,0,4.5,0,6,0c1.8,0,3,2,3,2v1h-1C20,12,18.8,10,18,10 z"/><circle cx="1.5" cy="9.5" r="1.5"/></svg>`;
// park4night's fixed vocabulary of Services/Activities codes (the ones
// p4n2gpx exports actually use — not necessarily park4night's entire API),
// each with a human label (always shown) and an icon where the NPS set has
// a reasonable match. formatDescription (public/js/gpx.js) reads this;
// an unrecognized code just renders as its raw text, same as before this
// existed, so nothing p4n2gpx might add later is ever hidden.
export const PARK4NIGHT_TAGS = {
// Services
animaux: { icon: PETS_ON_LEASH_ICON, label: "Pets allowed" },
boulangerie: { label: "Bakery nearby" },
caravaneige: { label: "Winter/snow camping" },
donnees_mobile: { label: "Mobile data coverage" },
douche: { icon: SHOWERS_ICON, label: "Showers" },
eau_noire: { icon: SANITARY_DISPOSAL_ICON, label: "Black water disposal" },
eau_usee: { icon: SANITARY_DISPOSAL_ICON, label: "Grey water disposal" },
electricite: { icon: ELECTRICAL_HOOKUP_ICON, label: "Electricity" },
gaz: { icon: PROPANE_ICON, label: "Gas/propane refill" },
gpl: { icon: PROPANE_ICON, label: "LPG refill" },
lavage: { label: "Vehicle wash" },
laverie: { icon: LAUNDRY_ICON, label: "Laundry" },
piscine: { icon: SWIMMING_ICON, label: "Swimming pool" },
point_eau: { icon: DRINKING_WATER_ICON, label: "Drinking water" },
poubelle: { icon: TRASH_DUMPSTER_ICON, label: "Trash disposal" },
wc_public: { icon: RESTROOMS_ICON, label: "Public toilet" },
wifi: { label: "Wi-Fi" },
// Activities
baignade: { icon: SWIMMING_ICON, label: "Swimming" },
eaux_vives: { icon: RIVER_RAFTING_ICON, label: "Whitewater sports" },
escalade: { icon: CLIMBING_ICON, label: "Climbing" },
jeux_enfants: { icon: PLAYGROUND_ICON, label: "Playground" },
moto: { icon: MOTOR_BIKE_TRAIL_ICON, label: "Motorcycling" },
peche: { icon: FISHING_ICON, label: "Fishing" },
peche_pied: { icon: FISHING_ICON, label: "Shore fishing" },
point_de_vue: { icon: SCENIC_VIEWPOINT_ICON, label: "Scenic viewpoint" },
rando: { icon: WALKING_ICON, label: "Hiking" },
visites: { icon: GUIDED_TOURS_ICON, label: "Sightseeing" },
vtt: { icon: BICYCLE_TRAIL_ICON, label: "Mountain biking" },
windsurf: { icon: WIND_SURFING_ICON, label: "Windsurfing" },
}; };
export const LIVETRACK_ICON = ` export const LIVETRACK_ICON = `