Files
intotheeast-com-content/themes/intotheeast/js/maplibre-utils.js
T

140 lines
4.7 KiB
JavaScript

/* Shared MapLibre GL utilities — loaded by map.html.twig, dailies.html.twig, home.html.twig */
(function (global) {
var ACCENT = '#2A8C73';
var ACCENT_DIM = '#155244';
var MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';
/* Build a GeoJSON LineString feature */
function lineFeature(coords) {
return { type: 'Feature', properties: {}, geometry: { type: 'LineString', coordinates: coords } };
}
/*
* Catmull-Rom spline through waypoints → dense interpolated coords.
* Produces a smooth curve that passes through every entry dot.
* steps: interpolated points per segment (16 is plenty for daily entries).
*/
function catmullRomSpline(coords, steps) {
if (coords.length < 2) return coords;
steps = steps || 16;
var out = [];
for (var i = 0; i < coords.length - 1; i++) {
var p0 = coords[Math.max(i - 1, 0)];
var p1 = coords[i];
var p2 = coords[i + 1];
var p3 = coords[Math.min(i + 2, coords.length - 1)];
for (var s = 0; s < steps; s++) {
var t = s / steps;
var t2 = t * t;
var t3 = t2 * t;
out.push([
0.5 * ((2*p1[0]) + (-p0[0]+p2[0])*t + (2*p0[0]-5*p1[0]+4*p2[0]-p3[0])*t2 + (-p0[0]+3*p1[0]-3*p2[0]+p3[0])*t3),
0.5 * ((2*p1[1]) + (-p0[1]+p2[1])*t + (2*p0[1]-5*p1[1]+4*p2[1]-p3[1])*t2 + (-p0[1]+3*p1[1]-3*p2[1]+p3[1])*t3)
]);
}
}
out.push(coords[coords.length - 1]);
return out;
}
/*
* Progressively draw the journey line using a requestAnimationFrame loop.
* splineCoords: dense interpolated coords from catmullRomSpline().
*/
function animateJourneyLine(map, splineCoords, sourceId) {
if (splineCoords.length < 2) return;
var segDist = [0];
for (var i = 1; i < splineCoords.length; i++) {
var dx = splineCoords[i][0] - splineCoords[i - 1][0];
var dy = splineCoords[i][1] - splineCoords[i - 1][1];
segDist.push(segDist[i - 1] + Math.sqrt(dx * dx + dy * dy));
}
var totalDist = segDist[segDist.length - 1];
var DURATION = 5000;
var startTime = performance.now();
function frame(now) {
if (!map.getSource(sourceId)) return;
var t = Math.min((now - startTime) / DURATION, 1);
var eased = 1 - Math.pow(1 - t, 3);
var target = eased * totalDist;
var animCoords = [splineCoords[0]];
for (var j = 1; j < splineCoords.length; j++) {
if (segDist[j] <= target) {
animCoords.push(splineCoords[j]);
} else {
var frac = (target - segDist[j - 1]) / (segDist[j] - segDist[j - 1]);
animCoords.push([
splineCoords[j - 1][0] + (splineCoords[j][0] - splineCoords[j - 1][0]) * frac,
splineCoords[j - 1][1] + (splineCoords[j][1] - splineCoords[j - 1][1]) * frac
]);
break;
}
}
map.getSource(sourceId).setData(lineFeature(animCoords));
if (t < 1) requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
}
/*
* Add a journey line to a loaded map — dotted, subordinate style so GPX
* tracks read as the primary route where they exist.
* coords: [[lng, lat], ...] raw waypoints (daily entry positions).
*/
function addJourneyLine(map, coords, sourceId) {
if (coords.length < 2) return;
var splineCoords = catmullRomSpline(coords, 16);
map.addSource(sourceId, { type: 'geojson', data: lineFeature([splineCoords[0]]) });
map.addLayer({
id: sourceId + '-line', type: 'line', source: sourceId,
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': ACCENT,
'line-width': 2,
'line-opacity': 0.45,
'line-dasharray': [0, 2.5]
}
});
var reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reducedMotion) {
map.getSource(sourceId).setData(lineFeature(splineCoords));
} else {
animateJourneyLine(map, splineCoords, sourceId);
}
}
/*
* Return a styled <div> element for a map marker dot.
* isLatest: make it larger with a teal ring.
*/
function createDotMarker(isLatest) {
var el = document.createElement('div');
var size = isLatest ? 18 : 12;
var bg = isLatest ? ACCENT_DIM : ACCENT;
var ring = isLatest ? ',0 0 0 4px rgba(42,140,115,0.25)' : '';
el.style.cssText = [
'width:' + size + 'px',
'height:' + size + 'px',
'background:' + bg,
'border:2px solid #fff',
'border-radius:50%',
'box-shadow:0 1px 4px rgba(0,0,0,0.4)' + ring,
'cursor:pointer'
].join(';');
return el;
}
global.MapUtils = { MAP_STYLE: MAP_STYLE, ACCENT: ACCENT, addJourneyLine: addJourneyLine, createDotMarker: createDotMarker };
})(window);