(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) ; })();