Add GPX Map shortcode and related assets for displaying GPX tracks
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 1m5s
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 1m5s
This commit is contained in:
52
layouts/shortcodes/gpx-map.html
Normal file
52
layouts/shortcodes/gpx-map.html
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<!--
|
||||||
|
Usage:
|
||||||
|
{{< gpx-map lat="47.123" lon="8.456" gpx="/uploads/gpx/my-track.gpx" zoom="12" height="600px" >}}
|
||||||
|
All parameters are optional except lat/lon and gpx.
|
||||||
|
-->
|
||||||
|
<div id="gpx-map-{{ .Get "lat" }}-{{ .Get "lon" }}-{{ .Get "zoom" | default "13" }}" style="height:{{ .Get "height" | default "500px" }};"></div>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.min.js"></script>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var mapId = "gpx-map-{{ .Get "lat" }}-{{ .Get "lon" }}-{{ .Get "zoom" | default "13" }}";
|
||||||
|
if (!window._gpx_maps) window._gpx_maps = {};
|
||||||
|
if (!window._gpx_maps[mapId]) {
|
||||||
|
var map = L.map(mapId).setView(
|
||||||
|
[{{ .Get "lat" }}, {{ .Get "lon" }}],
|
||||||
|
{{ .Get "zoom" | default "13" }}
|
||||||
|
);
|
||||||
|
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// Add a marker at the specified location
|
||||||
|
L.marker([{{ .Get "lat" }}, {{ .Get "lon" }}]).addTo(map);
|
||||||
|
|
||||||
|
// Load and display the GPX track
|
||||||
|
var gpxUrl = "{{ .Get "gpx" }}";
|
||||||
|
new L.GPX(gpxUrl, {
|
||||||
|
async: true,
|
||||||
|
marker_options: {
|
||||||
|
startIconUrl: '/images/pin-icon-start.png',
|
||||||
|
endIconUrl: '/images/pin-icon-end.png',
|
||||||
|
shadowUrl: '/images/pin-shadow.png'
|
||||||
|
},
|
||||||
|
polyline_options: {
|
||||||
|
color: '#3388ff',
|
||||||
|
weight: 5,
|
||||||
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
}).on('loaded', function(e) {
|
||||||
|
// Auto-fit the map to the track bounds if no specific lat/lon provided
|
||||||
|
if (!("{{ .Get "lat" }}" && "{{ .Get "lon" }}")) {
|
||||||
|
map.fitBounds(e.target.getBounds());
|
||||||
|
}
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
window._gpx_maps[mapId] = map;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -53,6 +53,16 @@ collections:
|
|||||||
media_library:
|
media_library:
|
||||||
config:
|
config:
|
||||||
multiple: true
|
multiple: true
|
||||||
|
- label: "GPX Files"
|
||||||
|
name: "gpxFiles"
|
||||||
|
widget: "file"
|
||||||
|
required: false
|
||||||
|
choose_url: true
|
||||||
|
media_library:
|
||||||
|
media_folder: "/static/uploads/gpx"
|
||||||
|
public_folder: "/images/uploads"
|
||||||
|
config:
|
||||||
|
multiple: true
|
||||||
- { label: "Body", name: "body", widget: "markdown" }
|
- { label: "Body", name: "body", widget: "markdown" }
|
||||||
########################
|
########################
|
||||||
# POSTS COLLECTION
|
# POSTS COLLECTION
|
||||||
|
|||||||
@@ -84,6 +84,44 @@
|
|||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register the GPX Map shortcode as an editor component
|
||||||
|
window.CMS.registerEditorComponent({
|
||||||
|
id: "gpx-map",
|
||||||
|
label: "GPX Track Map",
|
||||||
|
fields: [
|
||||||
|
{ name: "lat", label: "Latitude", widget: "string" },
|
||||||
|
{ name: "lon", label: "Longitude", widget: "string" },
|
||||||
|
{ name: "gpx", label: "GPX File Path", widget: "string", hint: "Example: /uploads/gpx/my-track.gpx" },
|
||||||
|
{ name: "zoom", label: "Zoom Level", widget: "number", default: 13, required: false },
|
||||||
|
{ name: "height", label: "Height (e.g. 500px)", widget: "string", default: "500px", required: false }
|
||||||
|
],
|
||||||
|
pattern: /{{<\s*gpx-map\s+lat="([^"]+)"\s+lon="([^"]+)"\s+gpx="([^"]+)"(?:\s+zoom="([^"]+)")?(?:\s+height="([^"]+)")?\s*>}}/,
|
||||||
|
fromBlock: function (match) {
|
||||||
|
return {
|
||||||
|
lat: match[1],
|
||||||
|
lon: match[2],
|
||||||
|
gpx: match[3],
|
||||||
|
zoom: match[4] || "13",
|
||||||
|
height: match[5] || "500px"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
toBlock: function (obj) {
|
||||||
|
let params = `lat="${obj.lat}" lon="${obj.lon}" gpx="${obj.gpx}"`;
|
||||||
|
if (obj.zoom) params += ` zoom="${obj.zoom}"`;
|
||||||
|
if (obj.height) params += ` height="${obj.height}"`;
|
||||||
|
return `{{< gpx-map ${params} >}}`;
|
||||||
|
},
|
||||||
|
toPreview: function (obj) {
|
||||||
|
// Simple preview (not interactive)
|
||||||
|
return `<div style="border:1px solid #ccc;padding:1em;">
|
||||||
|
<strong>GPX Track Map</strong><br>
|
||||||
|
Lat: ${obj.lat}, Lon: ${obj.lon}<br>
|
||||||
|
GPX: ${obj.gpx}<br>
|
||||||
|
Zoom: ${obj.zoom || 13}, Height: ${obj.height || "500px"}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
BIN
static/images/pin-icon-end.png
Normal file
BIN
static/images/pin-icon-end.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 960 B |
BIN
static/images/pin-icon-start.png
Normal file
BIN
static/images/pin-icon-start.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 971 B |
BIN
static/images/pin-shadow.png
Normal file
BIN
static/images/pin-shadow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user