OpenStreetMap Marker
Generates a self-contained, browser-previewable custom marker map HTML page styled after Amap's mobile app (light minimal basemap + circular colored icon + text label + immersive full-screen layout with safe-area support).
Core Workflow
- Get marker coordinates: prefer
apple-maps search --query "place name city" --lat <city lat> --lon <city lng> --limit 1 -q --compact, which returns JSON withlat/lon/coordinate_system. - Coordinate conversion (critical, do not skip): Apple Maps / Amap / Tencent / Baidu almost always return GCJ-02 coordinates in mainland China, while this skill's Leaflet+OSM/CartoDB basemap uses WGS-84. Using GCJ-02 directly causes markers to drift tens to hundreds of meters (often landing in a river or the sea). Coordinates within mainland China MUST be converted with
scripts/gcj_convert.pybefore writing tolandmarks.json; the script automatically skips conversion for points outside China (out_of_chinacheck).python3 /var/minis/skills/openstreetmap-marker/scripts/gcj_convert.py <lng> <lat> # outputs "wgs_lat,wgs_lng" - Assemble
landmarks.json: an array where each item is:{"name": "West Lake", "desc": "UNESCO World Heritage Site, Hangzhou's iconic landmark", "address": "Xihu District, Hangzhou", "lat": 30.2614, "lng": 120.1443, "type": "green", "icon": "fa-tree"}name/lat/lngare required;desc/address/type/iconare optional.typedetermines the marker dot color:red(government/medical/core attraction),green(park/nature),blue(transport/building),purple(commercial/venue),brown(heritage/university), ororange(other). Defaults toblue. Each type also has a sensible default Font Awesome icon (red→fa-star, green→fa-tree, blue→fa-building, purple→fa-bag-shopping, brown→fa-landmark, orange→fa-circle-dot). Leaveiconempty to use it.icon(optional): a Font Awesome 6 solid-style icon class name, e.g."fa-train","fa-utensils","fa-mosque","fa-museum","fa-mountain". Overrides the type default. Look up icon names at fontawesome.com/icons (free "solid" set). Do NOT use emoji; all icons are unified Font Awesome glyphs rendered via<i class="fa-solid ...">for a consistent cross-device look.- Write this JSON array to a temp file with
file_write, e.g./tmp/landmarks.json.
- Generate the page:
python3 /var/minis/skills/openstreetmap-marker/scripts/build_map.py \ /tmp/landmarks.json /var/minis/workspace/<descriptive-filename>.html \ --title "West Lake Area Landmarks" --header "⚡ Hangzhou Trip" \ --style light--style light(default, light minimal, Amap-like palette) or--style dark(dark immersive).- Without
--center/--zoom, the script automatically centers and zooms to fit all points with padding; a single point defaults to zoom 14.
- Deliver: present the result to the user as a Markdown link
[Preview](minis://workspace/<filename>.html), tappable to open in the built-in browser.
Built-in style features (already handled by the template, no need to re-explain)
- Full-screen immersive:
viewport-fit=cover+env(safe-area-inset-*)for notch/Dynamic Island devices. - Semi-transparent top status bar (live clock on the left, custom header text on the right).
- No bottom search bar / tab bar / side floating buttons (kept minimal per user preference: just map + markers).
- Markers are circular colored icons (Font Awesome, loaded via CDN
cdnjs.cloudflare.com/.../font-awesome/...) + white text label pills for a unified, non-emoji icon style. - Leaflet zoom control hidden by default (
zoomControl: false) for a clean UI. - Tapping a marker opens a Google Maps-style bottom info card (not Leaflet's default popup) showing: title (with FA icon), address (if provided, 📍-style row), coordinates (lat, lng), and
descas a note below a divider. No ratings/route/save/share buttons; kept intentionally minimal. A close (✕) button and tapping the basemap both dismiss it. - The info card has a single "Open Location in..." button, which slides up a bottom action sheet listing available map apps vertically (Amap / Google Maps / Apple Maps), each with an FA icon; a "Cancel" row closes the sheet. Available options are platform-aware:
- Amap (labeled "Amap" in the sheet UI): only shown if the point falls within mainland China (coarse lat/lng range check).
landmarks.jsonstores WGS-84; the frontend JS automatically converts WGS-84→GCJ-02 before building the Amap deep link. - Google Maps: always shown.
- Apple Maps: only shown on iOS (UA-detected); hidden on Android since it is not a relevant or installable app there.
- Platform is detected via
navigator.userAgent(isAndroid/isIOS). Each option first tries a native URL scheme / intent to directly launch the installed app even from inside an embedded WebView; plainhttps://links alone do NOT auto-launch native apps in an embedded webview (only a full browser like Safari/Chrome does that reliably). A shortsetTimeoutfallback then redirects to the equivalenthttps://web link if the scheme silently fails (app not installed):- Amap:
iosamap://(iOS) /amapuri://(Android) → fallbackhttps://uri.amap.com/marker?... - Google Maps:
comgooglemaps://(iOS) /geo:lat,lng?q=...(Android) → fallbackhttps://www.google.com/maps/search/?api=1&query=... - Apple Maps:
maps://→ fallbackhttps://maps.apple.com/?ll=...
- Amap:
- Amap (labeled "Amap" in the sheet UI): only shown if the point falls within mainland China (coarse lat/lng range check).
Tile provider note
Uses CARTO's free Voyager/Dark basemap tiles (no API key required). Fine for personal/demo use, but subject to CARTO's rate limits and non-commercial-scale terms. For high-traffic production use, switch TILE_STYLES in scripts/build_map.py to a licensed provider (Mapbox, Stadia Maps, etc.).
Common adjustments
- Change basemap: edit
TILE_STYLESinscripts/build_map.pyand add a new key, e.g. Esri World Imagery tile URL for satellite view. - Add routes/tracks: append
L.polyline([[lat,lng],...]).addTo(map)to the generated HTML; these coordinates also need GCJ-02→WGS-84 conversion first if in mainland China. - Multi-day itineraries: generate one HTML per day, or use different
typecolors to distinguish Day1/Day2/Day3.
Reference files
scripts/gcj_convert.py- GCJ-02→WGS-84 coordinate conversion (single-point CLI and batch JSON mode)scripts/build_map.py- map HTML generatorassets/template.html- page template with placeholders__PAGE_TITLE____HEADER_TEXT____LANDMARKS_JSON____CENTER_LAT____CENTER_LNG____ZOOM____TILE_URL__