Skip to content

Commit

Permalink
Popup on clicking map features (#80)
Browse files Browse the repository at this point in the history
Part of #4 

Needs more work on the fields that are available for each layer / source
per zoom level. Also descriptions / maps like gauge sizes, train
protection system and voltage description is not taken into account. In
general some layers need to be merged, and reused so the standard layer
gets e.g. gauge sizes on all zoom levels.
  • Loading branch information
hiddewie authored Jul 29, 2024
1 parent b955a0d commit 4604572
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 27 deletions.
1 change: 1 addition & 0 deletions martin/configuration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ postgres:
properties:
railway: string
ref: string
# TODO: rename local_operated
railway_local_operated: boolean

# --- Speed --- #
Expand Down
2 changes: 1 addition & 1 deletion proxy/css/ui.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ body {
border-color: #1c7430;
}
.maplibregl-ctrl-edit .maplibregl-ctrl-icon {
background-size: contain;
background-size: 24px;
background-image: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z' fill='%23000000'/%3E%3C/svg%3E");
}
.maplibregl-ctrl-configuration .maplibregl-ctrl-icon {
Expand Down
2 changes: 1 addition & 1 deletion proxy/js/styles.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3832,7 +3832,7 @@ const legendData = {
feature: 'does-not-exist',
type: 'line',
azimuth: 135.5,
direction_both: false,
direction_both: false,
},
variants: [
{
Expand Down
114 changes: 89 additions & 25 deletions proxy/js/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@ function showSearchResults(results) {
</div>
<div class="list-group">
${results.map(result =>
`<a class="list-group-item list-group-item-action" href="javascript:hideSearchResults(); map.easeTo({center: [${result.latitude}, ${result.longitude}], zoom: 15}); hideSearch()">
`<a class="list-group-item list-group-item-action" href="javascript:hideSearchResults(); map.easeTo({center: [${result.latitude}, ${result.longitude}], zoom: 15}); hideSearch()">
${result.icon ? `${result.icon}` : ''}
${result.label}
</a>`
).join('')}
).join('')}
</div>
`;
searchResults.style.display = 'block';
Expand Down Expand Up @@ -579,42 +579,106 @@ const onStylesheetChange = styleSheet => {
onMapZoom(map.getZoom());
}

map.on('load', () => onMapZoom(map.getZoom()));
map.on('zoomend', () => onMapZoom(map.getZoom()));

// When a click event occurs on a feature in the places layer, open a popup at the
// location of the feature, with description HTML from its properties.
map.on('click', 'search', (e) => {
const feature = e.features[0];
const coordinates = feature.geometry.coordinates.slice();
const properties = feature.properties;
const content = `
function popupContent(properties) {
// TODO move icon SVGs to proxy
// TODO reuse icons from map features for these features
// TODO lookup train protection name
// TODO format voltage
// TODO format gauge(s)
const label = properties.label ?? properties.name ?? properties.ref;
return `
<h6>
${properties.icon ? `<span title="${properties.railway}">${properties.icon}</span>` : ''}
<a title="View" href="https://www.openstreetmap.org/node/${properties.osm_id}" target="_blank">${properties.label}</a>
<a title="Edit" href="https://www.openstreetmap.org/edit?node=${properties.osm_id}" target="_blank">${icons.edit}</a>
${label ? `${properties.osm_id ? `<a title="View" href="https://www.openstreetmap.org/node/${properties.osm_id}" target="_blank">` : ''}${label}${properties.osm_id ? `</a>` : ''}` : ''}
${properties.osm_id ? `<a title="Edit" href="https://www.openstreetmap.org/edit?node=${properties.osm_id}" target="_blank">${icons.edit}</a>` : ''}
</h6>
<h6>
${properties.railway_ref ? `<span class="badge badge-pill badge-light">reference: <span class="text-monospace">${properties.railway_ref}</span></span>` : ''}
${properties.ref ? `<span class="badge badge-pill badge-light">reference: <span class="text-monospace">${properties.ref}</span></span>` : ''}
${properties.uic_ref ? `<span class="badge badge-pill badge-light">UIC reference: <span class="text-monospace">${properties.uic_ref}</span></span>` : ''}
${properties.position ? `<span class="badge badge-pill badge-light">position: ${properties.position}</span>` : ''}
${properties.pos ? `<span class="badge badge-pill badge-light">position: ${properties.pos}</span>` : ''}
${properties.operator ? `<span class="badge badge-pill badge-light">operator: ${properties.operator}</span>` : ''}
${properties.track_ref ? `<span class="badge badge-pill badge-light">track: ${properties.track_ref}</span>` : ''}
${properties.highspeed === true ? `<span class="badge badge-pill badge-light">high speed</span>` : ''}
${properties.usage ? `<span class="badge badge-pill badge-light">usage: <span class="text-monospace">${properties.usage}</span></span>` : ''}
${properties.service ? `<span class="badge badge-pill badge-light">service: <span class="text-monospace">${properties.service}</span></span>` : ''}
${properties.tunnel === true ? `<span class="badge badge-pill badge-light">tunnel</span>` : ''}
${properties.bridge === true ? `<span class="badge badge-pill badge-light">bridge</span>` : ''}
${properties.railway_local_operated === true ? `<span class="badge badge-pill badge-light">operated locally</span>` : ''}
${properties.maxspeed ? `<span class="badge badge-pill badge-light">maximum speed: ${properties.maxspeed} km/h</span>` : ''}
${properties.direction_both ? `<span class="badge badge-pill badge-light">both directions</span>` : ''}
${properties.train_protection ? `<span class="badge badge-pill badge-light">train protection: <span class="text-monospace">${properties.train_protection}</span></span>` : ''}
${properties.deactivated === true ? `<span class="badge badge-pill badge-light">deactivated</span>` : ''}
${properties.type === 'line' ? `<span class="badge badge-pill badge-light">line signal</span>` : ''}
${properties.electrification_state ? `<span class="badge badge-pill badge-light">line electrification: <span class="text-monospace">${properties.electrification_state}</span></span>` : ''}
${properties.voltage ? `<span class="badge badge-pill badge-light">voltage: ${properties.voltage} V</span>` : ''}
${properties.frequency ? `<span class="badge badge-pill badge-light">frequency: ${properties.frequency} Hz</span>` : ''}
${properties.gauge0 ? `<span class="badge badge-pill badge-light">gauge: ${properties.gauge0}</span>` : ''}
${properties.gauge1 ? `<span class="badge badge-pill badge-light">gauge: ${properties.gauge1}</span>` : ''}
${properties.gauge2 ? `<span class="badge badge-pill badge-light">gauge: ${properties.gauge2}</span>` : ''}
</h6>
`;
}

new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(content)
.addTo(map);
});
map.on('load', () => onMapZoom(map.getZoom()));
map.on('zoomend', () => onMapZoom(map.getZoom()));

// Change the cursor to a pointer when the mouse is over the places layer.
map.on('mouseenter', 'search', () => {
map.getCanvas().style.cursor = 'pointer';
map.on('mousehover', event => {
const features = map.queryRenderedFeatures(event.point);
if (features.length > 0) {
map.getCanvas().style.cursor = 'pointer';
} else {
map.getCanvas().style.cursor = '';
}
});

// Change it back to a pointer when it leaves.
map.on('mouseleave', 'search', () => {
map.getCanvas().style.cursor = '';
function closestPointOnLine(point, line) {
const lngLatPoint = maplibregl.LngLat.convert(point)
let {closest0, closest1} = line.map(maplibregl.LngLat.convert).reduce((acc, cur) => {
const d = lngLatPoint.distanceTo(cur)
if (acc.closest0 == null || d < lngLatPoint.distanceTo(acc.closest0)) {
return {closest0: cur, closest1: acc.closest0}
} else if (acc.closest1 == null || d < lngLatPoint.distanceTo(acc.closest1)) {
return {closest0: acc.closest0, closest1: cur}
} else {
return acc;
}
}, {closest0: null, closest1: null});

closest0 = closest0.toArray()
closest1 = closest1.toArray()
point = lngLatPoint.toArray()

if (closest0 == null && closest1 == null) {
return null;
} else if (closest1 == null) {
return closest0;
} else {
// project point onto line between closest0 and closest1
const abx = closest1[0] - closest0[0]
const aby = closest1[1] - closest0[1]
const acx = point[0] - closest0[0]
const acy = point[1] - closest0[1]
const coeff = (abx * acx + aby * acy) / (abx * abx + aby * aby)
return [closest0[0] + abx * coeff, closest0[1] + aby * coeff]
}
}

map.on('click', event => {
const features = map.queryRenderedFeatures(event.point);
if (features.length > 0) {
const feature = features[0];

const coordinates = feature.geometry.type === 'Point'
? feature.geometry.coordinates.slice()
: feature.geometry.type === 'LineString'
? closestPointOnLine(event.lngLat, feature.geometry.coordinates)
: event.lngLat;

new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(popupContent(feature.properties))
.addTo(map);
}
});

0 comments on commit 4604572

Please sign in to comment.