mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-14 21:28:11 +02:00
WIP: Add OpenStreetCam support
todo: Need to figure out API issue with bbox, then imageviewer
This commit is contained in:
@@ -67,6 +67,46 @@
|
||||
}
|
||||
|
||||
|
||||
/* OpenStreetCam Image Layer */
|
||||
|
||||
.layer-openstreetcam-images {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group {
|
||||
pointer-events: visible;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group * {
|
||||
stroke-width: 1;
|
||||
stroke: #444;
|
||||
fill: #ffc600;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group:hover * {
|
||||
stroke-width: 1;
|
||||
stroke: #333;
|
||||
fill: #ff9900;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group.selected * {
|
||||
stroke-width: 2;
|
||||
stroke: #222;
|
||||
fill: #ff5800;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group:hover path.viewfield,
|
||||
.layer-openstreetcam-images .viewfield-group.selected path.viewfield,
|
||||
.layer-openstreetcam-images .viewfield-group path.viewfield {
|
||||
stroke-width: 0;
|
||||
fill-opacity: 0.6;
|
||||
}
|
||||
|
||||
|
||||
/* Mapillary viewer */
|
||||
#mly .domRenderer .TagSymbol {
|
||||
font-size: 10px;
|
||||
@@ -546,6 +546,11 @@ en:
|
||||
title: "Traffic Sign Overlay (Mapillary)"
|
||||
mapillary:
|
||||
view_on_mapillary: "View this image on Mapillary"
|
||||
openstreetcam_images:
|
||||
tooltip: "Street-level photos from OpenStreetCam"
|
||||
title: "Photo Overlay (OpenStreetCam)"
|
||||
openstreetcam:
|
||||
view_on_openstreetcam: "View this image on OpenStreetCam"
|
||||
help:
|
||||
title: "Help"
|
||||
key: H
|
||||
|
||||
Vendored
+7
@@ -670,6 +670,13 @@
|
||||
"mapillary": {
|
||||
"view_on_mapillary": "View this image on Mapillary"
|
||||
},
|
||||
"openstreetcam_images": {
|
||||
"tooltip": "Street-level photos from OpenStreetCam",
|
||||
"title": "Photo Overlay (OpenStreetCam)"
|
||||
},
|
||||
"openstreetcam": {
|
||||
"view_on_openstreetcam": "View this image on OpenStreetCam"
|
||||
},
|
||||
"help": {
|
||||
"title": "Help",
|
||||
"key": "H",
|
||||
|
||||
@@ -104,6 +104,11 @@ export function rendererBackground(context) {
|
||||
imageryUsed.push('Mapillary Signs');
|
||||
}
|
||||
|
||||
var openstreetcam_images = context.layers().layer('openstreetcam-images');
|
||||
if (openstreetcam_images && openstreetcam_images.enabled()) {
|
||||
imageryUsed.push('OpenStreetCam Images');
|
||||
}
|
||||
|
||||
context.history().imageryUsed(imageryUsed);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import serviceMapillary from './mapillary';
|
||||
import serviceNominatim from './nominatim';
|
||||
import serviceOpenstreetcam from './openstreetcam';
|
||||
import serviceOsm from './osm';
|
||||
import serviceTaginfo from './taginfo';
|
||||
import serviceWikidata from './wikidata';
|
||||
import serviceWikipedia from './wikipedia';
|
||||
|
||||
export var services = {
|
||||
mapillary: serviceMapillary,
|
||||
geocoder: serviceNominatim,
|
||||
mapillary: serviceMapillary,
|
||||
openstreetcam: serviceOpenstreetcam,
|
||||
osm: serviceOsm,
|
||||
taginfo: serviceTaginfo,
|
||||
wikidata: serviceWikidata,
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
import _filter from 'lodash-es/filter';
|
||||
import _find from 'lodash-es/find';
|
||||
import _flatten from 'lodash-es/flatten';
|
||||
import _forEach from 'lodash-es/forEach';
|
||||
import _map from 'lodash-es/map';
|
||||
import _some from 'lodash-es/some';
|
||||
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
import { request as d3_request } from 'd3-request';
|
||||
|
||||
import {
|
||||
select as d3_select,
|
||||
selectAll as d3_selectAll
|
||||
} from 'd3-selection';
|
||||
|
||||
import rbush from 'rbush';
|
||||
|
||||
import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile';
|
||||
import { geoExtent } from '../geo';
|
||||
import { utilQsString, utilRebind } from '../util';
|
||||
|
||||
|
||||
var apibase = 'http://openstreetcam.org',
|
||||
maxResults = 1000,
|
||||
tileZoom = 14,
|
||||
dispatch = d3_dispatch('loadedImages'),
|
||||
openstreetcamCache,
|
||||
openstreetcamImage;
|
||||
|
||||
|
||||
function abortRequest(i) {
|
||||
i.abort();
|
||||
}
|
||||
|
||||
|
||||
function nearNullIsland(x, y, z) {
|
||||
if (z >= 7) {
|
||||
var center = Math.pow(2, z - 1),
|
||||
width = Math.pow(2, z - 6),
|
||||
min = center - (width / 2),
|
||||
max = center + (width / 2) - 1;
|
||||
return x >= min && x <= max && y >= min && y <= max;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
function maxPageAtZoom(z) {
|
||||
if (z < 15) return 2;
|
||||
if (z === 15) return 5;
|
||||
if (z === 16) return 10;
|
||||
if (z === 17) return 20;
|
||||
if (z === 18) return 40;
|
||||
if (z > 18) return 80;
|
||||
}
|
||||
|
||||
|
||||
function getTiles(projection) {
|
||||
var s = projection.scale() * 2 * Math.PI,
|
||||
z = Math.max(Math.log(s) / Math.log(2) - 8, 0),
|
||||
ts = 256 * Math.pow(2, z - tileZoom),
|
||||
origin = [
|
||||
s / 2 - projection.translate()[0],
|
||||
s / 2 - projection.translate()[1]];
|
||||
|
||||
return d3_geoTile()
|
||||
.scaleExtent([tileZoom, tileZoom])
|
||||
.scale(s)
|
||||
.size(projection.clipExtent()[1])
|
||||
.translate(projection.translate())()
|
||||
.map(function(tile) {
|
||||
var x = tile[0] * ts - origin[0],
|
||||
y = tile[1] * ts - origin[1];
|
||||
|
||||
return {
|
||||
id: tile.toString(),
|
||||
xyz: tile,
|
||||
extent: geoExtent(
|
||||
projection.invert([x, y + ts]),
|
||||
projection.invert([x + ts, y])
|
||||
)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadTiles(which, url, projection) {
|
||||
var s = projection.scale() * 2 * Math.PI,
|
||||
currZoom = Math.floor(Math.max(Math.log(s) / Math.log(2) - 8, 0));
|
||||
|
||||
var tiles = getTiles(projection).filter(function(t) {
|
||||
return !nearNullIsland(t.xyz[0], t.xyz[1], t.xyz[2]);
|
||||
});
|
||||
|
||||
_filter(which.inflight, function(v, k) {
|
||||
var wanted = _find(tiles, function(tile) { return k === (tile.id + ',0'); });
|
||||
if (!wanted) delete which.inflight[k];
|
||||
return !wanted;
|
||||
}).map(abortRequest);
|
||||
|
||||
tiles.forEach(function(tile) {
|
||||
loadNextTilePage(which, currZoom, url, tile);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadNextTilePage(which, currZoom, url, tile) {
|
||||
var cache = openstreetcamCache[which];
|
||||
var bbox = tile.extent.bbox();
|
||||
var maxPages = maxPageAtZoom(currZoom);
|
||||
var nextPage = cache.nextPage[tile.id] || 1;
|
||||
var params = utilQsString({
|
||||
ipp: maxResults,
|
||||
page: nextPage,
|
||||
// client_id: clientId,
|
||||
bbTopLeft: [bbox.maxY, bbox.minX].join(','),
|
||||
bbBottomRight: [bbox.minY, bbox.maxX].join(',')
|
||||
}, true);
|
||||
|
||||
if (nextPage > maxPages) return;
|
||||
|
||||
var id = tile.id + ',' + String(nextPage);
|
||||
if (cache.loaded[id] || cache.inflight[id]) return;
|
||||
|
||||
cache.inflight[id] = d3_request(url)
|
||||
.mimeType('application/json')
|
||||
.post(params, function(err, data) {
|
||||
cache.loaded[id] = true;
|
||||
delete cache.inflight[id];
|
||||
if (err || !data.currentPageItems || !data.currentPageItems.length) return;
|
||||
|
||||
var features = data.currentPageItems.map(function(item) {
|
||||
var loc = [+item.lng, +item.lat],
|
||||
d;
|
||||
|
||||
if (which === 'images') {
|
||||
d = {
|
||||
loc: loc,
|
||||
key: item.id,
|
||||
ca: +item.heading,
|
||||
captured_at: item.date_added,
|
||||
};
|
||||
}
|
||||
return {
|
||||
minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
|
||||
};
|
||||
});
|
||||
|
||||
cache.rtree.load(features);
|
||||
|
||||
if (which === 'images') {
|
||||
dispatch.call('loadedImages');
|
||||
}
|
||||
|
||||
if (data.currentPageItems.length === maxResults) { // more pages to load
|
||||
cache.nextPage[tile.id] = nextPage + 1;
|
||||
loadNextTilePage(which, currZoom, url, tile);
|
||||
} else {
|
||||
cache.nextPage[tile.id] = Infinity; // no more pages to load
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// partition viewport into `psize` x `psize` regions
|
||||
function partitionViewport(psize, projection) {
|
||||
var dimensions = projection.clipExtent()[1];
|
||||
psize = psize || 16;
|
||||
var cols = d3_range(0, dimensions[0], psize),
|
||||
rows = d3_range(0, dimensions[1], psize),
|
||||
partitions = [];
|
||||
|
||||
rows.forEach(function(y) {
|
||||
cols.forEach(function(x) {
|
||||
var min = [x, y + psize],
|
||||
max = [x + psize, y];
|
||||
partitions.push(
|
||||
geoExtent(projection.invert(min), projection.invert(max)));
|
||||
});
|
||||
});
|
||||
|
||||
return partitions;
|
||||
}
|
||||
|
||||
|
||||
// no more than `limit` results per partition.
|
||||
function searchLimited(psize, limit, projection, rtree) {
|
||||
limit = limit || 3;
|
||||
|
||||
var partitions = partitionViewport(psize, projection);
|
||||
var results;
|
||||
|
||||
results = _flatten(_map(partitions, function(extent) {
|
||||
return rtree.search(extent.bbox())
|
||||
.slice(0, limit)
|
||||
.map(function(d) { return d.data; });
|
||||
}));
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default {
|
||||
|
||||
init: function() {
|
||||
if (!openstreetcamCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
this.event = utilRebind(this, dispatch, 'on');
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var cache = openstreetcamCache;
|
||||
|
||||
if (cache) {
|
||||
if (cache.images && cache.images.inflight) {
|
||||
_forEach(cache.images.inflight, abortRequest);
|
||||
}
|
||||
}
|
||||
|
||||
openstreetcamCache = {
|
||||
images: { inflight: {}, loaded: {}, nextPage: {}, rtree: rbush() }
|
||||
};
|
||||
|
||||
openstreetcamImage = null;
|
||||
},
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, openstreetcamCache.images.rtree);
|
||||
},
|
||||
|
||||
loadImages: function(projection) {
|
||||
var url = apibase + '/1.0/list/nearby-photos/';
|
||||
loadTiles('images', url, projection);
|
||||
},
|
||||
|
||||
|
||||
loadViewer: function(context) {
|
||||
// var that = this;
|
||||
// var wrap = d3_select('#content').selectAll('.openstreetcam-wrap')
|
||||
// .data([0]);
|
||||
|
||||
// var enter = wrap.enter()
|
||||
// .append('div')
|
||||
// .attr('class', 'openstreetcam-wrap')
|
||||
// .classed('al', true) // 'al'=left, 'ar'=right
|
||||
// .classed('hidden', true);
|
||||
|
||||
// enter
|
||||
// .append('button')
|
||||
// .attr('class', 'thumb-hide')
|
||||
// .on('click', function () { that.hideViewer(); })
|
||||
// .append('div')
|
||||
// .call(svgIcon('#icon-close'));
|
||||
|
||||
// enter
|
||||
// .append('div')
|
||||
// .attr('id', 'mly')
|
||||
// .attr('class', 'mly-wrapper')
|
||||
// .classed('active', false);
|
||||
},
|
||||
|
||||
|
||||
showViewer: function() {
|
||||
// d3_select('#content')
|
||||
// .selectAll('.openstreetcam-wrap')
|
||||
// .classed('hidden', false)
|
||||
// .selectAll('.mly-wrapper')
|
||||
// .classed('active', true);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
hideViewer: function() {
|
||||
// d3_select('#content')
|
||||
// .selectAll('.openstreetcam-wrap')
|
||||
// .classed('hidden', true)
|
||||
// .selectAll('.mly-wrapper')
|
||||
// .classed('active', false);
|
||||
|
||||
// d3_selectAll('.layer-openstreetcam-images .viewfield-group')
|
||||
// .classed('selected', false);
|
||||
|
||||
openstreetcamImage = null;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
updateViewer: function(imageKey, context) {
|
||||
if (!imageKey) return;
|
||||
|
||||
// if (!openstreetcamViewer) {
|
||||
// this.initViewer(imageKey, context);
|
||||
// } else {
|
||||
// openstreetcamViewer.moveToKey(imageKey);
|
||||
// }
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
selectedImage: function(imageKey) {
|
||||
if (!arguments.length) return openstreetcamImage;
|
||||
openstreetcamImage = imageKey;
|
||||
|
||||
// d3_selectAll('.layer-openstreetcam-images .viewfield-group')
|
||||
// .classed('selected', function(d) {
|
||||
// return d.key === imageKey;
|
||||
// });
|
||||
|
||||
// if (!imageKey) return this;
|
||||
|
||||
|
||||
// function localeTimestamp(s) {
|
||||
// if (!s) return null;
|
||||
// var d = new Date(s);
|
||||
// if (isNaN(d.getTime())) return null;
|
||||
// return d.toLocaleString(undefined, { timeZone: 'UTC' });
|
||||
// }
|
||||
|
||||
// var selected = d3_selectAll('.layer-openstreetcam-images .viewfield-group.selected');
|
||||
// if (selected.empty()) return this;
|
||||
|
||||
// var datum = selected.datum();
|
||||
// var timestamp = localeTimestamp(datum.captured_at);
|
||||
// var attribution = d3_select('.openstreetcam-js-dom .Attribution');
|
||||
// var capturedAt = attribution.selectAll('.captured-at');
|
||||
// if (capturedAt.empty()) {
|
||||
// attribution
|
||||
// .append('span')
|
||||
// .text('|');
|
||||
// capturedAt = attribution
|
||||
// .append('span')
|
||||
// .attr('class', 'captured-at');
|
||||
// }
|
||||
// capturedAt
|
||||
// .text(timestamp);
|
||||
|
||||
// this.updateDetections();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
cache: function(_) {
|
||||
if (!arguments.length) return openstreetcamCache;
|
||||
openstreetcamCache = _;
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -10,6 +10,7 @@ export { svgMapillaryImages } from './mapillary_images.js';
|
||||
export { svgMapillarySigns } from './mapillary_signs.js';
|
||||
export { svgMidpoints } from './midpoints.js';
|
||||
export { svgOneWaySegments } from './one_way_segments.js';
|
||||
export { svgOpenstreetcamImages } from './openstreetcam_images.js';
|
||||
export { svgOsm } from './osm.js';
|
||||
export { svgPath } from './path.js';
|
||||
export { svgPointTransform } from './point_transform.js';
|
||||
|
||||
@@ -10,6 +10,7 @@ import { svgDebug } from './debug';
|
||||
import { svgGpx } from './gpx';
|
||||
import { svgMapillaryImages } from './mapillary_images';
|
||||
import { svgMapillarySigns } from './mapillary_signs';
|
||||
import { svgOpenstreetcamImages } from './openstreetcam_images';
|
||||
import { svgOsm } from './osm';
|
||||
import { utilRebind } from '../util/rebind';
|
||||
import { utilGetDimensions, utilSetDimensions } from '../util/dimensions';
|
||||
@@ -23,6 +24,7 @@ export function svgLayers(projection, context) {
|
||||
{ id: 'gpx', layer: svgGpx(projection, context, dispatch) },
|
||||
{ id: 'mapillary-images', layer: svgMapillaryImages(projection, context, dispatch) },
|
||||
{ id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch) },
|
||||
{ id: 'openstreetcam-images', layer: svgOpenstreetcamImages(projection, context, dispatch) },
|
||||
{ id: 'debug', layer: svgDebug(projection, context, dispatch) }
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
import { svgPointTransform } from './point_transform';
|
||||
import { services } from '../services';
|
||||
|
||||
|
||||
export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
minViewfieldZoom = 17,
|
||||
layer = d3_select(null),
|
||||
_openstreetcam;
|
||||
|
||||
|
||||
function init() {
|
||||
if (svgOpenstreetcamImages.initialized) return; // run once
|
||||
svgOpenstreetcamImages.enabled = false;
|
||||
svgOpenstreetcamImages.initialized = true;
|
||||
}
|
||||
|
||||
|
||||
function getOpenstreetcam() {
|
||||
if (services.openstreetcam && !_openstreetcam) {
|
||||
_openstreetcam = services.openstreetcam;
|
||||
_openstreetcam.event.on('loadedImages', throttledRedraw);
|
||||
} else if (!services.openstreetcam && _openstreetcam) {
|
||||
_openstreetcam = null;
|
||||
}
|
||||
|
||||
return _openstreetcam;
|
||||
}
|
||||
|
||||
|
||||
function showLayer() {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (!openstreetcam) return;
|
||||
|
||||
openstreetcam.loadViewer(context);
|
||||
editOn();
|
||||
|
||||
layer
|
||||
.style('opacity', 0)
|
||||
.transition()
|
||||
.duration(250)
|
||||
.style('opacity', 1)
|
||||
.on('end', function () { dispatch.call('change'); });
|
||||
}
|
||||
|
||||
|
||||
function hideLayer() {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (openstreetcam) {
|
||||
openstreetcam.hideViewer();
|
||||
}
|
||||
|
||||
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 click(d) {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (!openstreetcam) return;
|
||||
|
||||
context.map().centerEase(d.loc);
|
||||
|
||||
openstreetcam
|
||||
.selectedImage(d.key, true)
|
||||
.updateViewer(d.key, context)
|
||||
.showViewer();
|
||||
}
|
||||
|
||||
|
||||
function transform(d) {
|
||||
var t = svgPointTransform(projection)(d);
|
||||
if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
var openstreetcam = getOpenstreetcam(),
|
||||
data = (openstreetcam ? openstreetcam.images(projection) : []),
|
||||
imageKey = openstreetcam ? openstreetcam.selectedImage() : null;
|
||||
|
||||
var markers = layer.selectAll('.viewfield-group')
|
||||
.data(data, function(d) { return d.key; });
|
||||
|
||||
markers.exit()
|
||||
.remove();
|
||||
|
||||
var enter = markers.enter()
|
||||
.append('g')
|
||||
.attr('class', 'viewfield-group')
|
||||
.classed('selected', function(d) { return d.key === imageKey; })
|
||||
.on('click', click);
|
||||
|
||||
markers = markers
|
||||
.merge(enter)
|
||||
.attr('transform', transform);
|
||||
|
||||
|
||||
var viewfields = markers.selectAll('.viewfield')
|
||||
.data(~~context.map().zoom() >= minViewfieldZoom ? [0] : []);
|
||||
|
||||
viewfields.exit()
|
||||
.remove();
|
||||
|
||||
viewfields.enter()
|
||||
.append('path')
|
||||
.attr('class', 'viewfield')
|
||||
.attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
|
||||
.attr('d', 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z');
|
||||
|
||||
markers.selectAll('circle')
|
||||
.data([0])
|
||||
.enter()
|
||||
.append('circle')
|
||||
.attr('dx', '0')
|
||||
.attr('dy', '0')
|
||||
.attr('r', '6');
|
||||
}
|
||||
|
||||
|
||||
function drawImages(selection) {
|
||||
var enabled = svgOpenstreetcamImages.enabled,
|
||||
openstreetcam = getOpenstreetcam();
|
||||
|
||||
layer = selection.selectAll('.layer-openstreetcam-images')
|
||||
.data(openstreetcam ? [0] : []);
|
||||
|
||||
layer.exit()
|
||||
.remove();
|
||||
|
||||
layer = layer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-openstreetcam-images')
|
||||
.style('display', enabled ? 'block' : 'none')
|
||||
.merge(layer);
|
||||
|
||||
if (enabled) {
|
||||
if (openstreetcam && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
update();
|
||||
openstreetcam.loadImages(projection);
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
drawImages.enabled = function(_) {
|
||||
if (!arguments.length) return svgOpenstreetcamImages.enabled;
|
||||
svgOpenstreetcamImages.enabled = _;
|
||||
if (svgOpenstreetcamImages.enabled) {
|
||||
showLayer();
|
||||
} else {
|
||||
hideLayer();
|
||||
}
|
||||
dispatch.call('change');
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
drawImages.supported = function() {
|
||||
return !!getOpenstreetcam();
|
||||
};
|
||||
|
||||
|
||||
init();
|
||||
return drawImages;
|
||||
}
|
||||
+39
-87
@@ -80,113 +80,65 @@ export function uiMapData(context) {
|
||||
}
|
||||
|
||||
|
||||
function clickMapillaryImages() {
|
||||
toggleLayer('mapillary-images');
|
||||
if (!showsLayer('mapillary-images')) {
|
||||
setLayer('mapillary-signs', false);
|
||||
function drawPhotoItems(selection) {
|
||||
var photoKeys = ['mapillary-images', 'mapillary-signs', 'openstreetcam-images'];
|
||||
var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
|
||||
var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
|
||||
|
||||
function layerSupported(d) {
|
||||
return d.layer && d.layer.supported();
|
||||
}
|
||||
function layerEnabled(d) {
|
||||
return layerSupported(d) && d.layer.enabled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function clickMapillarySigns() {
|
||||
toggleLayer('mapillary-signs');
|
||||
}
|
||||
|
||||
|
||||
function drawMapillaryItems(selection) {
|
||||
var mapillaryImages = layers.layer('mapillary-images'),
|
||||
mapillarySigns = layers.layer('mapillary-signs'),
|
||||
supportsMapillaryImages = mapillaryImages && mapillaryImages.supported(),
|
||||
supportsMapillarySigns = mapillarySigns && mapillarySigns.supported(),
|
||||
showsMapillaryImages = supportsMapillaryImages && mapillaryImages.enabled(),
|
||||
showsMapillarySigns = supportsMapillarySigns && mapillarySigns.enabled();
|
||||
|
||||
var mapillaryList = selection
|
||||
.selectAll('.layer-list-mapillary')
|
||||
var ul = selection
|
||||
.selectAll('.layer-list-photos')
|
||||
.data([0]);
|
||||
|
||||
mapillaryList = mapillaryList.enter()
|
||||
ul = ul.enter()
|
||||
.append('ul')
|
||||
.attr('class', 'layer-list layer-list-mapillary')
|
||||
.merge(mapillaryList);
|
||||
.attr('class', 'layer-list layer-list-photos')
|
||||
.merge(ul);
|
||||
|
||||
var li = ul.selectAll('.list-item-photos')
|
||||
.data(data);
|
||||
|
||||
var mapillaryImageLayerItem = mapillaryList
|
||||
.selectAll('.list-item-mapillary-images')
|
||||
.data(supportsMapillaryImages ? [0] : []);
|
||||
|
||||
mapillaryImageLayerItem.exit()
|
||||
li.exit()
|
||||
.remove();
|
||||
|
||||
var enterImages = mapillaryImageLayerItem.enter()
|
||||
var liEnter = li.enter()
|
||||
.append('li')
|
||||
.attr('class', 'list-item-mapillary-images');
|
||||
.attr('class', function(d) { return 'list-item-photos list-item-' + d.id; });
|
||||
|
||||
var labelImages = enterImages
|
||||
var labelEnter = liEnter
|
||||
.append('label')
|
||||
.call(tooltip()
|
||||
.title(t('mapillary_images.tooltip'))
|
||||
.placement('top'));
|
||||
.each(function(d) {
|
||||
d3_select(this)
|
||||
.call(tooltip()
|
||||
.title(t(d.id.replace('-', '_') + '.tooltip'))
|
||||
.placement('top')
|
||||
);
|
||||
});
|
||||
|
||||
labelImages
|
||||
labelEnter
|
||||
.append('input')
|
||||
.attr('type', 'checkbox')
|
||||
.on('change', clickMapillaryImages);
|
||||
.on('change', function(d) { toggleLayer(d.id); });
|
||||
|
||||
labelImages
|
||||
labelEnter
|
||||
.append('span')
|
||||
.text(t('mapillary_images.title'));
|
||||
.text(function(d) { return t(d.id.replace('-', '_') + '.title'); });
|
||||
|
||||
|
||||
var mapillarySignLayerItem = mapillaryList
|
||||
.selectAll('.list-item-mapillary-signs')
|
||||
.data(supportsMapillarySigns ? [0] : []);
|
||||
// Update
|
||||
li = li
|
||||
.merge(liEnter);
|
||||
|
||||
mapillarySignLayerItem.exit()
|
||||
.remove();
|
||||
|
||||
var enterSigns = mapillarySignLayerItem.enter()
|
||||
.append('li')
|
||||
.attr('class', 'list-item-mapillary-signs');
|
||||
|
||||
var labelSigns = enterSigns
|
||||
.append('label')
|
||||
.call(tooltip()
|
||||
.title(t('mapillary_signs.tooltip'))
|
||||
.placement('top'));
|
||||
|
||||
labelSigns
|
||||
.append('input')
|
||||
.attr('type', 'checkbox')
|
||||
.on('change', clickMapillarySigns);
|
||||
|
||||
labelSigns
|
||||
.append('span')
|
||||
.text(t('mapillary_signs.title'));
|
||||
|
||||
|
||||
// Updates
|
||||
mapillaryImageLayerItem = mapillaryImageLayerItem
|
||||
.merge(enterImages);
|
||||
|
||||
mapillaryImageLayerItem
|
||||
.classed('active', showsMapillaryImages)
|
||||
li
|
||||
.classed('active', layerEnabled)
|
||||
.selectAll('input')
|
||||
.property('checked', showsMapillaryImages);
|
||||
|
||||
|
||||
mapillarySignLayerItem = mapillarySignLayerItem
|
||||
.merge(enterSigns);
|
||||
|
||||
mapillarySignLayerItem
|
||||
.classed('active', showsMapillarySigns)
|
||||
.selectAll('input')
|
||||
.property('disabled', !showsMapillaryImages)
|
||||
.property('checked', showsMapillarySigns);
|
||||
|
||||
mapillarySignLayerItem
|
||||
.selectAll('label')
|
||||
.classed('deemphasize', !showsMapillaryImages);
|
||||
.property('checked', layerEnabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -377,7 +329,7 @@ export function uiMapData(context) {
|
||||
function update() {
|
||||
dataLayerContainer
|
||||
.call(drawOsmItem)
|
||||
.call(drawMapillaryItems)
|
||||
.call(drawPhotoItems)
|
||||
.call(drawGpxItem);
|
||||
|
||||
fillList
|
||||
|
||||
@@ -26,12 +26,13 @@ describe('iD.svgLayers', function () {
|
||||
it('creates default data layers', function () {
|
||||
container.call(iD.svgLayers(projection, context));
|
||||
var nodes = container.selectAll('svg .data-layer').nodes();
|
||||
expect(nodes.length).to.eql(5);
|
||||
expect(nodes.length).to.eql(6);
|
||||
expect(d3.select(nodes[0]).classed('data-layer-osm')).to.be.true;
|
||||
expect(d3.select(nodes[1]).classed('data-layer-gpx')).to.be.true;
|
||||
expect(d3.select(nodes[2]).classed('data-layer-mapillary-images')).to.be.true;
|
||||
expect(d3.select(nodes[3]).classed('data-layer-mapillary-signs')).to.be.true;
|
||||
expect(d3.select(nodes[4]).classed('data-layer-debug')).to.be.true;
|
||||
expect(d3.select(nodes[4]).classed('data-layer-openstreetcam-images')).to.be.true;
|
||||
expect(d3.select(nodes[5]).classed('data-layer-debug')).to.be.true;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user