mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-16 13:59:27 +02:00
Merge pull request #4499 from openstreetmap/openstreetcam
OpenStreetCam Support!
This commit is contained in:
@@ -1,115 +0,0 @@
|
||||
/* Mapillary Image Layer */
|
||||
|
||||
.layer-mapillary-images {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group {
|
||||
pointer-events: visible;
|
||||
cursor: pointer; /* Opera */
|
||||
cursor: url(img/cursor-select-mapillary.png) 6 1, pointer; /* FF */
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group * {
|
||||
stroke-width: 1;
|
||||
stroke: #444;
|
||||
fill: #ffc600;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group:hover * {
|
||||
stroke-width: 1;
|
||||
stroke: #333;
|
||||
fill: #ff9900;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group.selected * {
|
||||
stroke-width: 2;
|
||||
stroke: #222;
|
||||
fill: #ff5800;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group:hover path.viewfield,
|
||||
.layer-mapillary-images .viewfield-group.selected path.viewfield,
|
||||
.layer-mapillary-images .viewfield-group path.viewfield {
|
||||
stroke-width: 0;
|
||||
fill-opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Mapillary Sign Layer */
|
||||
|
||||
.layer-mapillary-signs {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign .icon-sign-body {
|
||||
min-width: 20px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
outline: 2px solid transparent;
|
||||
pointer-events: visible;
|
||||
cursor: pointer; /* Opera */
|
||||
cursor: url(img/cursor-select-mapillary.png) 6 1, pointer; /* FF */
|
||||
z-index: 70;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign:hover .icon-sign-body {
|
||||
outline: 2px solid rgba(255,198,0,0.8);
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign.selected .icon-sign-body {
|
||||
outline: 2px solid rgba(255,0,0,0.8);
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
|
||||
/* Mapillary viewer */
|
||||
#mly .domRenderer .TagSymbol {
|
||||
font-size: 10px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
top: -25px;
|
||||
}
|
||||
|
||||
#mly .domRenderer .Attribution {
|
||||
width: 100%;
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.mapillary-wrap {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
width: 330px;
|
||||
height: 250px;
|
||||
padding: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.mapillary-wrap.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.mapillary-wrap button.thumb-hide {
|
||||
border-radius: 0;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.mly-wrapper {
|
||||
visibility: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mly-wrapper.active {
|
||||
visibility: visible;
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/* photo viewer div */
|
||||
#photoviewer {
|
||||
position: absolute;
|
||||
bottom: 30px;
|
||||
width: 330px;
|
||||
height: 250px;
|
||||
padding: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
#photoviewer button.thumb-hide {
|
||||
border-radius: 0;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.photo-wrapper,
|
||||
.photo-wrapper img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewfield-group {
|
||||
pointer-events: visible;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.viewfield-group * {
|
||||
stroke-width: 1;
|
||||
stroke: #444;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.viewfield-group.selected * {
|
||||
stroke-width: 2;
|
||||
stroke: #222;
|
||||
fill: #ff5800 !important;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.viewfield-group:hover * {
|
||||
stroke-width: 1;
|
||||
stroke: #333;
|
||||
fill: #ff9900 !important;
|
||||
z-index: 70;
|
||||
}
|
||||
|
||||
.viewfield-group:hover path.viewfield,
|
||||
.viewfield-group.selected path.viewfield,
|
||||
.viewfield-group path.viewfield {
|
||||
stroke-width: 0;
|
||||
fill-opacity: 0.6;
|
||||
}
|
||||
|
||||
.sequence {
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
|
||||
/* Mapillary Image Layer */
|
||||
.layer-mapillary-images {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .viewfield-group * {
|
||||
fill: #55ff22;
|
||||
}
|
||||
|
||||
.layer-mapillary-images .sequence {
|
||||
stroke: #55ff22;
|
||||
}
|
||||
|
||||
|
||||
/* Mapillary Sign Layer */
|
||||
.layer-mapillary-signs {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign .icon-sign-body {
|
||||
min-width: 20px;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
outline: 2px solid transparent;
|
||||
pointer-events: visible;
|
||||
cursor: pointer; /* Opera */
|
||||
cursor: url(img/cursor-select-mapillary.png) 6 1, pointer; /* FF */
|
||||
z-index: 70;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign:hover .icon-sign-body {
|
||||
outline: 2px solid rgba(255,198,0,0.8);
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
.layer-mapillary-signs .icon-sign.selected .icon-sign-body {
|
||||
outline: 2px solid rgba(255,0,0,0.8);
|
||||
z-index: 80;
|
||||
}
|
||||
|
||||
|
||||
/* OpenStreetCam Image Layer */
|
||||
.layer-openstreetcam-images {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .viewfield-group * {
|
||||
fill: #77ddff;
|
||||
}
|
||||
|
||||
.layer-openstreetcam-images .sequence {
|
||||
stroke: #77ddff;
|
||||
}
|
||||
|
||||
|
||||
/* Mapillary viewer */
|
||||
#mly .domRenderer .TagSymbol {
|
||||
font-size: 10px;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
top: -25px;
|
||||
}
|
||||
|
||||
#mly .domRenderer .Attribution {
|
||||
width: 100%;
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
||||
/* OpenStreetCam viewer */
|
||||
.osc-wrapper {
|
||||
position: relative;
|
||||
background-color: #000;
|
||||
background-image: url(img/loader-black.gif);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.osc-wrapper .osc-attribution {
|
||||
width: 100%;
|
||||
font-size: 10px;
|
||||
text-align: right;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 4px 2px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.osc-attribution a,
|
||||
.osc-attribution a:visited,
|
||||
.osc-attribution span {
|
||||
padding: 4px 2px;
|
||||
color: #fff;
|
||||
}
|
||||
.osc-attribution a:active,
|
||||
.osc-attribution a:hover {
|
||||
color: #77ddff;
|
||||
}
|
||||
|
||||
.osc-controls-wrap {
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.osc-controls {
|
||||
display: inline-block;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.osc-controls button {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
background: rgba(0,0,0,0.65);
|
||||
color: #eee;
|
||||
border-radius: 0;
|
||||
}
|
||||
.osc-controls button:first-of-type {
|
||||
border-radius: 3px 0 0 3px;
|
||||
}
|
||||
.osc-controls button:last-of-type {
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
.osc-controls button:hover,
|
||||
.osc-controls button:active,
|
||||
.osc-controls button:focus {
|
||||
background: rgba(0,0,0,0.85);
|
||||
color: #fff;
|
||||
}
|
||||
@@ -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,
|
||||
@@ -17,6 +19,7 @@ export var services = {
|
||||
export {
|
||||
serviceMapillary,
|
||||
serviceNominatim,
|
||||
serviceOpenstreetcam,
|
||||
serviceOsm,
|
||||
serviceTaginfo,
|
||||
serviceWikidata,
|
||||
|
||||
@@ -24,7 +24,6 @@ import rbush from 'rbush';
|
||||
|
||||
import { d3geoTile as d3_geoTile } from '../lib/d3.geo.tile';
|
||||
import { geoExtent } from '../geo';
|
||||
import { svgIcon } from '../svg';
|
||||
import { utilDetect } from '../util/detect';
|
||||
import { utilQsString, utilRebind } from '../util';
|
||||
|
||||
@@ -166,6 +165,15 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
captured_at: feature.properties.captured_at,
|
||||
pano: feature.properties.pano
|
||||
};
|
||||
|
||||
} else if (which === 'sequences') {
|
||||
var sk = feature.properties.key;
|
||||
cache.lineString[sk] = feature; // cache sequence_key -> linestring
|
||||
feature.properties.coordinateProperties.image_keys.forEach(function(ik) {
|
||||
cache.forImage[ik] = sk; // cache image_key -> sequence_key
|
||||
});
|
||||
return false; // nothing to actually insert
|
||||
|
||||
} else if (which === 'objects') {
|
||||
d = {
|
||||
loc: loc,
|
||||
@@ -191,11 +199,11 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
return {
|
||||
minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d
|
||||
};
|
||||
});
|
||||
}).filter(Boolean);
|
||||
|
||||
cache.rtree.load(features);
|
||||
|
||||
if (which === 'images') {
|
||||
if (which === 'images' || which === 'sequences') {
|
||||
dispatch.call('loadedImages');
|
||||
} else if (which === 'objects') {
|
||||
dispatch.call('loadedSigns');
|
||||
@@ -304,11 +312,15 @@ export default {
|
||||
if (cache.objects && cache.objects.inflight) {
|
||||
_forEach(cache.objects.inflight, abortRequest);
|
||||
}
|
||||
if (cache.sequences && cache.sequences.inflight) {
|
||||
_forEach(cache.sequences.inflight, abortRequest);
|
||||
}
|
||||
}
|
||||
|
||||
mapillaryCache = {
|
||||
images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() },
|
||||
objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() },
|
||||
objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() },
|
||||
sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImage: {}, lineString: {} },
|
||||
detections: {}
|
||||
};
|
||||
|
||||
@@ -329,6 +341,29 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
sequences: function(projection) {
|
||||
var viewport = projection.clipExtent();
|
||||
var min = [viewport[0][0], viewport[1][1]];
|
||||
var max = [viewport[1][0], viewport[0][1]];
|
||||
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
|
||||
var sequenceKeys = {};
|
||||
|
||||
// all sequences for images in viewport
|
||||
mapillaryCache.images.rtree.search(bbox)
|
||||
.forEach(function(d) {
|
||||
var sk = mapillaryCache.sequences.forImage[d.data.key];
|
||||
if (sk) {
|
||||
sequenceKeys[sk] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Return linestrings for the sequences
|
||||
return Object.keys(sequenceKeys).map(function(sk) {
|
||||
return mapillaryCache.sequences.lineString[sk];
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
signsSupported: function() {
|
||||
var detected = utilDetect();
|
||||
if (detected.ie) return false;
|
||||
@@ -355,8 +390,8 @@ export default {
|
||||
|
||||
|
||||
loadImages: function(projection) {
|
||||
var url = apibase + 'images?';
|
||||
loadTiles('images', url, projection);
|
||||
loadTiles('images', apibase + 'images?', projection);
|
||||
loadTiles('sequences', apibase + 'sequences?', projection);
|
||||
},
|
||||
|
||||
|
||||
@@ -377,28 +412,14 @@ export default {
|
||||
|
||||
|
||||
loadViewer: function(context) {
|
||||
var that = this;
|
||||
var wrap = d3_select('#content').selectAll('.mapillary-wrap')
|
||||
.data([0]);
|
||||
|
||||
var enter = wrap.enter()
|
||||
.append('div')
|
||||
.attr('class', 'mapillary-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
|
||||
// add mly-wrapper for viewer-js
|
||||
d3_select('#photoviewer').selectAll('.mly-wrapper')
|
||||
.data([0])
|
||||
.enter()
|
||||
.append('div')
|
||||
.attr('id', 'mly')
|
||||
.attr('class', 'mly-wrapper')
|
||||
.classed('active', false);
|
||||
.attr('class', 'photo-wrapper mly-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
// load mapillary-viewercss
|
||||
d3_select('head').selectAll('#mapillary-viewercss')
|
||||
@@ -420,22 +441,32 @@ export default {
|
||||
|
||||
|
||||
showViewer: function() {
|
||||
d3_select('#content')
|
||||
.selectAll('.mapillary-wrap')
|
||||
.classed('hidden', false)
|
||||
.selectAll('.mly-wrapper')
|
||||
.classed('active', true);
|
||||
var wrap = d3_select('#photoviewer')
|
||||
.classed('hide', false);
|
||||
|
||||
var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size();
|
||||
|
||||
if (isHidden) {
|
||||
wrap
|
||||
.selectAll('.photo-wrapper:not(.mly-wrapper)')
|
||||
.classed('hide', true);
|
||||
|
||||
wrap
|
||||
.selectAll('.photo-wrapper.mly-wrapper')
|
||||
.classed('hide', false);
|
||||
|
||||
mapillaryViewer.resize();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
hideViewer: function() {
|
||||
d3_select('#content')
|
||||
.selectAll('.mapillary-wrap')
|
||||
.classed('hidden', true)
|
||||
.selectAll('.mly-wrapper')
|
||||
.classed('active', false);
|
||||
d3_select('#photoviewer')
|
||||
.classed('hide', true)
|
||||
.selectAll('.photo-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
d3_selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
|
||||
.classed('selected', false);
|
||||
@@ -514,7 +545,7 @@ export default {
|
||||
mapillaryClicks.push(imageKey);
|
||||
}
|
||||
|
||||
d3_selectAll('.layer-mapillary-images .viewfield-group')
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('selected', function(d) {
|
||||
return d.key === imageKey;
|
||||
});
|
||||
@@ -544,12 +575,12 @@ export default {
|
||||
var attribution = d3_select('.mapillary-js-dom .Attribution');
|
||||
var capturedAt = attribution.selectAll('.captured-at');
|
||||
if (capturedAt.empty()) {
|
||||
attribution
|
||||
.append('span')
|
||||
.text('|');
|
||||
capturedAt = attribution
|
||||
.append('span')
|
||||
.insert('span', ':last-child')
|
||||
.attr('class', 'captured-at');
|
||||
attribution
|
||||
.insert('span', ':last-child')
|
||||
.text('|');
|
||||
}
|
||||
capturedAt
|
||||
.text(timestamp);
|
||||
|
||||
@@ -0,0 +1,479 @@
|
||||
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 { 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')
|
||||
.header('Content-type', 'application/x-www-form-urlencoded')
|
||||
.response(function(xhr) { return JSON.parse(xhr.responseText); })
|
||||
.post(params, function(err, data) {
|
||||
cache.loaded[id] = true;
|
||||
delete cache.inflight[id];
|
||||
if (err || !data.currentPageItems || !data.currentPageItems.length) return;
|
||||
|
||||
function localeDateString(s) {
|
||||
if (!s) return null;
|
||||
var d = new Date(s);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
return d.toLocaleDateString();
|
||||
}
|
||||
|
||||
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: localeDateString(item.shot_date || item.date_added),
|
||||
captured_by: item.username,
|
||||
imagePath: item.lth_name,
|
||||
sequence_id: +item.sequence_id,
|
||||
sequence_index: +item.sequence_index
|
||||
};
|
||||
|
||||
// cache sequence info
|
||||
var seq = openstreetcamCache.sequences[d.sequence_id];
|
||||
if (!seq) {
|
||||
seq = { rotation: 0, images: [] };
|
||||
openstreetcamCache.sequences[d.sequence_id] = seq;
|
||||
}
|
||||
seq.images[d.sequence_index] = d;
|
||||
}
|
||||
|
||||
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() },
|
||||
sequences: {}
|
||||
};
|
||||
|
||||
openstreetcamImage = null;
|
||||
},
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, openstreetcamCache.images.rtree);
|
||||
},
|
||||
|
||||
|
||||
sequences: function(projection) {
|
||||
var viewport = projection.clipExtent();
|
||||
var min = [viewport[0][0], viewport[1][1]];
|
||||
var max = [viewport[1][0], viewport[0][1]];
|
||||
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
|
||||
var seq_ids = {};
|
||||
|
||||
// all sequences for images in viewport
|
||||
openstreetcamCache.images.rtree.search(bbox)
|
||||
.forEach(function(d) { seq_ids[d.data.sequence_id] = true; });
|
||||
|
||||
// make linestrings from those sequences
|
||||
var lineStrings = [];
|
||||
Object.keys(seq_ids).forEach(function(seq_id) {
|
||||
var seq = openstreetcamCache.sequences[seq_id];
|
||||
var images = seq && seq.images;
|
||||
if (images) {
|
||||
lineStrings.push({
|
||||
type: 'LineString',
|
||||
coordinates: images.map(function (d) { return d.loc; }).filter(Boolean)
|
||||
});
|
||||
}
|
||||
});
|
||||
return lineStrings;
|
||||
},
|
||||
|
||||
|
||||
loadImages: function(projection) {
|
||||
var url = apibase + '/1.0/list/nearby-photos/';
|
||||
loadTiles('images', url, projection);
|
||||
},
|
||||
|
||||
|
||||
loadViewer: function(context) {
|
||||
var that = this;
|
||||
|
||||
// add osc-wrapper
|
||||
var wrap = d3_select('#photoviewer').selectAll('.osc-wrapper')
|
||||
.data([0]);
|
||||
|
||||
var wrapEnter = wrap.enter()
|
||||
.append('div')
|
||||
.attr('class', 'photo-wrapper osc-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
wrapEnter
|
||||
.append('div')
|
||||
.attr('class', 'osc-attribution fillD');
|
||||
|
||||
var controlsEnter = wrapEnter
|
||||
.append('div')
|
||||
.attr('class', 'osc-controls-wrap')
|
||||
.append('div')
|
||||
.attr('class', 'osc-controls');
|
||||
|
||||
controlsEnter
|
||||
.append('button')
|
||||
.on('click.back', step(-1))
|
||||
.text('◄');
|
||||
|
||||
controlsEnter
|
||||
.append('button')
|
||||
.on('click.rotate-ccw', rotate(-90))
|
||||
.text('⤿');
|
||||
|
||||
controlsEnter
|
||||
.append('button')
|
||||
.on('click.rotate-cw', rotate(90))
|
||||
.text('⤾');
|
||||
|
||||
controlsEnter
|
||||
.append('button')
|
||||
.on('click.forward', step(1))
|
||||
.text('►');
|
||||
|
||||
|
||||
function rotate(deg) {
|
||||
return function() {
|
||||
if (!openstreetcamImage) return;
|
||||
var seq_id = openstreetcamImage.sequence_id;
|
||||
var seq = openstreetcamCache.sequences[seq_id];
|
||||
if (!seq) return;
|
||||
|
||||
var r = seq.rotation || 0;
|
||||
r += deg;
|
||||
seq.rotation = r;
|
||||
|
||||
d3_select('#photoviewer .osc-wrapper .osc-image')
|
||||
.transition()
|
||||
.duration(100)
|
||||
.style('transform', 'rotate(' + r + 'deg)');
|
||||
};
|
||||
}
|
||||
|
||||
function step(stepBy) {
|
||||
return function() {
|
||||
if (!openstreetcamImage) return;
|
||||
var seq_id = openstreetcamImage.sequence_id;
|
||||
var seq = openstreetcamCache.sequences[seq_id];
|
||||
if (!seq) return;
|
||||
|
||||
var nextIndex = openstreetcamImage.sequence_index + stepBy;
|
||||
var nextImage = seq.images[nextIndex];
|
||||
if (!nextImage) return;
|
||||
|
||||
context.map().centerEase(nextImage.loc);
|
||||
|
||||
that
|
||||
.selectedImage(nextImage)
|
||||
.updateViewer(nextImage);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
showViewer: function() {
|
||||
var viewer = d3_select('#photoviewer')
|
||||
.classed('hide', false);
|
||||
|
||||
var isHidden = viewer.selectAll('.photo-wrapper.osc-wrapper.hide').size();
|
||||
|
||||
if (isHidden) {
|
||||
viewer
|
||||
.selectAll('.photo-wrapper:not(.osc-wrapper)')
|
||||
.classed('hide', true);
|
||||
|
||||
viewer
|
||||
.selectAll('.photo-wrapper.osc-wrapper')
|
||||
.classed('hide', false);
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
hideViewer: function() {
|
||||
d3_select('#photoviewer')
|
||||
.classed('hide', true)
|
||||
.selectAll('.photo-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
d3_selectAll('.layer-openstreetcam-images .viewfield-group')
|
||||
.classed('selected', false);
|
||||
|
||||
openstreetcamImage = null;
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
updateViewer: function(d) {
|
||||
var wrap = d3_select('#photoviewer .osc-wrapper');
|
||||
|
||||
wrap.selectAll('.osc-image')
|
||||
.remove();
|
||||
|
||||
if (d) {
|
||||
var seq = openstreetcamCache.sequences[d.sequence_id];
|
||||
var r = (seq && seq.rotation) || 0;
|
||||
|
||||
wrap.append('img')
|
||||
.attr('class', 'osc-image')
|
||||
.style('transform', 'rotate(' + r + 'deg)')
|
||||
.attr('src', apibase + '/' + d.imagePath);
|
||||
|
||||
var attribution = wrap.selectAll('.osc-attribution').html('');
|
||||
|
||||
if (d.captured_by) {
|
||||
attribution
|
||||
.append('a')
|
||||
.attr('class', 'captured_by')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', apibase + '/user/' + d.captured_by)
|
||||
.text('@' + d.captured_by);
|
||||
|
||||
attribution
|
||||
.append('span')
|
||||
.text('|');
|
||||
}
|
||||
|
||||
if (d.captured_at) {
|
||||
attribution
|
||||
.append('span')
|
||||
.attr('class', 'captured_at')
|
||||
.text(d.captured_at);
|
||||
|
||||
attribution
|
||||
.append('span')
|
||||
.text('|');
|
||||
}
|
||||
|
||||
attribution
|
||||
.append('a')
|
||||
.attr('class', 'image_link')
|
||||
.attr('target', '_blank')
|
||||
.attr('href', apibase + '/details/' + d.sequence_id + '/' + d.sequence_index)
|
||||
.text('openstreetcam.org');
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
selectedImage: function(d) {
|
||||
if (!arguments.length) return openstreetcamImage;
|
||||
openstreetcamImage = d;
|
||||
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('selected', function(d) {
|
||||
return d.key === openstreetcamImage.key;
|
||||
});
|
||||
|
||||
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) }
|
||||
];
|
||||
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath
|
||||
} from 'd3-geo';
|
||||
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { svgPointTransform } from './point_transform';
|
||||
import { services } from '../services';
|
||||
|
||||
@@ -95,12 +102,35 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function update() {
|
||||
var mapillary = getMapillary(),
|
||||
data = (mapillary ? mapillary.images(projection) : []),
|
||||
imageKey = mapillary ? mapillary.selectedImage() : null;
|
||||
var highZoom = ~~context.map().zoom() >= minViewfieldZoom;
|
||||
var mapillary = getMapillary();
|
||||
var images = (mapillary ? mapillary.images(projection) : []);
|
||||
var sequences = (mapillary && highZoom ? mapillary.sequences(projection) : []);
|
||||
var imageKey = mapillary ? mapillary.selectedImage() : null;
|
||||
|
||||
var markers = layer.selectAll('.viewfield-group')
|
||||
.data(data, function(d) { return d.key; });
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
var makePath = d3_geoPath().projection({ stream: function(output) {
|
||||
return project(clip(output));
|
||||
}});
|
||||
|
||||
var lineStrings = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences);
|
||||
|
||||
lineStrings.exit()
|
||||
.remove();
|
||||
|
||||
lineStrings = lineStrings.enter()
|
||||
.append('path')
|
||||
.attr('class', 'sequence')
|
||||
.merge(lineStrings);
|
||||
|
||||
lineStrings
|
||||
.attr('d', makePath);
|
||||
|
||||
|
||||
var markers = layer.selectAll('.markers').selectAll('.viewfield-group')
|
||||
.data(images, function(d) { return d.key; });
|
||||
|
||||
markers.exit()
|
||||
.remove();
|
||||
@@ -117,7 +147,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
var viewfields = markers.selectAll('.viewfield')
|
||||
.data(~~context.map().zoom() >= minViewfieldZoom ? [0] : []);
|
||||
.data(highZoom ? [0] : []);
|
||||
|
||||
viewfields.exit()
|
||||
.remove();
|
||||
@@ -148,10 +178,20 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
layer.exit()
|
||||
.remove();
|
||||
|
||||
layer = layer.enter()
|
||||
var layerEnter = layer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-mapillary-images')
|
||||
.style('display', enabled ? 'block' : 'none')
|
||||
.style('display', enabled ? 'block' : 'none');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'sequences');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'markers');
|
||||
|
||||
layer = layerEnter
|
||||
.merge(layer);
|
||||
|
||||
if (enabled) {
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
import _throttle from 'lodash-es/throttle';
|
||||
|
||||
import { select as d3_select } from 'd3-selection';
|
||||
import {
|
||||
geoIdentity as d3_geoIdentity,
|
||||
geoPath as d3_geoPath
|
||||
} from 'd3-geo';
|
||||
|
||||
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)
|
||||
.updateViewer(d)
|
||||
.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 highZoom = ~~context.map().zoom() >= minViewfieldZoom;
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
var sequences = (openstreetcam && highZoom ? openstreetcam.sequences(projection) : []);
|
||||
var images = (openstreetcam ? openstreetcam.images(projection) : []);
|
||||
var selectedImage = openstreetcam && openstreetcam.selectedImage();
|
||||
var imageKey = selectedImage && selectedImage.key;
|
||||
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
var makePath = d3_geoPath().projection({ stream: function(output) {
|
||||
return project(clip(output));
|
||||
}});
|
||||
|
||||
var lineStrings = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences);
|
||||
|
||||
lineStrings.exit()
|
||||
.remove();
|
||||
|
||||
lineStrings = lineStrings.enter()
|
||||
.append('path')
|
||||
.attr('class', 'sequence')
|
||||
.merge(lineStrings);
|
||||
|
||||
lineStrings
|
||||
.attr('d', makePath);
|
||||
|
||||
|
||||
var markers = layer.selectAll('.markers').selectAll('.viewfield-group')
|
||||
.data(images, 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(highZoom ? [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();
|
||||
|
||||
var layerEnter = layer.enter()
|
||||
.append('g')
|
||||
.attr('class', 'layer-openstreetcam-images')
|
||||
.style('display', enabled ? 'block' : 'none');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'sequences');
|
||||
|
||||
layerEnter
|
||||
.append('g')
|
||||
.attr('class', 'markers');
|
||||
|
||||
layer = layerEnter
|
||||
.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;
|
||||
}
|
||||
+22
-3
@@ -8,9 +8,9 @@ import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js';
|
||||
import { t, textDirection } from '../util/locale';
|
||||
import { tooltip } from '../util/tooltip';
|
||||
|
||||
import { svgDefs, svgIcon } from '../svg/index';
|
||||
import { modeBrowse } from '../modes/index';
|
||||
import { behaviorHash } from '../behavior/index';
|
||||
import { svgDefs, svgIcon } from '../svg';
|
||||
import { modeBrowse } from '../modes';
|
||||
import { behaviorHash } from '../behavior';
|
||||
import { utilGetDimensions } from '../util/dimensions';
|
||||
|
||||
import { uiAccount } from './account';
|
||||
@@ -238,6 +238,25 @@ export function uiInit(context) {
|
||||
.call(uiContributors(context));
|
||||
|
||||
|
||||
var photoviewer = content
|
||||
.append('div')
|
||||
.attr('id', 'photoviewer')
|
||||
.classed('al', true) // 'al'=left, 'ar'=right
|
||||
.classed('hide', true);
|
||||
|
||||
photoviewer
|
||||
.append('button')
|
||||
.attr('class', 'thumb-hide')
|
||||
.on('click', function () {
|
||||
d3_select('#photoviewer')
|
||||
.classed('hide', true)
|
||||
.select('div')
|
||||
.classed('hide', true);
|
||||
})
|
||||
.append('div')
|
||||
.call(svgIcon('#icon-close'));
|
||||
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
return context.save();
|
||||
};
|
||||
|
||||
+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
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
|
||||
<script src='spec/services/mapillary.js'></script>
|
||||
<script src='spec/services/nominatim.js'></script>
|
||||
<script src='spec/services/openstreetcam.js'></script>
|
||||
<script src='spec/services/osm.js'></script>
|
||||
<script src='spec/services/taginfo.js'></script>
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@ describe('iD.serviceMapillary', function() {
|
||||
var cache = mapillary.cache();
|
||||
expect(cache).to.have.property('images');
|
||||
expect(cache).to.have.property('objects');
|
||||
expect(cache).to.have.property('sequences');
|
||||
expect(cache).to.have.property('detections');
|
||||
|
||||
mapillary.init();
|
||||
@@ -348,6 +349,44 @@ describe('iD.serviceMapillary', function() {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#sequences', function() {
|
||||
it('returns sequence linestrings in the visible map area', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90 } },
|
||||
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90 } }
|
||||
];
|
||||
|
||||
mapillary.cache().images.rtree.load(features);
|
||||
|
||||
var gj = {
|
||||
type: 'Feature',
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: [[10,0], [10,0], [10,1]],
|
||||
properties: {
|
||||
key: '-',
|
||||
pano: false,
|
||||
coordinateProperties: {
|
||||
cas: [90, 90, 90],
|
||||
image_keys: ['0', '1', '2']
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
mapillary.cache().sequences.lineString['-'] = gj;
|
||||
mapillary.cache().sequences.forImage['0'] = '-';
|
||||
mapillary.cache().sequences.forImage['1'] = '-';
|
||||
mapillary.cache().sequences.forImage['2'] = '-';
|
||||
|
||||
var res = mapillary.sequences(context.projection);
|
||||
expect(res).to.deep.eql([gj]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#signsSupported', function() {
|
||||
it('returns false for Internet Explorer', function() {
|
||||
ua = 'Trident/7.0; rv:11.0';
|
||||
|
||||
@@ -0,0 +1,310 @@
|
||||
describe('iD.serviceOpenstreetcam', function() {
|
||||
var dimensions = [64, 64],
|
||||
ua = navigator.userAgent,
|
||||
isPhantom = (navigator.userAgent.match(/PhantomJS/) !== null),
|
||||
uaMock = function () { return ua; },
|
||||
context, server, openstreetcam, orig;
|
||||
|
||||
before(function() {
|
||||
iD.services.openstreetcam = iD.serviceOpenstreetcam;
|
||||
});
|
||||
|
||||
after(function() {
|
||||
delete iD.services.openstreetcam;
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
context = iD.Context().assetPath('../dist/');
|
||||
context.projection
|
||||
.scale(667544.214430109) // z14
|
||||
.translate([-116508, 0]) // 10,0
|
||||
.clipExtent([[0,0], dimensions]);
|
||||
|
||||
server = sinon.fakeServer.create();
|
||||
openstreetcam = iD.services.openstreetcam;
|
||||
openstreetcam.reset();
|
||||
|
||||
/* eslint-disable no-global-assign */
|
||||
/* mock userAgent */
|
||||
if (isPhantom) {
|
||||
orig = navigator;
|
||||
navigator = Object.create(orig, { userAgent: { get: uaMock }});
|
||||
} else {
|
||||
orig = navigator.__lookupGetter__('userAgent');
|
||||
navigator.__defineGetter__('userAgent', uaMock);
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
server.restore();
|
||||
|
||||
/* restore userAgent */
|
||||
if (isPhantom) {
|
||||
navigator = orig;
|
||||
} else {
|
||||
navigator.__defineGetter__('userAgent', orig);
|
||||
}
|
||||
/* eslint-enable no-global-assign */
|
||||
});
|
||||
|
||||
|
||||
describe('#init', function() {
|
||||
it('Initializes cache one time', function() {
|
||||
var cache = openstreetcam.cache();
|
||||
expect(cache).to.have.property('images');
|
||||
expect(cache).to.have.property('sequences');
|
||||
|
||||
openstreetcam.init();
|
||||
var cache2 = openstreetcam.cache();
|
||||
expect(cache).to.equal(cache2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reset', function() {
|
||||
it('resets cache and image', function() {
|
||||
openstreetcam.cache({foo: 'bar'});
|
||||
openstreetcam.selectedImage('baz');
|
||||
|
||||
openstreetcam.reset();
|
||||
expect(openstreetcam.cache()).to.not.have.property('foo');
|
||||
expect(openstreetcam.selectedImage()).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#loadImages', function() {
|
||||
it('fires loadedImages when images are loaded', function() {
|
||||
var spy = sinon.spy();
|
||||
openstreetcam.on('loadedImages', spy);
|
||||
openstreetcam.loadImages(context.projection);
|
||||
|
||||
var data = {
|
||||
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
|
||||
currentPageItems:[{
|
||||
id: '1',
|
||||
sequence_id: '100',
|
||||
sequence_index: '1',
|
||||
lat: '0',
|
||||
lng: '10.001',
|
||||
name: 'storage6\/files\/photo\/foo1.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo1.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo1.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}, {
|
||||
id: '2',
|
||||
sequence_id: '100',
|
||||
sequence_index: '2',
|
||||
lat: '0',
|
||||
lng: '10.002',
|
||||
name: 'storage6\/files\/photo\/foo2.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo2.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo2.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}, {
|
||||
id: '3',
|
||||
sequence_id: '100',
|
||||
sequence_index: '3',
|
||||
lat: '0',
|
||||
lng: '10.003',
|
||||
name: 'storage6\/files\/photo\/foo3.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo3.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo3.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}],
|
||||
totalFilteredItems: ['3']
|
||||
};
|
||||
|
||||
server.respondWith('POST', /nearby-photos/,
|
||||
[200, { 'Content-Type': 'application/json' }, JSON.stringify(data) ]);
|
||||
server.respond();
|
||||
|
||||
expect(spy).to.have.been.calledOnce;
|
||||
});
|
||||
|
||||
it('does not load images around null island', function() {
|
||||
var spy = sinon.spy();
|
||||
context.projection.translate([0,0]);
|
||||
openstreetcam.on('loadedImages', spy);
|
||||
openstreetcam.loadImages(context.projection);
|
||||
|
||||
var data = {
|
||||
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
|
||||
currentPageItems:[{
|
||||
id: '1',
|
||||
sequence_id: '100',
|
||||
sequence_index: '1',
|
||||
lat: '0',
|
||||
lng: '0',
|
||||
name: 'storage6\/files\/photo\/foo1.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo1.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo1.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}, {
|
||||
id: '2',
|
||||
sequence_id: '100',
|
||||
sequence_index: '2',
|
||||
lat: '0',
|
||||
lng: '0',
|
||||
name: 'storage6\/files\/photo\/foo2.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo2.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo2.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}, {
|
||||
id: '3',
|
||||
sequence_id: '100',
|
||||
sequence_index: '3',
|
||||
lat: '0',
|
||||
lng: '0',
|
||||
name: 'storage6\/files\/photo\/foo3.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo3.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo3.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
}],
|
||||
totalFilteredItems: ['3']
|
||||
};
|
||||
|
||||
server.respondWith('POST', /nearby-photos/,
|
||||
[200, { 'Content-Type': 'application/json' }, JSON.stringify(data) ]);
|
||||
server.respond();
|
||||
|
||||
expect(spy).to.have.been.not.called;
|
||||
});
|
||||
|
||||
it.skip('loads multiple pages of image results', function() {
|
||||
var spy = sinon.spy();
|
||||
openstreetcam.on('loadedImages', spy);
|
||||
openstreetcam.loadImages(context.projection);
|
||||
|
||||
var features0 = [],
|
||||
features1 = [],
|
||||
i;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
features0.push({
|
||||
id: String(i),
|
||||
sequence_id: '100',
|
||||
sequence_index: String(i),
|
||||
lat: '10',
|
||||
lng: '0',
|
||||
name: 'storage6\/files\/photo\/foo' + String(i) +'.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo' + String(i) +'.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo' + String(i) +'.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
});
|
||||
}
|
||||
for (i = 0; i < 500; i++) {
|
||||
features1.push({
|
||||
id: String(i),
|
||||
sequence_id: '100',
|
||||
sequence_index: String(1000 + i),
|
||||
lat: '10',
|
||||
lng: '0',
|
||||
name: 'storage6\/files\/photo\/foo' + String(1000 + i) +'.jpg',
|
||||
lth_name: 'storage6\/files\/photo\/lth\/foo' + String(1000 + i) +'.jpg',
|
||||
th_name: 'storage6\/files\/photo\/th\/foo' + String(1000 + i) +'.jpg',
|
||||
shot_date: '2017-09-24 23:58:07',
|
||||
heading: '90',
|
||||
username: 'test'
|
||||
});
|
||||
}
|
||||
|
||||
var response0 = {
|
||||
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
|
||||
currentPageItems: [features0],
|
||||
totalFilteredItems: ['1000']
|
||||
},
|
||||
response1 = {
|
||||
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
|
||||
currentPageItems: [features1],
|
||||
totalFilteredItems: ['500']
|
||||
};
|
||||
|
||||
server.respondWith('POST', /nearby-photos/, function (request) {
|
||||
var response;
|
||||
if (request.requestBody.match(/page=1/) !== null) {
|
||||
response = JSON.stringify(response0);
|
||||
} else if (request.requestBody.match(/page=2/) !== null) {
|
||||
response = JSON.stringify(response1);
|
||||
}
|
||||
request.respond(200, {'Content-Type': 'application/json'}, response);
|
||||
});
|
||||
server.respond();
|
||||
|
||||
expect(spy).to.have.been.calledTwice;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#images', function() {
|
||||
it('returns images in the visible map area', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 0 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 1 } },
|
||||
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90, sequence_id: 100, sequence_index: 2 } }
|
||||
];
|
||||
|
||||
openstreetcam.cache().images.rtree.load(features);
|
||||
var res = openstreetcam.images(context.projection);
|
||||
|
||||
expect(res).to.deep.eql([
|
||||
{ key: '0', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 0 },
|
||||
{ key: '1', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 1 }
|
||||
]);
|
||||
});
|
||||
|
||||
it('limits results no more than 3 stacked images in one spot', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 0 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 1 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 2 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 3 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 4 } }
|
||||
];
|
||||
|
||||
openstreetcam.cache().images.rtree.load(features);
|
||||
var res = openstreetcam.images(context.projection);
|
||||
expect(res).to.have.length.of.at.most(3);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#sequences', function() {
|
||||
it('returns sequence linestrings in the visible map area', function() {
|
||||
var features = [
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 0 } },
|
||||
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: 100, sequence_index: 1 } },
|
||||
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90, sequence_id: 100, sequence_index: 2 } }
|
||||
];
|
||||
|
||||
openstreetcam.cache().images.rtree.load(features);
|
||||
openstreetcam.cache().sequences['100'] = { rotation: 0, images: [ features[0].data, features[1].data, features[2].data ] };
|
||||
|
||||
var res = openstreetcam.sequences(context.projection);
|
||||
expect(res).to.deep.eql([{
|
||||
type: 'LineString',
|
||||
coordinates: [[10,0], [10,0], [10,1]]
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectedImage', function() {
|
||||
it('sets and gets selected image', function() {
|
||||
openstreetcam.selectedImage('foo');
|
||||
expect(openstreetcam.selectedImage()).to.eql('foo');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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