sync: 2026-08-21 15:18:36
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||||
href="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css"
|
||||
>
|
||||
|
||||
<style>
|
||||
@@ -612,30 +612,57 @@
|
||||
|
||||
|
||||
/* =====================================================
|
||||
LEAFLET
|
||||
MAPLIBRE CONTROLS
|
||||
===================================================== */
|
||||
|
||||
.leaflet-control-zoom {
|
||||
margin-right: 18px !important;
|
||||
|
||||
margin-bottom: 82px !important;
|
||||
|
||||
.maplibregl-ctrl-group {
|
||||
border: none !important;
|
||||
|
||||
box-shadow:
|
||||
0 2px 7px rgba(0, 0, 0, 0.25) !important;
|
||||
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.leaflet-control-zoom a {
|
||||
.maplibregl-ctrl-group button {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.leaflet-control-attribution {
|
||||
.maplibregl-ctrl-attrib {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Position nav controls away from the search UI */
|
||||
.maplibregl-ctrl-topleft {
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.maplibregl-ctrl-topright {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* =====================================================
|
||||
MARKER STYLES
|
||||
===================================================== */
|
||||
|
||||
/* Wrapper that holds both the label and the pin circle */
|
||||
.poi-pin-wrapper {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
}
|
||||
|
||||
/* Permanent name label shown above each pin */
|
||||
.pin-label {
|
||||
position: absolute;
|
||||
bottom: 38px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
@@ -645,18 +672,10 @@
|
||||
font-weight: 500;
|
||||
padding: 2px 7px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pin-label::before {
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Custom circular pin that reflects the POI type */
|
||||
.poi-pin {
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.poi-pin-inner {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
@@ -681,8 +700,7 @@
|
||||
|
||||
/* User location marker (blue dot) */
|
||||
.user-loc-marker {
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.user-loc-dot {
|
||||
@@ -762,10 +780,6 @@
|
||||
right: 12px;
|
||||
bottom: 72px;
|
||||
}
|
||||
|
||||
.leaflet-control-zoom {
|
||||
margin-bottom: 125px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -929,7 +943,7 @@
|
||||
</button>
|
||||
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
@@ -949,30 +963,26 @@
|
||||
*/
|
||||
const CLICK_ZOOM = 15;
|
||||
|
||||
const MAPTILER_KEY = "aUftDXS9UHdRVbSuaUWz";
|
||||
|
||||
|
||||
/* =========================================================
|
||||
MAP
|
||||
========================================================= */
|
||||
|
||||
const map =
|
||||
L.map("map").setView(
|
||||
[
|
||||
AMSTERDAM_LAT,
|
||||
AMSTERDAM_LON
|
||||
],
|
||||
DEFAULT_ZOOM
|
||||
);
|
||||
const map = new maplibregl.Map({
|
||||
container: "map",
|
||||
style:
|
||||
"https://api.maptiler.com/maps/streets-v4/style.json?key=" +
|
||||
MAPTILER_KEY,
|
||||
center: [AMSTERDAM_LON, AMSTERDAM_LAT],
|
||||
zoom: DEFAULT_ZOOM
|
||||
});
|
||||
|
||||
|
||||
L.tileLayer(
|
||||
"https://api.maptiler.com/maps/streets-v4/256/{z}/{x}/{y}.png?key=aUftDXS9UHdRVbSuaUWz",
|
||||
{
|
||||
maxZoom: 19,
|
||||
|
||||
attribution:
|
||||
"© OpenStreetMap contributors"
|
||||
}
|
||||
).addTo(map);
|
||||
map.addControl(
|
||||
new maplibregl.NavigationControl(),
|
||||
"top-right"
|
||||
);
|
||||
|
||||
|
||||
/* =========================================================
|
||||
@@ -1014,6 +1024,8 @@
|
||||
|
||||
let selectedMarker = null;
|
||||
|
||||
let selectedPopup = null;
|
||||
|
||||
let userLocationMarker = null;
|
||||
|
||||
let zoomMessageTimer = null;
|
||||
@@ -1052,13 +1064,13 @@
|
||||
|
||||
// Build a viewbox from the currently visible map area so
|
||||
// results are restricted to what the user can see.
|
||||
const visibleBounds = map.getBounds();
|
||||
const mb = map.getBounds();
|
||||
|
||||
const viewbox = [
|
||||
visibleBounds.getWest(),
|
||||
visibleBounds.getNorth(),
|
||||
visibleBounds.getEast(),
|
||||
visibleBounds.getSouth()
|
||||
mb.west,
|
||||
mb.north,
|
||||
mb.east,
|
||||
mb.south
|
||||
].join(",");
|
||||
|
||||
|
||||
@@ -1182,27 +1194,42 @@
|
||||
);
|
||||
|
||||
|
||||
// Build the marker element with label + pin
|
||||
const el =
|
||||
document.createElement(
|
||||
"div"
|
||||
);
|
||||
|
||||
el.className =
|
||||
"poi-pin-wrapper";
|
||||
|
||||
el.innerHTML = `
|
||||
<div class="pin-label">
|
||||
${escapeHtml(
|
||||
getName(place)
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
class="poi-pin-inner"
|
||||
style="background:${getPoiColor(place)};"
|
||||
>
|
||||
${getIcon(place)}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
const marker =
|
||||
L.marker([
|
||||
place.lat,
|
||||
place.lon
|
||||
], {
|
||||
icon: createPoiIcon(place)
|
||||
}).addTo(map);
|
||||
new maplibregl.Marker({
|
||||
element: el
|
||||
})
|
||||
.setLngLat([
|
||||
place.lon,
|
||||
place.lat
|
||||
])
|
||||
.addTo(map);
|
||||
|
||||
|
||||
marker.bindTooltip(
|
||||
escapeHtml(getName(place)),
|
||||
{
|
||||
permanent: true,
|
||||
direction: "top",
|
||||
offset: [0, -20],
|
||||
className: "pin-label"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
marker.on(
|
||||
marker.getElement().addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
|
||||
@@ -1231,26 +1258,36 @@
|
||||
|
||||
const zoom = getZoomForPlace(data[0]);
|
||||
|
||||
map.flyTo(
|
||||
bounds[0],
|
||||
zoom,
|
||||
{
|
||||
duration: 0.7
|
||||
}
|
||||
);
|
||||
map.flyTo({
|
||||
center: [
|
||||
bounds[0][1],
|
||||
bounds[0][0]
|
||||
],
|
||||
zoom: zoom,
|
||||
duration: 700
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
map.fitBounds(
|
||||
bounds,
|
||||
{
|
||||
padding: [
|
||||
80,
|
||||
80
|
||||
]
|
||||
}
|
||||
);
|
||||
const lons =
|
||||
bounds.map(b => b[1]);
|
||||
|
||||
const lats =
|
||||
bounds.map(b => b[0]);
|
||||
|
||||
map.fitBounds([
|
||||
[
|
||||
Math.min(...lons),
|
||||
Math.min(...lats)
|
||||
],
|
||||
[
|
||||
Math.max(...lons),
|
||||
Math.max(...lats)
|
||||
]
|
||||
], {
|
||||
padding: 80
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1361,47 +1398,66 @@
|
||||
"none";
|
||||
|
||||
|
||||
if (selectedMarker) {
|
||||
|
||||
map.removeLayer(
|
||||
selectedMarker
|
||||
);
|
||||
}
|
||||
clearSelection();
|
||||
|
||||
|
||||
selectedMarker =
|
||||
L.marker([
|
||||
place.lat,
|
||||
place.lon
|
||||
]).addTo(map);
|
||||
new maplibregl.Marker()
|
||||
.setLngLat([
|
||||
place.lon,
|
||||
place.lat
|
||||
])
|
||||
.addTo(map);
|
||||
|
||||
|
||||
const zoom = getZoomForPlace(place);
|
||||
|
||||
|
||||
map.flyTo(
|
||||
[
|
||||
place.lat,
|
||||
place.lon
|
||||
map.flyTo({
|
||||
center: [
|
||||
place.lon,
|
||||
place.lat
|
||||
],
|
||||
zoom,
|
||||
{
|
||||
duration: 0.7
|
||||
}
|
||||
);
|
||||
zoom: zoom,
|
||||
duration: 700
|
||||
});
|
||||
|
||||
|
||||
renderPlace(place);
|
||||
}
|
||||
|
||||
|
||||
/* =========================================================
|
||||
CLEAR SELECTION
|
||||
========================================================= */
|
||||
|
||||
function clearSelection() {
|
||||
|
||||
if (selectedMarker) {
|
||||
|
||||
selectedMarker.remove();
|
||||
|
||||
selectedMarker =
|
||||
null;
|
||||
}
|
||||
|
||||
if (selectedPopup) {
|
||||
|
||||
selectedPopup.remove();
|
||||
|
||||
selectedPopup =
|
||||
null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =========================================================
|
||||
MAP CLICK
|
||||
========================================================= */
|
||||
|
||||
map.on(
|
||||
"click",
|
||||
async event => {
|
||||
async e => {
|
||||
|
||||
/*
|
||||
* Don't reverse-geocode at low zoom.
|
||||
@@ -1418,10 +1474,10 @@
|
||||
|
||||
|
||||
const lat =
|
||||
event.latlng.lat;
|
||||
e.lngLat.lat;
|
||||
|
||||
const lon =
|
||||
event.latlng.lng;
|
||||
e.lngLat.lng;
|
||||
|
||||
|
||||
resultsCard.style.display =
|
||||
@@ -1441,19 +1497,16 @@
|
||||
`;
|
||||
|
||||
|
||||
if (selectedMarker) {
|
||||
|
||||
map.removeLayer(
|
||||
selectedMarker
|
||||
);
|
||||
}
|
||||
clearSelection();
|
||||
|
||||
|
||||
selectedMarker =
|
||||
L.marker([
|
||||
lat,
|
||||
lon
|
||||
]).addTo(map);
|
||||
new maplibregl.Marker()
|
||||
.setLngLat([
|
||||
lon,
|
||||
lat
|
||||
])
|
||||
.addTo(map);
|
||||
|
||||
|
||||
try {
|
||||
@@ -1496,11 +1549,20 @@
|
||||
}
|
||||
|
||||
|
||||
selectedMarker.bindPopup(
|
||||
escapeHtml(
|
||||
getName(place)
|
||||
selectedPopup =
|
||||
new maplibregl.Popup({
|
||||
offset: 25
|
||||
})
|
||||
.setHTML(
|
||||
escapeHtml(
|
||||
getName(place)
|
||||
)
|
||||
)
|
||||
).openPopup();
|
||||
.setLngLat([
|
||||
lon,
|
||||
lat
|
||||
])
|
||||
.addTo(map);
|
||||
|
||||
|
||||
renderPlace(place);
|
||||
@@ -1923,15 +1985,7 @@
|
||||
"none";
|
||||
|
||||
|
||||
if (selectedMarker) {
|
||||
|
||||
map.removeLayer(
|
||||
selectedMarker
|
||||
);
|
||||
|
||||
selectedMarker =
|
||||
null;
|
||||
}
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
|
||||
@@ -2185,30 +2239,41 @@
|
||||
|
||||
function showUserLocation(lat, lon) {
|
||||
|
||||
const coords = [lat, lon];
|
||||
const coords = [lon, lat];
|
||||
|
||||
|
||||
if (!userLocationMarker) {
|
||||
|
||||
const el =
|
||||
document.createElement(
|
||||
"div"
|
||||
);
|
||||
|
||||
el.className =
|
||||
"user-loc-marker";
|
||||
|
||||
el.innerHTML =
|
||||
'<div class="user-loc-dot"></div>';
|
||||
|
||||
|
||||
userLocationMarker =
|
||||
L.marker(coords, {
|
||||
icon: createUserLocationIcon(),
|
||||
zIndexOffset: 1000
|
||||
}).addTo(map);
|
||||
new maplibregl.Marker({
|
||||
element: el
|
||||
})
|
||||
.setLngLat(coords)
|
||||
.addTo(map);
|
||||
|
||||
} else {
|
||||
|
||||
userLocationMarker.setLatLng(coords);
|
||||
userLocationMarker.setLngLat(coords);
|
||||
}
|
||||
|
||||
|
||||
map.flyTo(
|
||||
coords,
|
||||
16,
|
||||
{
|
||||
duration: 0.8
|
||||
}
|
||||
);
|
||||
map.flyTo({
|
||||
center: coords,
|
||||
zoom: 16,
|
||||
duration: 800
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2340,24 +2405,14 @@
|
||||
|
||||
markers.forEach(
|
||||
marker =>
|
||||
map.removeLayer(
|
||||
marker
|
||||
)
|
||||
marker.remove()
|
||||
);
|
||||
|
||||
|
||||
markers = [];
|
||||
|
||||
|
||||
if (selectedMarker) {
|
||||
|
||||
map.removeLayer(
|
||||
selectedMarker
|
||||
);
|
||||
|
||||
selectedMarker =
|
||||
null;
|
||||
}
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
|
||||
@@ -2481,7 +2536,7 @@
|
||||
|
||||
road +=
|
||||
" " +
|
||||
address.house_number;
|
||||
address.house_number;
|
||||
}
|
||||
|
||||
|
||||
@@ -2655,38 +2710,6 @@
|
||||
}
|
||||
|
||||
|
||||
function createPoiIcon(place) {
|
||||
|
||||
const html = `
|
||||
<div
|
||||
class="poi-pin-inner"
|
||||
style="background:${getPoiColor(place)};"
|
||||
>
|
||||
${getIcon(place)}
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
return L.divIcon({
|
||||
className: "poi-pin",
|
||||
html: html,
|
||||
iconSize: [34, 34],
|
||||
iconAnchor: [17, 17]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function createUserLocationIcon() {
|
||||
|
||||
return L.divIcon({
|
||||
className: "user-loc-marker",
|
||||
html: `<div class="user-loc-dot"></div>`,
|
||||
iconSize: [18, 18],
|
||||
iconAnchor: [9, 9]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function safeUrl(url) {
|
||||
|
||||
if (!url)
|
||||
|
||||
Reference in New Issue
Block a user