Skip to content

Instantly share code, notes, and snippets.

@oscarlorentzon
Last active January 20, 2021 08:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save oscarlorentzon/0ec42b32dd175ca4cc7518006b888d3a to your computer and use it in GitHub Desktop.
Save oscarlorentzon/0ec42b32dd175ca4cc7518006b888d3a to your computer and use it in GitHub Desktop.
Sync viewer and map markers
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<link rel='stylesheet' href='https://unpkg.com/mapillary-js@3.1.0/dist/mapillary.min.css' />
<link href='https://unpkg.com/leaflet@1.0.1/dist/leaflet.css' rel='stylesheet' />
<script src='https://unpkg.com/mapillary-js@3.1.0/dist/mapillary.min.js'></script>
<script src='https://unpkg.com/leaflet@1.0.1/dist/leaflet.js'></script>
<style>
html, body { margin:0; padding:0; height: 100%; }
#mly { position: absolute; width: 60%; height: 100%; }
#map { position: absolute; width: 40%; height: 100%; right: 0; }
</style>
</head>
<body>
<div id='mly'></div>
<div id='map'></div>
<script>
// Setup map
var map = L.map('map').setView([56.04351888068181, 12.695600612967427], 18);
var osmUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
var osm = new L.TileLayer(osmUrl, { maxZoom: 19, attribution: osmAttrib});
map.addLayer(osm);
map.keyboard.disable();
// Setup viewer
var mly = new Mapillary.Viewer({
apiClient: 'QjI1NnU0aG5FZFZISE56U3R5aWN4Zzo3NTM1MjI5MmRjODZlMzc0',
component: {
marker: {
visibleBBoxSize: 100,
},
mouse: {
doubleClickZoom: false,
},
},
container: 'mly',
});
mly.moveToKey('6Zhtztzt67fWmdd4OYH44w').then(
function() { /* noop */ },
function(e) { console.error(e); });
// Show original latLon and latLon (if computed exist it will differ) of current
// viewer node on map. Show line linking the two points together
const mapNodePosition = {
line: L.polyline([[0, 0], [0, 0]], {color: '#0ff', weight: 1}),
originalPos: L.circleMarker([0, 0], { radius: 7, color: '#0ff' }),
pos: L.circleMarker([0, 0], { radius: 7, color: '#00f' }),
};
mly.on(Mapillary.Viewer.nodechanged, function(node) {
const latLon = [node.latLon.lat, node.latLon.lon];
const originalLatLon = [node.originalLatLon.lat, node.originalLatLon.lon];
mapNodePosition.line.setLatLngs([originalLatLon, latLon]);
mapNodePosition.originalPos.setLatLng(originalLatLon);
mapNodePosition.pos.setLatLng(latLon);
map.setView(latLon);
if (!map.hasLayer(mapNodePosition.line)) {
mapNodePosition.line.addTo(map);
mapNodePosition.originalPos.addTo(map);
mapNodePosition.pos.addTo(map);
}
});
// Get marker component
const markerComponent = mly.getComponent('marker');
// Show a flat circle marker in the viewer when hovering the map
let mapHoverViewerMarker;
const removeMapHoverViewerMarker = function() {
if (!!mapHoverViewerMarker && markerComponent.has(mapHoverViewerMarker.id)) {
markerComponent.remove([mapHoverViewerMarker.id]);
mapHoverViewerMarker = null;
}
}
const onMapMouseEvent = function(e) {
mapHoverViewerMarker = new Mapillary.MarkerComponent.CircleMarker(
'map-hover-viewer-marker-id',
{ lat: e.latlng.lat, lon: e.latlng.lng },
{ color: '#0f0' });
markerComponent.add([mapHoverViewerMarker]);
}
map.on('mousemove', onMapMouseEvent);
map.on('mouseover', onMapMouseEvent);
map.on('mouseout', removeMapHoverViewerMarker);
// Show a flat circle marker in the viewer and a corresponding map marker when hovering the viewer
const indicator = {
id: "indicator-id",
mapLine: L.polyline([[0, 0], [0, 0]], { color: '#0f0', weight: 1, id: "indicator-id-line" }),
mapMarker: L.circleMarker([0, 0], { radius: 5, color: '#0f0', id: "indicator-id-circle" }),
viewerMarker: null,
state: {
dragging: false,
lastPos: null,
moving: false,
},
};
const addMapIndicator = function() {
if (!map.hasLayer(indicator.mapLine)) {
indicator.mapLine.addTo(map);
}
if (!map.hasLayer(indicator.mapMarker)) {
indicator.mapMarker.addTo(map);
}
}
const removeMapIndicator = function() {
if (map.hasLayer(indicator.mapLine)) {
map.removeLayer(indicator.mapLine);
}
if (map.hasLayer(indicator.mapMarker)) {
map.removeLayer(indicator.mapMarker);
}
}
const removeViewerIndicator = function() {
if (!!indicator.viewerMarker && markerComponent.has(indicator.viewerMarker.id)) {
markerComponent.remove([indicator.viewerMarker.id]);
indicator.viewerMarker = null;
}
}
const setViewerIndicatorMarker = function(latLon) {
const viewerMarker = new Mapillary.MarkerComponent.CircleMarker(
indicator.id,
latLon,
{ color: '#0f0' });
markerComponent.add([viewerMarker]);
indicator.viewerMarker = viewerMarker;
}
const moveIndicatorMarker = function(latLon) {
if (indicator.state.dragging) { return; }
if (latLon == null) {
removeMapIndicator();
removeViewerIndicator();
return;
}
const posLatLng = mapNodePosition.pos.getLatLng();
const lineString = [
[posLatLng.lat, posLatLng.lng],
[latLon.lat, latLon.lon],
[
posLatLng.lat + 5 * (latLon.lat - posLatLng.lat),
posLatLng.lng + 5 * (latLon.lon - posLatLng.lng),
],
];
indicator.mapLine.setLatLngs(lineString);
indicator.mapMarker.setLatLng([latLon.lat, latLon.lon]);
setViewerIndicatorMarker({ lat: latLon.lat, lon: latLon.lon });
addMapIndicator();
}
const onViewerMouseEvent = function(event) {
indicator.state.lastPos = event.pixelPoint;
moveIndicatorMarker(event.latLon);
}
mly.on(Mapillary.Viewer.mouseup, onViewerMouseEvent);
mly.on(Mapillary.Viewer.mouseover, onViewerMouseEvent);
mly.on(Mapillary.Viewer.mousedown, onViewerMouseEvent);
mly.on(Mapillary.Viewer.mousemove, function(event) {
// Store last mouse position for later unprojection
indicator.state.lastPos = event.pixelPoint;
if (indicator.state.moving || indicator.state.dragging) { return; }
moveIndicatorMarker(event.latLon);
});
mly.on(Mapillary.Viewer.mouseout, function(event) {
indicator.state.lastPos = null;
removeViewerIndicator();
removeMapIndicator();
});
mly.on(Mapillary.Viewer.movestart, function(event) { indicator.state.moving = true; });
mly.on(Mapillary.Viewer.moveend, function(event) {
indicator.state.moving = false;
if (!indicator.state.lastPos) { return; }
// Unproject the last position and move indicator marker if latLon exist
mly.unproject(indicator.state.lastPos).then(moveIndicatorMarker);
});
markerComponent.on(Mapillary.MarkerComponent.MarkerComponent.dragstart, function() {
// Remove indicators when dragging marker in the viewer
indicator.state.dragging = true;
removeViewerIndicator();
removeMapIndicator();
});
markerComponent.on(Mapillary.MarkerComponent.MarkerComponent.dragend, function() {
indicator.state.dragging = false;
if (!indicator.state.lastPos) { return; }
// Unproject the last position and move indicator marker if latLon exist
mly.unproject(indicator.state.lastPos).then(moveIndicatorMarker);
});
// Create markers on click in map or viewer
let addedMarkerId = 0;
const mapMarkers = {};
const addOrReplaceViewerMarker = function(id, latLon) {
// Create an interactive marker to be able to drag it in viewer
// and retrieve it with getMarkerIdAt method
const marker = new Mapillary.MarkerComponent.SimpleMarker(
id,
latLon,
{ interactive: true });
markerComponent.add([marker]);
}
const handleMapMarkerDrag = function(mapMarker) {
// Listen to map events and act to move map and viewer markers accordingly
mapMarker.on({
mousedown: function(event) {
const onMouseMove = function(e) {
// Update both viewer marker and map marker on map marker drag
addOrReplaceViewerMarker(mapMarker.options.id, { lat: e.latlng.lat, lon: e.latlng.lng });
mapMarker.setLatLng(e.latlng);
};
const onMouseUp = function(e) {
map.off('mousemove', onMouseMove)
map.off('mouseup', onMouseUp);
}
map.on('mousemove', onMouseMove);
map.on('mouseup', onMouseUp);
},
mouseover: function(event) {
// Remove map hover viewer marker when hovering a map marker
removeMapHoverViewerMarker();
// Disable map dragging to ensure that only map marker is dragged
map.dragging.disable();
map.off('mousemove', onMapMouseEvent);
map.off('click', mapOnClick);
},
mouseout: function(event) {
map.dragging.enable();
map.on('mousemove', onMapMouseEvent);
map.on('click', mapOnClick);
},
});
}
const createMarker = function(latLon) {
const id = (addedMarkerId++).toString();
addOrReplaceViewerMarker(id, latLon);
const mapMarker =
L.circleMarker(
[latLon.lat, latLon.lon],
{ radius: 5, color: '#f00', draggable: 'true', id: id })
.addTo(map);
mapMarkers[id] = mapMarker;
handleMapMarkerDrag(mapMarker);
}
mly.on(Mapillary.Viewer.click, function(e) {
if (!e.latLon) { return; }
markerComponent.getMarkerIdAt(e.pixelPoint).then(function(markerId) {
// Only create a new marker if no interactive markers are hovered
if (markerId != null) { return; }
createMarker(e.latLon);
});
});
const mapOnClick = function(e) {
if (!e.latlng) { return; }
createMarker({ lat: e.latlng.lat, lon: e.latlng.lng });
};
map.on('click', mapOnClick);
// Update map marker when latLon changes for a marker by dragging in the viewer
markerComponent.on(Mapillary.MarkerComponent.MarkerComponent.changed, function(e) {
const mapMarker = mapMarkers[e.marker.id];
if (!mapMarker) {
return;
}
mapMarker.setLatLng([e.marker.latLon.lat, e.marker.latLon.lon]);
});
// Trigger render on browser window resize
window.addEventListener("resize", function() { mly.resize(); });
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment