208 lines
6.5 KiB
JavaScript
208 lines
6.5 KiB
JavaScript
(function () {
|
|
'use strict';
|
|
|
|
const container = document.getElementById('campground_map');
|
|
if (!container) {
|
|
console.warn('could not find map container');
|
|
}
|
|
|
|
const svg = container.childNodes[0];
|
|
svg.remove();
|
|
|
|
const latLngBounds = L.latLngBounds([[0, 0], [1192, 1500]]);
|
|
const map = L.map(container, {
|
|
center: latLngBounds.getCenter(),
|
|
minZoom: -2,
|
|
maxZoom: 2,
|
|
zoom: -1,
|
|
scrollWheelZoom: false,
|
|
//maxBounds: latLngBounds,
|
|
//maxBoundsViscosity: 1.0,
|
|
zoomSnap: 0,
|
|
crs: L.CRS.Simple,
|
|
});
|
|
map.fitBounds(latLngBounds);
|
|
L.svgOverlay(svg, latLngBounds).addTo(map);
|
|
|
|
// from https://github.com/jkroso/parse-svg-path/
|
|
const parse = (function () {
|
|
'use strict';
|
|
|
|
const length = {a: 7, c: 6, h: 1, l: 2, m: 2, q: 4, s: 4, t: 2, v: 1, z: 0}
|
|
const segment = /([astvzqmhlc])([^astvzqmhlc]*)/ig;
|
|
const number = /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig;
|
|
|
|
function parseValues(args) {
|
|
const numbers = args.match(number)
|
|
return numbers ? numbers.map(Number) : []
|
|
}
|
|
|
|
return function (path) {
|
|
const data = []
|
|
path.replace(segment, function (_, command, args) {
|
|
let type = command.toLowerCase()
|
|
args = parseValues(args)
|
|
|
|
// overloaded moveTo
|
|
if (type === 'm' && args.length > 2) {
|
|
data.push([command].concat(args.splice(0, 2)))
|
|
type = 'l'
|
|
command = command === 'm' ? 'l' : 'L'
|
|
}
|
|
|
|
while (true) {
|
|
if (args.length === length[type]) {
|
|
args.unshift(command)
|
|
return data.push(args)
|
|
}
|
|
if (args.length < length[type]) throw new Error('malformed path data')
|
|
data.push([command].concat(args.splice(0, length[type])))
|
|
}
|
|
})
|
|
return data
|
|
}
|
|
})();
|
|
|
|
function highlightAccommodation(e) {
|
|
const layer = e.target;
|
|
layer.setStyle({
|
|
color: '#ffe673',
|
|
});
|
|
}
|
|
|
|
function resetHighlight(e) {
|
|
const layer = e.target;
|
|
layer.setStyle({
|
|
color: 'transparent',
|
|
});
|
|
}
|
|
|
|
function getPath(el) {
|
|
if (!el) {
|
|
return el;
|
|
}
|
|
switch (el.localName) {
|
|
case 'path':
|
|
case 'rect':
|
|
return el;
|
|
case 'g':
|
|
return getPath(el.firstElementChild);
|
|
default:
|
|
console.warn('unhandled element type', el.localName);
|
|
return null
|
|
}
|
|
}
|
|
|
|
function getCTM(ctm, el) {
|
|
if (el.localName === 'svg') {
|
|
return ctm;
|
|
}
|
|
const transform = el.getAttribute('transform');
|
|
if (!transform) {
|
|
return getCTM(ctm, el.parentElement);
|
|
}
|
|
const matrix = new DOMMatrix(transform);
|
|
return getCTM(matrix.multiply(ctm), el.parentElement);
|
|
}
|
|
|
|
function convertToPoints(path) {
|
|
const ctm = getCTM(new DOMMatrix([1, 0, 0, 1, 0, 0]), path);
|
|
const points = [];
|
|
const p0 = new DOMPoint();
|
|
|
|
function setP0(x, y) {
|
|
p0.x = x;
|
|
p0.y = y;
|
|
const p1 = p0.matrixTransform(ctm);
|
|
p0.x = p1.x;
|
|
p0.y = latLngBounds.getNorth() - p1.y;
|
|
}
|
|
|
|
switch (path.localName) {
|
|
case 'rect':
|
|
const x = parseFloat(path.getAttribute('x'));
|
|
const y = parseFloat(path.getAttribute('y'));
|
|
const w = parseFloat(path.getAttribute('width'));
|
|
const h = parseFloat(path.getAttribute('height'));
|
|
setP0(x, y);
|
|
points.push([p0.y, p0.x]);
|
|
setP0(x + w, y);
|
|
points.push([p0.y, p0.x]);
|
|
setP0(x + w, y + h);
|
|
points.push([p0.y, p0.x]);
|
|
setP0(x, y + h);
|
|
points.push([p0.y, p0.x]);
|
|
break;
|
|
case 'path':
|
|
const commands = parse(path.getAttribute('d'));
|
|
for (const cmd of commands) {
|
|
switch (cmd[0]) {
|
|
case 'M':
|
|
case 'L':
|
|
setP0(cmd[1], cmd[2]);
|
|
break;
|
|
case 'm':
|
|
case 'l':
|
|
p0.x += cmd[1];
|
|
p0.y -= cmd[2];
|
|
break;
|
|
case 'C':
|
|
setP0(cmd[5], cmd[6]);
|
|
break;
|
|
case 'Z':
|
|
case 'z':
|
|
continue;
|
|
default:
|
|
console.error(cmd);
|
|
}
|
|
points.push([p0.y, p0.x]);
|
|
}
|
|
break;
|
|
}
|
|
return points;
|
|
}
|
|
|
|
function setupFeatures(prefix, baseURI) {
|
|
for (const campsite of Array.from(container.querySelectorAll(`[id^="${prefix}"]`))) {
|
|
const label = campsite.id.substring(prefix.length);
|
|
if (!label) {
|
|
continue;
|
|
}
|
|
const path = getPath(campsite);
|
|
if (!path) {
|
|
console.warn(campsite, 'has no path');
|
|
continue;
|
|
}
|
|
|
|
const points = convertToPoints(path);
|
|
const feature = L.polygon(points, {color: 'transparent'}).addTo(map);
|
|
feature.on({
|
|
mouseover: highlightAccommodation,
|
|
mouseout: resetHighlight,
|
|
click: function () {
|
|
window.location = `${baseURI}/${label}`;
|
|
},
|
|
})
|
|
}
|
|
}
|
|
|
|
const language = document.documentElement.getAttribute('lang');
|
|
setupFeatures('cp_', `/${language}/campsites`);
|
|
setupFeatures('cr_', `/${language}/amenities`);
|
|
|
|
const zones = container.querySelector('#zones');
|
|
const zonesOverlay = L.layerGroup();
|
|
for (const path of Array.from(zones.querySelectorAll('path'))) {
|
|
const points = convertToPoints(path);
|
|
const zone = L
|
|
.polygon(points, {color: 'var(--accent)'})
|
|
.bindTooltip(path.getAttribute('aria-label'), {permanent: true})
|
|
;
|
|
zonesOverlay.addLayer(zone);
|
|
}
|
|
L.control.layers(null, null, {collapsed: false})
|
|
.addOverlay(zonesOverlay, zones.getAttribute('aria-label'))
|
|
.addTo(map)
|
|
;
|
|
})();
|