Add a polygon around each accommodation on the public map
I can not use <a> in that map because Leaflet handles the mouse over
before the anchors sees it, thus it is impossible to click on them; i
have to use a Leaflet layer.
Fortunately, i can just use the <path>’s coordinates as
the polygon points, because with CRS.Simple the coordinates map to
pixel, except for the reversed Y/latitude coordinate. Unfortunately,
<path> coordinates are not straightforward to get: I have to follow the
drawing coordinates, taking into account the current transformation
(CTM), and keeping the last point around for relative coordinates.
Bézier curves are simplified to a straight line from start to end.
There is one single accommodation that started with a relative move
command (m), which apparently have to be treated as an absolute
move (M), but subsequent pairs are treated as relative coordinates[0].
It was easier for me to convert that relative move to absolute and add
a relative lineto command (l) to the next pair.
For now, all i do is highlight the accommodation and zoom it on click,
because i do not know how i should the accommodation’s information.
[0]: https://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands
2024-01-25 03:28:51 +00:00
|
|
|
(function () {
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const container = document.getElementById('campground_map');
|
|
|
|
container.style.height = '80vh';
|
|
|
|
|
|
|
|
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',
|
|
|
|
});
|
|
|
|
layer.bringToFront();
|
|
|
|
}
|
|
|
|
|
|
|
|
function resetHighlight(e) {
|
|
|
|
const layer = e.target;
|
|
|
|
layer.setStyle({
|
|
|
|
color: 'transparent',
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX: this is from the “parceles” layer.
|
|
|
|
const ctm = [1, 0, 0, 1, 83.2784, 66.1766];
|
|
|
|
const transform = function (x, y) {
|
|
|
|
return [ctm[0] * x + ctm[2] * y + ctm[4], ctm[1] * x + ctm[3] * y + ctm[5]];
|
|
|
|
}
|
|
|
|
|
2024-01-26 21:27:54 +00:00
|
|
|
const language = document.documentElement.getAttribute('lang');
|
Add a polygon around each accommodation on the public map
I can not use <a> in that map because Leaflet handles the mouse over
before the anchors sees it, thus it is impossible to click on them; i
have to use a Leaflet layer.
Fortunately, i can just use the <path>’s coordinates as
the polygon points, because with CRS.Simple the coordinates map to
pixel, except for the reversed Y/latitude coordinate. Unfortunately,
<path> coordinates are not straightforward to get: I have to follow the
drawing coordinates, taking into account the current transformation
(CTM), and keeping the last point around for relative coordinates.
Bézier curves are simplified to a straight line from start to end.
There is one single accommodation that started with a relative move
command (m), which apparently have to be treated as an absolute
move (M), but subsequent pairs are treated as relative coordinates[0].
It was easier for me to convert that relative move to absolute and add
a relative lineto command (l) to the next pair.
For now, all i do is highlight the accommodation and zoom it on click,
because i do not know how i should the accommodation’s information.
[0]: https://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands
2024-01-25 03:28:51 +00:00
|
|
|
const prefix = 'cp_';
|
|
|
|
for (const campsite of Array.from(container.querySelectorAll(`[id^="${prefix}"]`))) {
|
|
|
|
const label = campsite.id.substring(prefix.length);
|
|
|
|
if (!label) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const path = campsite.firstElementChild;
|
|
|
|
const commands = parse(path.getAttribute('d'));
|
|
|
|
const points = [];
|
|
|
|
const p0 = [0, 0];
|
|
|
|
for (const cmd of commands) {
|
|
|
|
switch (cmd[0]) {
|
|
|
|
case 'M':
|
|
|
|
case 'L':
|
|
|
|
const cmdM = transform(cmd[1], cmd[2]);
|
|
|
|
p0[0] = cmdM[0];
|
|
|
|
p0[1] = latLngBounds.getNorth() - cmdM[1];
|
|
|
|
break;
|
|
|
|
case 'm':
|
|
|
|
case 'l':
|
|
|
|
p0[0] += cmd[1];
|
|
|
|
p0[1] -= cmd[2];
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
const cmdC = transform(cmd[5], cmd[6]);
|
|
|
|
p0[0] = cmdC[0];
|
|
|
|
p0[1] = latLngBounds.getNorth() - cmdC[1];
|
|
|
|
break;
|
|
|
|
case 'Z':
|
|
|
|
case 'z':
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
console.error(cmd);
|
|
|
|
}
|
|
|
|
points.push([p0[1], p0[0]]);
|
|
|
|
}
|
|
|
|
const accommodation = L.polygon(points, {color: 'transparent'}).addTo(map);
|
|
|
|
accommodation.on({
|
|
|
|
mouseover: highlightAccommodation,
|
|
|
|
mouseout: resetHighlight,
|
2024-01-26 21:27:54 +00:00
|
|
|
click: function () {
|
|
|
|
window.location = `/${language}/campsites/${label}`;
|
|
|
|
},
|
Add a polygon around each accommodation on the public map
I can not use <a> in that map because Leaflet handles the mouse over
before the anchors sees it, thus it is impossible to click on them; i
have to use a Leaflet layer.
Fortunately, i can just use the <path>’s coordinates as
the polygon points, because with CRS.Simple the coordinates map to
pixel, except for the reversed Y/latitude coordinate. Unfortunately,
<path> coordinates are not straightforward to get: I have to follow the
drawing coordinates, taking into account the current transformation
(CTM), and keeping the last point around for relative coordinates.
Bézier curves are simplified to a straight line from start to end.
There is one single accommodation that started with a relative move
command (m), which apparently have to be treated as an absolute
move (M), but subsequent pairs are treated as relative coordinates[0].
It was easier for me to convert that relative move to absolute and add
a relative lineto command (l) to the next pair.
For now, all i do is highlight the accommodation and zoom it on click,
because i do not know how i should the accommodation’s information.
[0]: https://www.w3.org/TR/SVG11/paths.html#PathDataMovetoCommands
2024-01-25 03:28:51 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})();
|