mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 05:30:35 +02:00
first commit
This commit is contained in:
@@ -251,7 +251,8 @@ export function rendererBackground(context) {
|
||||
mapillary: 'Mapillary Images',
|
||||
'mapillary-map-features': 'Mapillary Map Features',
|
||||
'mapillary-signs': 'Mapillary Signs',
|
||||
kartaview: 'KartaView Images'
|
||||
kartaview: 'KartaView Images',
|
||||
mapilio: 'Mapilio Images'
|
||||
};
|
||||
|
||||
for (let layerID in photoOverlayLayers) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { utilQsString, utilStringQs } from '../util';
|
||||
|
||||
export function rendererPhotos(context) {
|
||||
var dispatch = d3_dispatch('change');
|
||||
var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'kartaview'];
|
||||
var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'kartaview', 'mapilio'];
|
||||
var _allPhotoTypes = ['flat', 'panoramic'];
|
||||
var _shownPhotoTypes = _allPhotoTypes.slice(); // shallow copy
|
||||
var _dateFilters = ['fromDate', 'toDate'];
|
||||
|
||||
@@ -13,6 +13,7 @@ import serviceTaginfo from './taginfo';
|
||||
import serviceVectorTile from './vector_tile';
|
||||
import serviceWikidata from './wikidata';
|
||||
import serviceWikipedia from './wikipedia';
|
||||
import serviceMapilio from './mapilio';
|
||||
|
||||
|
||||
export let services = {
|
||||
@@ -30,7 +31,8 @@ export let services = {
|
||||
taginfo: serviceTaginfo,
|
||||
vectorTile: serviceVectorTile,
|
||||
wikidata: serviceWikidata,
|
||||
wikipedia: serviceWikipedia
|
||||
wikipedia: serviceWikipedia,
|
||||
mapilio: serviceMapilio
|
||||
};
|
||||
|
||||
export {
|
||||
@@ -48,5 +50,6 @@ export {
|
||||
serviceTaginfo,
|
||||
serviceVectorTile,
|
||||
serviceWikidata,
|
||||
serviceWikipedia
|
||||
serviceWikipedia,
|
||||
serviceMapilio
|
||||
};
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
|
||||
import Protobuf from 'pbf';
|
||||
import RBush from 'rbush';
|
||||
import { VectorTile } from '@mapbox/vector-tile';
|
||||
|
||||
import { utilRebind, utilTiler } from '../util';
|
||||
|
||||
const baseTileUrl = 'https://geo.mapilio.com/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=mapilio:';
|
||||
const pointsTileUrl = `${baseTileUrl}points_mapilio_map&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&FORMAT=application/vnd.mapbox-vector-tile&TILECOL={x}&TILEROW={y}`;
|
||||
|
||||
const minZoom = 14;
|
||||
const dispatch = d3_dispatch('change', 'loadedImages', 'loadedSigns', 'loadedMapFeatures', 'bearingChanged', 'imageChanged');
|
||||
|
||||
let _mlyActiveImage;
|
||||
let _mlyCache;
|
||||
|
||||
|
||||
// Load all data for the specified type from Mapilio vector tiles
|
||||
function loadTiles(which, url, maxZoom, projection) {
|
||||
const tiler = utilTiler().zoomExtent([minZoom, maxZoom]).skipNullIsland(true);
|
||||
const tiles = tiler.getTiles(projection);
|
||||
|
||||
tiles.forEach(function(tile) {
|
||||
loadTile(which, url, tile);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Load all data for the specified type from one vector tile
|
||||
function loadTile(which, url, tile) {
|
||||
const cache = _mlyCache.requests;
|
||||
const tileId = `${tile.id}-${which}`;
|
||||
if (cache.loaded[tileId] || cache.inflight[tileId]) return;
|
||||
const controller = new AbortController();
|
||||
cache.inflight[tileId] = controller;
|
||||
const requestUrl = url.replace('{x}', tile.xyz[0])
|
||||
.replace('{y}', tile.xyz[1])
|
||||
.replace('{z}', tile.xyz[2]);
|
||||
|
||||
fetch(requestUrl, { signal: controller.signal })
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.status + ' ' + response.statusText);
|
||||
}
|
||||
cache.loaded[tileId] = true;
|
||||
delete cache.inflight[tileId];
|
||||
return response.arrayBuffer();
|
||||
})
|
||||
.then(function(data) {
|
||||
if (!data) {
|
||||
throw new Error('No Data');
|
||||
}
|
||||
|
||||
loadTileDataToCache(data, tile, which);
|
||||
|
||||
if (which === 'images') {
|
||||
dispatch.call('loadedImages');
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
cache.loaded[tileId] = true;
|
||||
delete cache.inflight[tileId];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Load the data from the vector tile into cache
|
||||
function loadTileDataToCache(data, tile, which) {
|
||||
const vectorTile = new VectorTile(new Protobuf(data));
|
||||
let features,
|
||||
cache,
|
||||
layer,
|
||||
i,
|
||||
feature,
|
||||
loc,
|
||||
d;
|
||||
if (vectorTile.layers.hasOwnProperty('points_mapilio_map')) {
|
||||
features = [];
|
||||
cache = _mlyCache.images;
|
||||
layer = vectorTile.layers.points_mapilio_map;
|
||||
|
||||
for (i = 0; i < layer.length; i++) {
|
||||
feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
|
||||
loc = feature.geometry.coordinates;
|
||||
d = {
|
||||
loc: loc,
|
||||
captured_at: feature.properties.captured_at,
|
||||
created_at: feature.properties.created_at,
|
||||
id: feature.properties.id,
|
||||
sequence_id: feature.properties.sequence_uuid,
|
||||
};
|
||||
cache.forImageId[d.id] = d;
|
||||
features.push({
|
||||
minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
|
||||
});
|
||||
}
|
||||
if (cache.rtree) {
|
||||
cache.rtree.load(features);
|
||||
}
|
||||
}
|
||||
|
||||
if (vectorTile.layers.hasOwnProperty('sequence')) {
|
||||
features = [];
|
||||
cache = _mlyCache.sequences;
|
||||
layer = vectorTile.layers.sequence;
|
||||
|
||||
for (i = 0; i < layer.length; i++) {
|
||||
feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
|
||||
if (cache.lineString[feature.properties.id]) {
|
||||
cache.lineString[feature.properties.id].push(feature);
|
||||
} else {
|
||||
cache.lineString[feature.properties.id] = [feature];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (vectorTile.layers.hasOwnProperty('point')) {
|
||||
features = [];
|
||||
cache = _mlyCache[which];
|
||||
layer = vectorTile.layers.point;
|
||||
|
||||
for (i = 0; i < layer.length; i++) {
|
||||
feature = layer.feature(i).toGeoJSON(tile.xyz[0], tile.xyz[1], tile.xyz[2]);
|
||||
loc = feature.geometry.coordinates;
|
||||
|
||||
d = {
|
||||
loc: loc,
|
||||
id: feature.properties.id,
|
||||
first_seen_at: feature.properties.first_seen_at,
|
||||
last_seen_at: feature.properties.last_seen_at,
|
||||
value: feature.properties.value
|
||||
};
|
||||
features.push({
|
||||
minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
|
||||
});
|
||||
}
|
||||
if (cache.rtree) {
|
||||
cache.rtree.load(features);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
// Initialize Mapilio
|
||||
init: function() {
|
||||
if (!_mlyCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this.event = utilRebind(this, dispatch, 'on');
|
||||
},
|
||||
|
||||
// Reset cache and state
|
||||
reset: function() {
|
||||
if (_mlyCache) {
|
||||
Object.values(_mlyCache.requests.inflight).forEach(function(request) { request.abort(); });
|
||||
}
|
||||
|
||||
_mlyCache = {
|
||||
images: { rtree: new RBush(), forImageId: {} },
|
||||
sequences: { rtree: new RBush(), lineString: {} },
|
||||
requests: { loaded: {}, inflight: {} }
|
||||
};
|
||||
|
||||
_mlyActiveImage = null;
|
||||
},
|
||||
|
||||
|
||||
// Load images in the visible area
|
||||
loadImages: function(projection) {
|
||||
loadTiles('images', pointsTileUrl, 14, projection);
|
||||
},
|
||||
|
||||
|
||||
// Update the currently highlighted sequence and selected bubble.
|
||||
setStyles: function(context, hovered) {
|
||||
const hoveredImageId = hovered && hovered.id;
|
||||
const hoveredSequenceId = hovered && hovered.sequence_id;
|
||||
const selectedSequenceId = _mlyActiveImage && _mlyActiveImage.sequence_id;
|
||||
|
||||
context.container().selectAll('.layer-mapilio .viewfield-group')
|
||||
.classed('highlighted', function(d) { return (d.sequence_id === selectedSequenceId) || (d.id === hoveredImageId); })
|
||||
.classed('hovered', function(d) { return d.id === hoveredImageId; });
|
||||
|
||||
context.container().selectAll('.layer-mapilio .sequence')
|
||||
.classed('highlighted', function(d) { return d.properties.id === hoveredSequenceId; })
|
||||
.classed('currentView', function(d) { return d.properties.id === selectedSequenceId; });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
// Return the current cache
|
||||
cache: function() {
|
||||
return _mlyCache;
|
||||
}
|
||||
};
|
||||
@@ -27,3 +27,4 @@ export { svgTagPattern } from './tag_pattern.js';
|
||||
export { svgTouch } from './touch.js';
|
||||
export { svgTurns } from './turns.js';
|
||||
export { svgVertices } from './vertices.js';
|
||||
export { svgMapilioImages } from './mapilio_images.js';
|
||||
|
||||
@@ -13,6 +13,7 @@ import { svgMapillaryPosition } from './mapillary_position';
|
||||
import { svgMapillarySigns } from './mapillary_signs';
|
||||
import { svgMapillaryMapFeatures } from './mapillary_map_features';
|
||||
import { svgKartaviewImages } from './kartaview_images';
|
||||
import { svgMapilioImages } from './mapilio_images';
|
||||
import { svgOsm } from './osm';
|
||||
import { svgNotes } from './notes';
|
||||
import { svgTouch } from './touch';
|
||||
@@ -38,7 +39,8 @@ export function svgLayers(projection, context) {
|
||||
{ id: 'kartaview', layer: svgKartaviewImages(projection, context, dispatch) },
|
||||
{ id: 'debug', layer: svgDebug(projection, context, dispatch) },
|
||||
{ id: 'geolocate', layer: svgGeolocate(projection, context, dispatch) },
|
||||
{ id: 'touch', layer: svgTouch(projection, context, dispatch) }
|
||||
{ id: 'touch', layer: svgTouch(projection, context, dispatch) },
|
||||
{ id: 'mapilio', layer: svgMapilioImages(projection, context, dispatch) }
|
||||
];
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
import { services } from '../services';
|
||||
|
||||
|
||||
export function svgMapilioImages(projection, context, dispatch) {
|
||||
const throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
|
||||
const minZoom = 12;
|
||||
let layer = d3_select(null);
|
||||
let _mapilio;
|
||||
|
||||
|
||||
function init() {
|
||||
if (svgMapilioImages.initialized) return;
|
||||
svgMapilioImages.enabled = false;
|
||||
svgMapilioImages.initialized = true;
|
||||
}
|
||||
|
||||
|
||||
function getService() {
|
||||
if (services.mapilio && !_mapilio) {
|
||||
_mapilio = services.mapilio;
|
||||
_mapilio.event.on('loadedImages', throttledRedraw);
|
||||
} else if (!services.mapilio && _mapilio) {
|
||||
_mapilio = null;
|
||||
}
|
||||
|
||||
return _mapilio;
|
||||
}
|
||||
|
||||
|
||||
function showLayer() {
|
||||
const service = getService();
|
||||
if (!service) return;
|
||||
|
||||
editOn();
|
||||
|
||||
layer
|
||||
.style('opacity', 0)
|
||||
.transition()
|
||||
.duration(250)
|
||||
.style('opacity', 1)
|
||||
.on('end', function () { dispatch.call('change'); });
|
||||
}
|
||||
|
||||
|
||||
function hideLayer() {
|
||||
throttledRedraw.cancel();
|
||||
|
||||
layer
|
||||
.transition()
|
||||
.duration(250)
|
||||
.style('opacity', 0)
|
||||
.on('end', editOff);
|
||||
}
|
||||
|
||||
|
||||
function editOn() {
|
||||
layer.style('display', 'block');
|
||||
}
|
||||
|
||||
|
||||
function editOff() {
|
||||
layer.selectAll('.viewfield-group').remove();
|
||||
layer.style('display', 'none');
|
||||
}
|
||||
|
||||
|
||||
function drawImages(selection) {
|
||||
const enabled = svgMapilioImages.enabled;
|
||||
const service = getService();
|
||||
|
||||
layer = selection.selectAll('.layer-mapilio')
|
||||
.data(service ? [0] : []);
|
||||
|
||||
layer.exit()
|
||||
.remove();
|
||||
|
||||
const layerEnter = layer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-mapilio')
|
||||
.style('display', enabled ? 'block' : 'none');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'sequences');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'markers');
|
||||
|
||||
layer = layerEnter
|
||||
.merge(layer);
|
||||
|
||||
if (enabled) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
service.loadImages(projection);
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drawImages.enabled = function(_) {
|
||||
if (!arguments.length) return svgMapilioImages.enabled;
|
||||
svgMapilioImages.enabled = _;
|
||||
if (svgMapilioImages.enabled) {
|
||||
showLayer();
|
||||
context.photos().on('change.mapilio_images', null);
|
||||
} else {
|
||||
hideLayer();
|
||||
context.photos().on('change.mapilio_images', null);
|
||||
}
|
||||
dispatch.call('change');
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
drawImages.supported = function() {
|
||||
return !!getService();
|
||||
};
|
||||
|
||||
|
||||
init();
|
||||
return drawImages;
|
||||
}
|
||||
Reference in New Issue
Block a user