class mglStreetViewControl {
/**
*
* @param {NewType} options
*/
constructor(options) {
this._mapillaryAlias = (!options || !options.mapillaryAlias) ? "Mapillary" : options.mapillaryAlias;
this._mapillaryLayerOptions = (!options || !options.mapillaryLayerOptions) ? {
userKey: "WpzhyQeWLPnZ_TwvxcdU_w",
pano: 1
} : options.mapillaryLayerOptions
}
onAdd (map) {
const _mapillaryAlias = this._mapillaryAlias
//TODO does this need to exist or can we just use map
const _map = this._map = map;
//button element
this._btn = document.createElement('button');
this._btn.innerHTML = streetViewIcon();
this._btn.type = 'button';
this._btn['aria-label'] = 'Open Streetview';
this._btn['title'] = 'Open Streetview';
this._btn.id = "mglStreetViewControl"
//spectre css only
//TODO add option to put this on right if control is set to the right
this._btn.classList = "tooltip tooltip-left";
this._btn.dataset.tooltip = "Open Streetview"
//add mapillary points and lines
const _mapillaryLayers = mapillaryLayers(this._mapillaryLayerOptions);
_mapillaryLayers.map(l => {
_map.addLayer(l);
});
const _mapMinZoom = this._map.getMinZoom();
//add global marker
const _marker = new mapboxgl.Marker({
draggable: true
});
this._btn.onclick = function () {
//if layers are already showing, reset and return
if (_map.getLayoutProperty(_mapillaryLayers[0].id, "visibility") === "visible") {
reset(_marker);
return;
}
//set latlng and show marker
_marker.setLngLat(map.getCenter())
.addTo(map)
.on('dragend', onDragEnd);
//map set min zoom to 14 and set zoom to 14 is below 14
_map.setMinZoom(14);
if (_map.getZoom() < 14)
_map.setZoom(14);
//show mapillary imagery points and lines
_mapillaryLayers.forEach(l => {
_map.setLayoutProperty(l.id, "visibility", "visible");
});
//show toast, spectre css only
if (!document.getElementById("streetviewControlToast")) {
const toast = document.createElement("div");
toast.id = "streetviewControlToast";
toast.classList.add("toast");
toast.style.backgroundColor = "firebrick";
toast.style.position = "absolute";
toast.style.bottom = 0;
toast.style.width = "100%";
toast.style.padding = "1rem 0";
toast.style.textAlign = "center";
toast.style.color = "white";
toast.innerText = "Drag the marker to a location on the street. The selected street view option will open in a new window.";
document.body.appendChild(toast);
} else {
document.getElementById("streetviewControlToast").style.display = "block";
}
setTimeout(function() {
document.getElementById("streetviewControlToast").style.display = "none"
}, 2600)
//listener for 'dragend' event
async function onDragEnd() {
//show loading
showLoading()
//get latlng
const lngLat = this.getLngLat();
const lng = Number(lngLat.lng.toFixed(8));
const lat = Number(lngLat.lat.toFixed(8));
//see if mapillary imags are available
const bboxClickTargetSize = 40;
const point = _map.project(lngLat)
const bbox = [
[point.x - bboxClickTargetSize / 2, point.y - bboxClickTargetSize / 2],
[point.x + bboxClickTargetSize / 2, point.y + bboxClickTargetSize / 2]
]
const mapillaryLayerIds = _mapillaryLayers.reduce((i, l) => [...i, l.id], []);
const mapillaryImages = _map.queryRenderedFeatures(bbox, { layers: mapillaryLayerIds });
const mapillaryExists = (!mapillaryImages.length) ? false : true;
const urls = ["https://www.google.com/maps/@?api=1&map_action=pano&viewpoint=".concat(lngLat.lat, ",").concat(lngLat.lng)]
if (mapillaryExists) {
urls.push("https://www.mapillary.com/app/?z=16&lat=" + lat + "&lng=" + lng + "&focus=photo&panos=true&pKey=" + mapillaryImages[0].properties.key);
}
const imgSource = (urls.length < 2) ? 0 : (confirm(_mapillaryAlias + " Street View Imagery is avalable at this location.\n\nClick 'OK' to use " + _mapillaryAlias + "Street View Imagery\n--OR--\n'Cancel' to use Google Street View Imagery")) ? 1 : 0;
window.open(urls[imgSource], "_blank")
reset(this);
}
function reset(marker) {
//TODO fix this hack of getting rid of the event listeners
//remove marker and listener
marker._listeners = [];
marker.off('dragend', onDragEnd);
marker.remove();
//reset map minzoom
_map.setMinZoom(_mapMinZoom);
//hide mapillary points and lines
_mapillaryLayers.forEach(l => {
_map.setLayoutProperty(l.id, "visibility", "none");
});
//hide toast message
if (document.querySelector("#streetviewControlToast")) {
document.getElementById("streetviewControlToast").style.display = "none";
}
//hide loading
hideLoading()
}
};
this._container = document.createElement('div');
this._container.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
this._container.appendChild(this._btn);
return this._container;
}
onRemove() {
this._container.parentNode.removeChild(this._container);
this._map = undefined;
}
}
export {
mglStreetViewControl
};
function showLoading() {
if (document.querySelector("#loading")) {
document.querySelector("#loading").classList.add("loading");
}
}
function hideLoading() {
if (document.querySelector("#loading")) {
document.querySelector("#loading").classList.remove("loading");
}
}
/**
* these options are supplied in the global config object of the plugin
* @param {*} options abject with a mapillary userKey and panamoramic boolean option 1 or 0
*/
function mapillaryLayers(options) {
const {userKey, pano} = options
return JSON.parse(`[{
"id": "svcMapillaryLinesOuter",
"type": "line",
"source": {
"type": "vector",
"tiles": [
"https://tiles3.mapillary.com/v0.1/{z}/{x}/{y}.mvt"
],
"minzoom": 0,
"maxzoom": 14,
"promoteId": "key"
},
"source-layer": "mapillary-sequences",
"paint": {
"line-opacity": 0.9,
"line-color": "white",
"line-gap-width": 3,
"line-width": 2
},
"layout": {
"visibility": "none",
"line-cap": "round",
"line-join": "round"
},
"filter": [
"all",
${(!userKey) ? "" : `[
"==",
[
"get",
"userkey"
],
"${userKey}"
],`}
[
"==",
[
"get",
"pano"
],
${pano}
]
]
},
{
"id": "svcMapillaryLinesInner",
"type": "line",
"source": {
"type": "vector",
"tiles": [
"https://tiles3.mapillary.com/v0.1/{z}/{x}/{y}.mvt"
],
"minzoom": 0,
"maxzoom": 14,
"promoteId": "key"
},
"source-layer": "mapillary-sequences",
"paint": {
"line-opacity": 0.6,
"line-color": "#05CB63",
"line-width": 3
},
"layout": {
"visibility": "none",
"line-cap": "round",
"line-join": "round"
},
"filter": [
"all",
${(!userKey) ? "" : `[
"==",
[
"get",
"userkey"
],
"${userKey}"
],`}
[
"==",
[
"get",
"pano"
],
${pano}
]
]
},
{
"id": "svcMapillaryPoints",
"type": "circle",
"source": {
"type": "vector",
"tiles": [
"https://tiles3.mapillary.com/v0.1/{z}/{x}/{y}.mvt"
],
"minzoom": 0,
"maxzoom": 14,
"promoteId": "key"
},
"source-layer": "mapillary-images",
"paint": {
"circle-color": "#05CB63",
"circle-stroke-color": "white",
"circle-stroke-width": 1,
"circle-pitch-scale": "map",
"circle-pitch-alignment": "map"
},
"layout": {
"visibility": "none"
},
"filter": [
"all",
${(!userKey) ? "" : `[
"==",
[
"get",
"userkey"
],
"${userKey}"
],`}
[
"==",
[
"get",
"pano"
],
${pano}
]
]
}]`)
}
//see https://material.io/resources/icons/?icon=person_pin_circle&style=baseline
/**
* returns a material design street view svg icon
*/
function streetViewIcon() {
return '<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24"viewBox="0 0 24 24"><g><rect fill="none" height="24" width="24"/></g><g><g><g><path d="M12,2C8.14,2,5,5.14,5,9c0,5.25,7,13,7,13s7-7.75,7-13C19,5.14,15.86,2,12,2z M12,4c1.1,0,2,0.9,2,2c0,1.11-0.9,2-2,2 s-2-0.89-2-2C10,4.9,10.9,4,12,4z M12,14c-1.67,0-3.14-0.85-4-2.15c0.02-1.32,2.67-2.05,4-2.05s3.98,0.73,4,2.05 C15.14,13.15,13.67,14,12,14z"/></g></g></g></svg>'
}
/**
* Use this to show the bounding box for debug purposes
* @param {*} map instance of Mapbox map object
* @param {*} bbox bounding box
*/
function showBoundingBox(map, bbox) {
var bboxCoords = [map.unproject(bbox[0]), map.unproject(bbox[1])]
var poly = [
[bboxCoords[0].lng, bboxCoords[0].lat],
[bboxCoords[1].lng, bboxCoords[0].lat],
[bboxCoords[1].lng, bboxCoords[1].lat],
[bboxCoords[0].lng, bboxCoords[1].lat],
[bboxCoords[0].lng, bboxCoords[0].lat]
]
var number = Math.random()
map.addLayer({
'id': 'bbox' + number,
'type': 'fill',
'source': {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [poly]
}
}
},
'layout': {},
'paint': {
'fill-color': 'firebrick',
'fill-opacity': 0.5
}
});
}