mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-14 05:12:13 +02:00
Highlight selected/hovered streetview tracks, fade unselected
Also some code cleanups and nitpicky variable renames More consistency in Mapillary/OpenStreetCam services
This commit is contained in:
+45
-15
@@ -24,41 +24,71 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* markers and sequences */
|
||||
.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;
|
||||
fill: #ffee00 !important;
|
||||
z-index: 60;
|
||||
}
|
||||
.viewfield-group.hovered * {
|
||||
fill: #eebb00 !important;
|
||||
z-index: 70;
|
||||
}
|
||||
.viewfield-group.highlighted * {
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
.viewfield-group:hover * {
|
||||
stroke-width: 1;
|
||||
.viewfield-group circle {
|
||||
stroke: #333;
|
||||
fill: #ff9900 !important;
|
||||
z-index: 70;
|
||||
stroke-width: 1;
|
||||
stroke-opacity: 0.4;
|
||||
fill-opacity: 0.4;
|
||||
}
|
||||
.viewfield-group.highlighted circle {
|
||||
stroke-opacity: 0.9;
|
||||
fill-opacity: 0.9;
|
||||
}
|
||||
.viewfield-group.highlighted.hovered circle {
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 0.9;
|
||||
fill-opacity: 0.9;
|
||||
}
|
||||
.viewfield-group.highlighted.selected circle {
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 1;
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.viewfield-group:hover path.viewfield,
|
||||
.viewfield-group.selected path.viewfield,
|
||||
.viewfield-group path.viewfield {
|
||||
.viewfield-group .viewfield {
|
||||
stroke-width: 0;
|
||||
fill-opacity: 0.6;
|
||||
fill-opacity: 0.4;
|
||||
}
|
||||
.viewfield-group.highlighted .viewfield {
|
||||
fill-opacity: 0.8;
|
||||
}
|
||||
.viewfield-group.highlighted.hovered .viewfield {
|
||||
fill-opacity: 0.8;
|
||||
}
|
||||
.viewfield-group.highlighted.selected .viewfield {
|
||||
fill-opacity: 0.9;
|
||||
}
|
||||
|
||||
.sequence {
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
stroke-width: 2;
|
||||
stroke-opacity: 0.4;
|
||||
}
|
||||
.sequence.highlighted,
|
||||
.sequence.selected {
|
||||
stroke-width: 4;
|
||||
stroke-opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+150
-89
@@ -6,6 +6,7 @@ import _forEach from 'lodash-es/forEach';
|
||||
import _isEmpty from 'lodash-es/isEmpty';
|
||||
import _map from 'lodash-es/map';
|
||||
import _some from 'lodash-es/some';
|
||||
import _union from 'lodash-es/union';
|
||||
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
@@ -35,12 +36,12 @@ var apibase = 'https://a.mapillary.com/v3/',
|
||||
maxResults = 1000,
|
||||
tileZoom = 14,
|
||||
dispatch = d3_dispatch('loadedImages', 'loadedSigns'),
|
||||
mapillaryCache,
|
||||
mapillaryClicks,
|
||||
mapillaryImage,
|
||||
mapillarySignDefs,
|
||||
mapillarySignSprite,
|
||||
mapillaryViewer;
|
||||
_mlyCache,
|
||||
_mlyClicks,
|
||||
_mlySelectedImage,
|
||||
_mlySignDefs,
|
||||
_mlySignSprite,
|
||||
_mlyViewer;
|
||||
|
||||
|
||||
function abortRequest(i) {
|
||||
@@ -120,7 +121,7 @@ function loadTiles(which, url, projection) {
|
||||
|
||||
|
||||
function loadNextTilePage(which, currZoom, url, tile) {
|
||||
var cache = mapillaryCache[which],
|
||||
var cache = _mlyCache[which],
|
||||
rect = tile.extent.rectangle(),
|
||||
maxPages = maxPageAtZoom(currZoom),
|
||||
nextPage = cache.nextPage[tile.id] || 0,
|
||||
@@ -165,14 +166,15 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
captured_at: feature.properties.captured_at,
|
||||
pano: feature.properties.pano
|
||||
};
|
||||
cache.forImageKey[d.key] = d; // cache imageKey -> image
|
||||
|
||||
} 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
|
||||
var sequenceKey = feature.properties.key;
|
||||
cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString
|
||||
feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) {
|
||||
cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey
|
||||
});
|
||||
return false; // nothing to actually insert
|
||||
return false; // because no `d` data worth loading into an rbush
|
||||
|
||||
} else if (which === 'objects') {
|
||||
d = {
|
||||
@@ -183,15 +185,15 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
detections: feature.properties.detections
|
||||
};
|
||||
|
||||
// cache image_key -> detection_key
|
||||
// cache imageKey -> detectionKey
|
||||
feature.properties.detections.forEach(function(detection) {
|
||||
var ik = detection.image_key;
|
||||
var dk = detection.detection_key;
|
||||
if (!mapillaryCache.detections[ik]) {
|
||||
mapillaryCache.detections[ik] = {};
|
||||
var imageKey = detection.image_key;
|
||||
var detectionKey = detection.detection_key;
|
||||
if (!_mlyCache.detections[imageKey]) {
|
||||
_mlyCache.detections[imageKey] = {};
|
||||
}
|
||||
if (!mapillaryCache.detections[ik][dk]) {
|
||||
mapillaryCache.detections[ik][dk] = {};
|
||||
if (!_mlyCache.detections[imageKey][detectionKey]) {
|
||||
_mlyCache.detections[imageKey][detectionKey] = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -199,6 +201,7 @@ 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);
|
||||
@@ -295,7 +298,7 @@ function searchLimited(psize, limit, projection, rtree) {
|
||||
export default {
|
||||
|
||||
init: function() {
|
||||
if (!mapillaryCache) {
|
||||
if (!_mlyCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
@@ -303,7 +306,7 @@ export default {
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var cache = mapillaryCache;
|
||||
var cache = _mlyCache;
|
||||
|
||||
if (cache) {
|
||||
if (cache.images && cache.images.inflight) {
|
||||
@@ -317,27 +320,27 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
mapillaryCache = {
|
||||
images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() },
|
||||
_mlyCache = {
|
||||
images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {} },
|
||||
objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() },
|
||||
sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImage: {}, lineString: {} },
|
||||
sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {}, lineString: {} },
|
||||
detections: {}
|
||||
};
|
||||
|
||||
mapillaryImage = null;
|
||||
mapillaryClicks = [];
|
||||
_mlySelectedImage = null;
|
||||
_mlyClicks = [];
|
||||
},
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, mapillaryCache.images.rtree);
|
||||
return searchLimited(psize, limit, projection, _mlyCache.images.rtree);
|
||||
},
|
||||
|
||||
|
||||
signs: function(projection) {
|
||||
var psize = 32, limit = 3;
|
||||
return searchLimited(psize, limit, projection, mapillaryCache.objects.rtree);
|
||||
return searchLimited(psize, limit, projection, _mlyCache.objects.rtree);
|
||||
},
|
||||
|
||||
|
||||
@@ -349,17 +352,17 @@ export default {
|
||||
var sequenceKeys = {};
|
||||
|
||||
// all sequences for images in viewport
|
||||
mapillaryCache.images.rtree.search(bbox)
|
||||
_mlyCache.images.rtree.search(bbox)
|
||||
.forEach(function(d) {
|
||||
var sk = mapillaryCache.sequences.forImage[d.data.key];
|
||||
if (sk) {
|
||||
sequenceKeys[sk] = true;
|
||||
var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key];
|
||||
if (sequenceKey) {
|
||||
sequenceKeys[sequenceKey] = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Return linestrings for the sequences
|
||||
return Object.keys(sequenceKeys).map(function(sk) {
|
||||
return mapillaryCache.sequences.lineString[sk];
|
||||
// Return lineStrings for the sequences
|
||||
return Object.keys(sequenceKeys).map(function(sequenceKey) {
|
||||
return _mlyCache.sequences.lineString[sequenceKey];
|
||||
});
|
||||
},
|
||||
|
||||
@@ -373,11 +376,11 @@ export default {
|
||||
|
||||
|
||||
signHTML: function(d) {
|
||||
if (!mapillarySignDefs || !mapillarySignSprite) return;
|
||||
var position = mapillarySignDefs[d.value];
|
||||
if (!_mlySignDefs || !_mlySignSprite) return;
|
||||
var position = _mlySignDefs[d.value];
|
||||
if (!position) return '<div></div>';
|
||||
var iconStyle = [
|
||||
'background-image:url(' + mapillarySignSprite + ')',
|
||||
'background-image:url(' + _mlySignSprite + ')',
|
||||
'background-repeat:no-repeat',
|
||||
'height:' + position.height + 'px',
|
||||
'width:' + position.width + 'px',
|
||||
@@ -400,12 +403,12 @@ export default {
|
||||
loadTiles('objects', url, projection);
|
||||
|
||||
// load traffic sign defs
|
||||
if (!mapillarySignDefs) {
|
||||
mapillarySignSprite = context.asset('img/traffic-signs/traffic-signs.png');
|
||||
mapillarySignDefs = {};
|
||||
if (!_mlySignDefs) {
|
||||
_mlySignSprite = context.asset('img/traffic-signs/traffic-signs.png');
|
||||
_mlySignDefs = {};
|
||||
d3_json(context.asset('img/traffic-signs/traffic-signs.json'), function(err, data) {
|
||||
if (err) return;
|
||||
mapillarySignDefs = data;
|
||||
_mlySignDefs = data;
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -455,7 +458,7 @@ export default {
|
||||
.selectAll('.photo-wrapper.mly-wrapper')
|
||||
.classed('hide', false);
|
||||
|
||||
mapillaryViewer.resize();
|
||||
_mlyViewer.resize();
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -468,10 +471,10 @@ export default {
|
||||
.selectAll('.photo-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
d3_selectAll('.layer-mapillary-images .viewfield-group, .layer-mapillary-signs .icon-sign')
|
||||
d3_selectAll('.viewfield-group, .sequence, .icon-sign')
|
||||
.classed('selected', false);
|
||||
|
||||
mapillaryImage = null;
|
||||
_mlySelectedImage = null;
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -479,13 +482,13 @@ export default {
|
||||
parsePagination: parsePagination,
|
||||
|
||||
|
||||
updateViewer: function(imageKey, context) {
|
||||
if (!imageKey) return;
|
||||
updateViewer: function(d, context) {
|
||||
if (!d || !d.key) return;
|
||||
|
||||
if (!mapillaryViewer) {
|
||||
this.initViewer(imageKey, context);
|
||||
if (!_mlyViewer) {
|
||||
this.initViewer(d.key, context);
|
||||
} else {
|
||||
mapillaryViewer.moveToKey(imageKey);
|
||||
_mlyViewer.moveToKey(d.key);
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -504,51 +507,54 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
mapillaryViewer = new Mapillary.Viewer('mly', clientId, imageKey, opts);
|
||||
mapillaryViewer.on('nodechanged', nodeChanged);
|
||||
_mlyViewer = new Mapillary.Viewer('mly', clientId, imageKey, opts);
|
||||
_mlyViewer.on('nodechanged', nodeChanged);
|
||||
}
|
||||
|
||||
// nodeChanged: called after the viewer has changed images and is ready.
|
||||
//
|
||||
// There is some logic here to batch up clicks into a mapillaryClicks array
|
||||
// There is some logic here to batch up clicks into a _mlyClicks array
|
||||
// because the user might click on a lot of markers quickly and nodechanged
|
||||
// may be called out of order asychronously.
|
||||
//
|
||||
// Clicks are added to the array in `selectedImage` and removed here.
|
||||
//
|
||||
function nodeChanged(node) {
|
||||
mapillaryViewer.getComponent('tag').removeAll(); // remove previous detections
|
||||
_mlyViewer.getComponent('tag').removeAll(); // remove previous detections
|
||||
|
||||
var clicks = mapillaryClicks;
|
||||
var clicks = _mlyClicks;
|
||||
var index = clicks.indexOf(node.key);
|
||||
if (index > -1) { // `nodechanged` initiated from clicking on a marker..
|
||||
clicks.splice(index, 1);
|
||||
// If `node.key` matches the current mapillaryImage, call `selectedImage()`
|
||||
var selectedKey = _mlySelectedImage && _mlySelectedImage.key;
|
||||
|
||||
if (index > -1) { // `nodechanged` initiated from clicking on a marker..
|
||||
clicks.splice(index, 1); // remove the click
|
||||
// If `node.key` matches the current _mlySelectedImage, call `selectImage()`
|
||||
// one more time to update the detections and attribution..
|
||||
if (node.key === mapillaryImage) {
|
||||
that.selectedImage(node.key, false);
|
||||
if (node.key === selectedKey) {
|
||||
that.selectImage(_mlySelectedImage, node.key, true);
|
||||
}
|
||||
} else { // `nodechanged` initiated from the Mapillary viewer controls..
|
||||
var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat];
|
||||
context.map().centerEase(loc);
|
||||
that.selectedImage(node.key, false);
|
||||
that.selectImage(undefined, node.key, true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
selectedImage: function(imageKey, fromClick) {
|
||||
if (!arguments.length) return mapillaryImage;
|
||||
mapillaryImage = imageKey;
|
||||
|
||||
if (fromClick) {
|
||||
mapillaryClicks.push(imageKey);
|
||||
selectImage: function(d, imageKey, fromViewer) {
|
||||
if (!d && imageKey) {
|
||||
d = _mlyCache.images.forImageKey[imageKey];
|
||||
}
|
||||
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('selected', function(d) {
|
||||
return d.key === imageKey;
|
||||
});
|
||||
_mlySelectedImage = d;
|
||||
imageKey = d && d.key;
|
||||
|
||||
if (!fromViewer && imageKey) {
|
||||
_mlyClicks.push(imageKey);
|
||||
}
|
||||
|
||||
this.setStyles(null, _mlySelectedImage, true);
|
||||
|
||||
d3_selectAll('.layer-mapillary-signs .icon-sign')
|
||||
.classed('selected', function(d) {
|
||||
@@ -557,7 +563,7 @@ export default {
|
||||
});
|
||||
});
|
||||
|
||||
if (!imageKey) return this;
|
||||
if (!d) return this;
|
||||
|
||||
|
||||
function localeTimestamp(s) {
|
||||
@@ -570,14 +576,14 @@ export default {
|
||||
var selected = d3_selectAll('.layer-mapillary-images .viewfield-group.selected');
|
||||
if (selected.empty()) return this;
|
||||
|
||||
var datum = selected.datum();
|
||||
var timestamp = localeTimestamp(datum.captured_at);
|
||||
var timestamp = localeTimestamp(d.captured_at);
|
||||
var attribution = d3_select('.mapillary-js-dom .Attribution');
|
||||
var capturedAt = attribution.selectAll('.captured-at');
|
||||
if (capturedAt.empty()) {
|
||||
capturedAt = attribution
|
||||
.insert('span', ':last-child')
|
||||
.attr('class', 'captured-at');
|
||||
|
||||
attribution
|
||||
.insert('span', ':last-child')
|
||||
.text('|');
|
||||
@@ -585,23 +591,79 @@ export default {
|
||||
capturedAt
|
||||
.text(timestamp);
|
||||
|
||||
this.updateDetections();
|
||||
this.updateDetections(d);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
updateDetections: function() {
|
||||
if (!mapillaryViewer) return;
|
||||
getSelectedImage: function() {
|
||||
return _mlySelectedImage;
|
||||
},
|
||||
|
||||
|
||||
getSequenceKeyForImage: function(d) {
|
||||
var imageKey = d && d.key;
|
||||
return imageKey && _mlyCache.sequences.forImageKey[imageKey];
|
||||
},
|
||||
|
||||
|
||||
getSelectedSequenceKey: function() {
|
||||
return this.getSequenceKeyForImage(_mlySelectedImage);
|
||||
},
|
||||
|
||||
|
||||
setStyles: function(hovered, selected, reset) {
|
||||
if (reset) { // reset all layers
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('highlighted', false)
|
||||
.classed('hovered', false)
|
||||
.classed('selected', false);
|
||||
|
||||
d3_selectAll('.sequence')
|
||||
.classed('highlighted', false)
|
||||
.classed('selected', false);
|
||||
}
|
||||
|
||||
var hoveredImageKey = hovered && hovered.key;
|
||||
var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
|
||||
var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey];
|
||||
var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || [];
|
||||
|
||||
var selectedImageKey = selected && selected.key;
|
||||
var selectedSequenceKey = this.getSequenceKeyForImage(selected);
|
||||
var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey];
|
||||
var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || [];
|
||||
|
||||
// highlight sibling viewfields on either the selected or the hovered sequences
|
||||
var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys);
|
||||
|
||||
d3_selectAll('.layer-mapillary-images .viewfield-group')
|
||||
.classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
|
||||
.classed('hovered', function(d) { return d.key === hoveredImageKey; })
|
||||
.classed('selected', function(d) { return d.key === selectedImageKey; });
|
||||
|
||||
d3_selectAll('.layer-mapillary-images .sequence')
|
||||
.classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
|
||||
.classed('selected', function(d) { return d.properties.key === selectedSequenceKey; });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
updateDetections: function(d) {
|
||||
if (!_mlyViewer) return;
|
||||
|
||||
var imageKey = d && d.key;
|
||||
var detections = (imageKey && _mlyCache.detections[imageKey]) || [];
|
||||
|
||||
var detections = mapillaryCache.detections[mapillaryImage];
|
||||
_forEach(detections, function(data, k) {
|
||||
if (_isEmpty(data)) {
|
||||
loadDetection(k);
|
||||
} else {
|
||||
var tag = makeTag(data);
|
||||
if (tag) {
|
||||
var tagComponent = mapillaryViewer.getComponent('tag');
|
||||
var tagComponent = _mlyViewer.getComponent('tag');
|
||||
tagComponent.add([tag]);
|
||||
}
|
||||
}
|
||||
@@ -622,13 +684,14 @@ export default {
|
||||
.get(function(err, data) {
|
||||
if (!data || !data.properties) return;
|
||||
|
||||
var ik = data.properties.image_key;
|
||||
mapillaryCache.detections[ik][detectionKey] = data;
|
||||
var imageKey = data.properties.image_key;
|
||||
_mlyCache.detections[imageKey][detectionKey] = data;
|
||||
|
||||
if (mapillaryImage === ik) {
|
||||
var selectedKey = _mlySelectedImage && _mlySelectedImage.key;
|
||||
if (imageKey === selectedKey) {
|
||||
var tag = makeTag(data);
|
||||
if (tag) {
|
||||
var tagComponent = mapillaryViewer.getComponent('tag');
|
||||
var tagComponent = _mlyViewer.getComponent('tag');
|
||||
tagComponent.add([tag]);
|
||||
}
|
||||
}
|
||||
@@ -683,16 +746,14 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
cache: function(_) {
|
||||
if (!arguments.length) return mapillaryCache;
|
||||
mapillaryCache = _;
|
||||
return this;
|
||||
cache: function() {
|
||||
return _mlyCache;
|
||||
},
|
||||
|
||||
|
||||
signDefs: function(_) {
|
||||
if (!arguments.length) return mapillarySignDefs;
|
||||
mapillarySignDefs = _;
|
||||
if (!arguments.length) return _mlySignDefs;
|
||||
_mlySignDefs = _;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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 _union from 'lodash-es/union';
|
||||
|
||||
import { range as d3_range } from 'd3-array';
|
||||
import { dispatch as d3_dispatch } from 'd3-dispatch';
|
||||
@@ -24,8 +25,8 @@ var apibase = 'http://openstreetcam.org',
|
||||
maxResults = 1000,
|
||||
tileZoom = 14,
|
||||
dispatch = d3_dispatch('loadedImages'),
|
||||
openstreetcamCache,
|
||||
openstreetcamImage;
|
||||
_oscCache,
|
||||
_oscSelectedImage;
|
||||
|
||||
|
||||
function abortRequest(i) {
|
||||
@@ -105,7 +106,7 @@ function loadTiles(which, url, projection) {
|
||||
|
||||
|
||||
function loadNextTilePage(which, currZoom, url, tile) {
|
||||
var cache = openstreetcamCache[which];
|
||||
var cache = _oscCache[which];
|
||||
var bbox = tile.extent.bbox();
|
||||
var maxPages = maxPageAtZoom(currZoom);
|
||||
var nextPage = cache.nextPage[tile.id] || 1;
|
||||
@@ -150,15 +151,15 @@ function loadNextTilePage(which, currZoom, url, tile) {
|
||||
captured_at: localeDateString(item.shot_date || item.date_added),
|
||||
captured_by: item.username,
|
||||
imagePath: item.lth_name,
|
||||
sequence_id: +item.sequence_id,
|
||||
sequence_id: item.sequence_id,
|
||||
sequence_index: +item.sequence_index
|
||||
};
|
||||
|
||||
// cache sequence info
|
||||
var seq = openstreetcamCache.sequences[d.sequence_id];
|
||||
var seq = _oscCache.sequences[d.sequence_id];
|
||||
if (!seq) {
|
||||
seq = { rotation: 0, images: [] };
|
||||
openstreetcamCache.sequences[d.sequence_id] = seq;
|
||||
_oscCache.sequences[d.sequence_id] = seq;
|
||||
}
|
||||
seq.images[d.sequence_index] = d;
|
||||
}
|
||||
@@ -225,7 +226,7 @@ function searchLimited(psize, limit, projection, rtree) {
|
||||
export default {
|
||||
|
||||
init: function() {
|
||||
if (!openstreetcamCache) {
|
||||
if (!_oscCache) {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
@@ -233,7 +234,7 @@ export default {
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
var cache = openstreetcamCache;
|
||||
var cache = _oscCache;
|
||||
|
||||
if (cache) {
|
||||
if (cache.images && cache.images.inflight) {
|
||||
@@ -241,18 +242,18 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
openstreetcamCache = {
|
||||
_oscCache = {
|
||||
images: { inflight: {}, loaded: {}, nextPage: {}, rtree: rbush() },
|
||||
sequences: {}
|
||||
};
|
||||
|
||||
openstreetcamImage = null;
|
||||
_oscSelectedImage = null;
|
||||
},
|
||||
|
||||
|
||||
images: function(projection) {
|
||||
var psize = 16, limit = 3;
|
||||
return searchLimited(psize, limit, projection, openstreetcamCache.images.rtree);
|
||||
return searchLimited(psize, limit, projection, _oscCache.images.rtree);
|
||||
},
|
||||
|
||||
|
||||
@@ -261,24 +262,26 @@ export default {
|
||||
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 = {};
|
||||
var sequenceKeys = {};
|
||||
|
||||
// all sequences for images in viewport
|
||||
openstreetcamCache.images.rtree.search(bbox)
|
||||
.forEach(function(d) { seq_ids[d.data.sequence_id] = true; });
|
||||
_oscCache.images.rtree.search(bbox)
|
||||
.forEach(function(d) { sequenceKeys[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)
|
||||
});
|
||||
}
|
||||
});
|
||||
Object.keys(sequenceKeys)
|
||||
.forEach(function(sequenceKey) {
|
||||
var seq = _oscCache.sequences[sequenceKey];
|
||||
var images = seq && seq.images;
|
||||
if (images) {
|
||||
lineStrings.push({
|
||||
type: 'LineString',
|
||||
coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
|
||||
properties: { key: sequenceKey }
|
||||
});
|
||||
}
|
||||
});
|
||||
return lineStrings;
|
||||
},
|
||||
|
||||
@@ -334,14 +337,14 @@ export default {
|
||||
|
||||
function rotate(deg) {
|
||||
return function() {
|
||||
if (!openstreetcamImage) return;
|
||||
var seq_id = openstreetcamImage.sequence_id;
|
||||
var seq = openstreetcamCache.sequences[seq_id];
|
||||
if (!seq) return;
|
||||
if (!_oscSelectedImage) return;
|
||||
var sequenceKey = _oscSelectedImage.sequence_id;
|
||||
var sequence = _oscCache.sequences[sequenceKey];
|
||||
if (!sequence) return;
|
||||
|
||||
var r = seq.rotation || 0;
|
||||
var r = sequence.rotation || 0;
|
||||
r += deg;
|
||||
seq.rotation = r;
|
||||
sequence.rotation = r;
|
||||
|
||||
d3_select('#photoviewer .osc-wrapper .osc-image')
|
||||
.transition()
|
||||
@@ -352,19 +355,19 @@ export default {
|
||||
|
||||
function step(stepBy) {
|
||||
return function() {
|
||||
if (!openstreetcamImage) return;
|
||||
var seq_id = openstreetcamImage.sequence_id;
|
||||
var seq = openstreetcamCache.sequences[seq_id];
|
||||
if (!seq) return;
|
||||
if (!_oscSelectedImage) return;
|
||||
var sequenceKey = _oscSelectedImage.sequence_id;
|
||||
var sequence = _oscCache.sequences[sequenceKey];
|
||||
if (!sequence) return;
|
||||
|
||||
var nextIndex = openstreetcamImage.sequence_index + stepBy;
|
||||
var nextImage = seq.images[nextIndex];
|
||||
var nextIndex = _oscSelectedImage.sequence_index + stepBy;
|
||||
var nextImage = sequence.images[nextIndex];
|
||||
if (!nextImage) return;
|
||||
|
||||
context.map().centerEase(nextImage.loc);
|
||||
|
||||
that
|
||||
.selectedImage(nextImage)
|
||||
.selectImage(nextImage)
|
||||
.updateViewer(nextImage);
|
||||
};
|
||||
}
|
||||
@@ -397,10 +400,10 @@ export default {
|
||||
.selectAll('.photo-wrapper')
|
||||
.classed('hide', true);
|
||||
|
||||
d3_selectAll('.layer-openstreetcam-images .viewfield-group')
|
||||
d3_selectAll('.viewfield-group, .sequence, .icon-sign')
|
||||
.classed('selected', false);
|
||||
|
||||
openstreetcamImage = null;
|
||||
_oscSelectedImage = null;
|
||||
return this;
|
||||
},
|
||||
|
||||
@@ -412,8 +415,8 @@ export default {
|
||||
.remove();
|
||||
|
||||
if (d) {
|
||||
var seq = openstreetcamCache.sequences[d.sequence_id];
|
||||
var r = (seq && seq.rotation) || 0;
|
||||
var sequence = _oscCache.sequences[d.sequence_id];
|
||||
var r = (sequence && sequence.rotation) || 0;
|
||||
|
||||
wrap.append('img')
|
||||
.attr('class', 'osc-image')
|
||||
@@ -457,23 +460,73 @@ export default {
|
||||
},
|
||||
|
||||
|
||||
selectedImage: function(d) {
|
||||
if (!arguments.length) return openstreetcamImage;
|
||||
openstreetcamImage = d;
|
||||
selectImage: function(d) {
|
||||
_oscSelectedImage = d;
|
||||
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('selected', function(d) {
|
||||
return d.key === openstreetcamImage.key;
|
||||
});
|
||||
this.setStyles(null, _oscSelectedImage, true);
|
||||
|
||||
d3_selectAll('.icon-sign')
|
||||
.classed('selected', false);
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
cache: function(_) {
|
||||
if (!arguments.length) return openstreetcamCache;
|
||||
openstreetcamCache = _;
|
||||
getSelectedImage: function() {
|
||||
return _oscSelectedImage;
|
||||
},
|
||||
|
||||
|
||||
getSequenceKeyForImage: function(d) {
|
||||
return d && d.sequence_id;
|
||||
},
|
||||
|
||||
|
||||
getSelectedSequenceKey: function() {
|
||||
return this.getSequenceKeyForImage(_oscSelectedImage);
|
||||
},
|
||||
|
||||
|
||||
setStyles: function(hovered, selected, reset) {
|
||||
if (reset) { // reset all layers
|
||||
d3_selectAll('.viewfield-group')
|
||||
.classed('highlighted', false)
|
||||
.classed('hovered', false)
|
||||
.classed('selected', false);
|
||||
|
||||
d3_selectAll('.sequence')
|
||||
.classed('highlighted', false)
|
||||
.classed('selected', false);
|
||||
}
|
||||
|
||||
var hoveredImageKey = hovered && hovered.key;
|
||||
var hoveredSequenceKey = this.getSequenceKeyForImage(hovered);
|
||||
var hoveredSequence = hoveredSequenceKey && _oscCache.sequences[hoveredSequenceKey];
|
||||
var hoveredImageKeys = (hoveredSequence && hoveredSequence.images.map(function (d) { return d.key; })) || [];
|
||||
|
||||
var selectedImageKey = selected && selected.key;
|
||||
var selectedSequenceKey = this.getSequenceKeyForImage(selected);
|
||||
var selectedSequence = selectedSequenceKey && _oscCache.sequences[selectedSequenceKey];
|
||||
var selectedImageKeys = (selectedSequence && selectedSequence.images.map(function (d) { return d.key; })) || [];
|
||||
|
||||
// highlight sibling viewfields on either the selected or the hovered sequences
|
||||
var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys);
|
||||
|
||||
d3_selectAll('.layer-openstreetcam-images .viewfield-group')
|
||||
.classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
|
||||
.classed('hovered', function(d) { return d.key === hoveredImageKey; })
|
||||
.classed('selected', function(d) { return d.key === selectedImageKey; });
|
||||
|
||||
d3_selectAll('.layer-openstreetcam-images .sequence')
|
||||
.classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
|
||||
.classed('selected', function(d) { return d.properties.key === selectedSequenceKey; });
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
|
||||
cache: function() {
|
||||
return _oscCache;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { services } from '../services';
|
||||
export function svgMapillaryImages(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
minViewfieldZoom = 17,
|
||||
minViewfieldZoom = 18,
|
||||
layer = d3_select(null),
|
||||
_mapillary;
|
||||
|
||||
@@ -26,7 +26,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
}
|
||||
|
||||
|
||||
function getMapillary() {
|
||||
function getService() {
|
||||
if (services.mapillary && !_mapillary) {
|
||||
_mapillary = services.mapillary;
|
||||
_mapillary.event.on('loadedImages', throttledRedraw);
|
||||
@@ -39,10 +39,10 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function showLayer() {
|
||||
var mapillary = getMapillary();
|
||||
if (!mapillary) return;
|
||||
var service = getService();
|
||||
if (!service) return;
|
||||
|
||||
mapillary.loadViewer(context);
|
||||
service.loadViewer(context);
|
||||
editOn();
|
||||
|
||||
layer
|
||||
@@ -55,9 +55,9 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function hideLayer() {
|
||||
var mapillary = getMapillary();
|
||||
if (mapillary) {
|
||||
mapillary.hideViewer();
|
||||
var service = getService();
|
||||
if (service) {
|
||||
service.hideViewer();
|
||||
}
|
||||
|
||||
throttledRedraw.cancel();
|
||||
@@ -82,18 +82,34 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function click(d) {
|
||||
var mapillary = getMapillary();
|
||||
if (!mapillary) return;
|
||||
var service = getService();
|
||||
if (!service) return;
|
||||
|
||||
context.map().centerEase(d.loc);
|
||||
|
||||
mapillary
|
||||
.selectedImage(d.key, true)
|
||||
.updateViewer(d.key, context)
|
||||
service
|
||||
.selectImage(d)
|
||||
.updateViewer(d, context)
|
||||
.showViewer();
|
||||
}
|
||||
|
||||
|
||||
function mouseover(d) {
|
||||
var service = getService();
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(d, datum);
|
||||
}
|
||||
|
||||
|
||||
function mouseout() {
|
||||
var service = getService();
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(null, datum);
|
||||
}
|
||||
|
||||
|
||||
function transform(d) {
|
||||
var t = svgPointTransform(projection)(d);
|
||||
if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
|
||||
@@ -103,10 +119,9 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
function update() {
|
||||
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 service = getService();
|
||||
var images = (service ? service.images(projection) : []);
|
||||
var sequences = (service && highZoom ? service.sequences(projection) : []);
|
||||
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
@@ -115,7 +130,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
}});
|
||||
|
||||
var lineStrings = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences);
|
||||
.data(sequences, function(d) { return d.properties.key; });
|
||||
|
||||
lineStrings.exit()
|
||||
.remove();
|
||||
@@ -138,7 +153,8 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
var enter = markers.enter()
|
||||
.append('g')
|
||||
.attr('class', 'viewfield-group')
|
||||
.classed('selected', function(d) { return d.key === imageKey; })
|
||||
.on('mouseover', mouseover)
|
||||
.on('mouseout', mouseout)
|
||||
.on('click', click);
|
||||
|
||||
markers = markers
|
||||
@@ -166,6 +182,12 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
.attr('dy', '0')
|
||||
.attr('r', '6');
|
||||
|
||||
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(null, datum);
|
||||
|
||||
|
||||
function viewfieldPath() {
|
||||
var d = this.parentNode.__data__;
|
||||
if (d.pano) {
|
||||
@@ -179,7 +201,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
function drawImages(selection) {
|
||||
var enabled = svgMapillaryImages.enabled,
|
||||
mapillary = getMapillary();
|
||||
mapillary = getService();
|
||||
|
||||
layer = selection.selectAll('.layer-mapillary-images')
|
||||
.data(mapillary ? [0] : []);
|
||||
@@ -229,7 +251,7 @@ export function svgMapillaryImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
drawImages.supported = function() {
|
||||
return !!getMapillary();
|
||||
return !!getService();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
}
|
||||
|
||||
|
||||
function getMapillary() {
|
||||
function getService() {
|
||||
if (services.mapillary && !_mapillary) {
|
||||
_mapillary = services.mapillary;
|
||||
_mapillary.event.on('loadedSigns', throttledRedraw);
|
||||
@@ -52,12 +52,12 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
|
||||
|
||||
function click(d) {
|
||||
var mapillary = getMapillary();
|
||||
if (!mapillary) return;
|
||||
var service = getService();
|
||||
if (!service) return;
|
||||
|
||||
context.map().centerEase(d.loc);
|
||||
|
||||
var selected = mapillary.selectedImage(),
|
||||
var selected = service.selectedImage(),
|
||||
imageKey;
|
||||
|
||||
// Pick one of the images the sign was detected in,
|
||||
@@ -68,17 +68,18 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
}
|
||||
});
|
||||
|
||||
mapillary
|
||||
.selectedImage(imageKey, true)
|
||||
service
|
||||
.selectImage(null, imageKey)
|
||||
.updateViewer(imageKey, context)
|
||||
.showViewer();
|
||||
}
|
||||
|
||||
|
||||
function update() {
|
||||
var mapillary = getMapillary(),
|
||||
data = (mapillary ? mapillary.signs(projection) : []),
|
||||
imageKey = mapillary ? mapillary.selectedImage() : null;
|
||||
var service = getService();
|
||||
var data = (service ? service.signs(projection) : []);
|
||||
var image = service && service.getSelectedImage();
|
||||
var imageKey = image && image.key;
|
||||
|
||||
var signs = layer.selectAll('.icon-sign')
|
||||
.data(data, function(d) { return d.key; });
|
||||
@@ -101,7 +102,7 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
enter
|
||||
.append('xhtml:body')
|
||||
.attr('class', 'icon-sign-body')
|
||||
.html(mapillary.signHTML);
|
||||
.html(service.signHTML);
|
||||
|
||||
signs
|
||||
.merge(enter)
|
||||
@@ -112,10 +113,10 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
|
||||
function drawSigns(selection) {
|
||||
var enabled = svgMapillarySigns.enabled,
|
||||
mapillary = getMapillary();
|
||||
service = getService();
|
||||
|
||||
layer = selection.selectAll('.layer-mapillary-signs')
|
||||
.data(mapillary ? [0] : []);
|
||||
.data(service ? [0] : []);
|
||||
|
||||
layer.exit()
|
||||
.remove();
|
||||
@@ -127,10 +128,10 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
.merge(layer);
|
||||
|
||||
if (enabled) {
|
||||
if (mapillary && ~~context.map().zoom() >= minZoom) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
update();
|
||||
mapillary.loadSigns(context, projection);
|
||||
service.loadSigns(context, projection);
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
@@ -152,8 +153,8 @@ export function svgMapillarySigns(projection, context, dispatch) {
|
||||
|
||||
|
||||
drawSigns.supported = function() {
|
||||
var mapillary = getMapillary();
|
||||
return (mapillary && mapillary.signsSupported());
|
||||
var service = getService();
|
||||
return (service && service.signsSupported());
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
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 { select as d3_select } from 'd3-selection';
|
||||
|
||||
import { svgPointTransform } from './point_transform';
|
||||
import { services } from '../services';
|
||||
|
||||
@@ -13,7 +14,7 @@ import { services } from '../services';
|
||||
export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000),
|
||||
minZoom = 12,
|
||||
minViewfieldZoom = 17,
|
||||
minViewfieldZoom = 18,
|
||||
layer = d3_select(null),
|
||||
_openstreetcam;
|
||||
|
||||
@@ -25,7 +26,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
}
|
||||
|
||||
|
||||
function getOpenstreetcam() {
|
||||
function getService() {
|
||||
if (services.openstreetcam && !_openstreetcam) {
|
||||
_openstreetcam = services.openstreetcam;
|
||||
_openstreetcam.event.on('loadedImages', throttledRedraw);
|
||||
@@ -38,10 +39,10 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function showLayer() {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (!openstreetcam) return;
|
||||
var service = getService();
|
||||
if (!service) return;
|
||||
|
||||
openstreetcam.loadViewer(context);
|
||||
service.loadViewer(context);
|
||||
editOn();
|
||||
|
||||
layer
|
||||
@@ -54,9 +55,9 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function hideLayer() {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (openstreetcam) {
|
||||
openstreetcam.hideViewer();
|
||||
var service = getService();
|
||||
if (service) {
|
||||
service.hideViewer();
|
||||
}
|
||||
|
||||
throttledRedraw.cancel();
|
||||
@@ -81,18 +82,34 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
function click(d) {
|
||||
var openstreetcam = getOpenstreetcam();
|
||||
if (!openstreetcam) return;
|
||||
var service = getService();
|
||||
if (!service) return;
|
||||
|
||||
context.map().centerEase(d.loc);
|
||||
|
||||
openstreetcam
|
||||
.selectedImage(d)
|
||||
service
|
||||
.selectImage(d)
|
||||
.updateViewer(d)
|
||||
.showViewer();
|
||||
}
|
||||
|
||||
|
||||
function mouseover(d) {
|
||||
var service = getService();
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(d, datum);
|
||||
}
|
||||
|
||||
|
||||
function mouseout() {
|
||||
var service = getService();
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(null, datum);
|
||||
}
|
||||
|
||||
|
||||
function transform(d) {
|
||||
var t = svgPointTransform(projection)(d);
|
||||
if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
|
||||
@@ -102,11 +119,9 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
|
||||
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 service = getService();
|
||||
var sequences = (service && highZoom ? service.sequences(projection) : []);
|
||||
var images = (service ? service.images(projection) : []);
|
||||
|
||||
var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
|
||||
var project = projection.stream;
|
||||
@@ -115,7 +130,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
}});
|
||||
|
||||
var lineStrings = layer.selectAll('.sequences').selectAll('.sequence')
|
||||
.data(sequences);
|
||||
.data(sequences, function(d) { return d.properties.key; });
|
||||
|
||||
lineStrings.exit()
|
||||
.remove();
|
||||
@@ -138,7 +153,8 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
var enter = markers.enter()
|
||||
.append('g')
|
||||
.attr('class', 'viewfield-group')
|
||||
.classed('selected', function(d) { return d.key === imageKey; })
|
||||
.on('mouseover', mouseover)
|
||||
.on('mouseout', mouseout)
|
||||
.on('click', click);
|
||||
|
||||
markers = markers
|
||||
@@ -165,15 +181,20 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
.attr('dx', '0')
|
||||
.attr('dy', '0')
|
||||
.attr('r', '6');
|
||||
|
||||
|
||||
var selected = d3_select('.viewfield-group.selected');
|
||||
var datum = selected.size() && selected.datum();
|
||||
if (service) service.setStyles(null, datum);
|
||||
}
|
||||
|
||||
|
||||
function drawImages(selection) {
|
||||
var enabled = svgOpenstreetcamImages.enabled,
|
||||
openstreetcam = getOpenstreetcam();
|
||||
service = getService();
|
||||
|
||||
layer = selection.selectAll('.layer-openstreetcam-images')
|
||||
.data(openstreetcam ? [0] : []);
|
||||
.data(service ? [0] : []);
|
||||
|
||||
layer.exit()
|
||||
.remove();
|
||||
@@ -195,10 +216,10 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
.merge(layer);
|
||||
|
||||
if (enabled) {
|
||||
if (openstreetcam && ~~context.map().zoom() >= minZoom) {
|
||||
if (service && ~~context.map().zoom() >= minZoom) {
|
||||
editOn();
|
||||
update();
|
||||
openstreetcam.loadImages(projection);
|
||||
service.loadImages(projection);
|
||||
} else {
|
||||
editOff();
|
||||
}
|
||||
@@ -220,7 +241,7 @@ export function svgOpenstreetcamImages(projection, context, dispatch) {
|
||||
|
||||
|
||||
drawImages.supported = function() {
|
||||
return !!getOpenstreetcam();
|
||||
return !!getService();
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ describe('iD.serviceMapillary', function() {
|
||||
|
||||
describe('#reset', function() {
|
||||
it('resets cache and image', function() {
|
||||
mapillary.cache({foo: 'bar'});
|
||||
mapillary.selectedImage('baz');
|
||||
mapillary.cache().foo = 'bar';
|
||||
mapillary.selectImage({key: 'baz'});
|
||||
|
||||
mapillary.reset();
|
||||
expect(mapillary.cache()).to.not.have.property('foo');
|
||||
expect(mapillary.selectedImage()).to.be.null;
|
||||
expect(mapillary.getSelectedImage()).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -377,9 +377,9 @@ describe('iD.serviceMapillary', function() {
|
||||
};
|
||||
|
||||
mapillary.cache().sequences.lineString['-'] = gj;
|
||||
mapillary.cache().sequences.forImage['0'] = '-';
|
||||
mapillary.cache().sequences.forImage['1'] = '-';
|
||||
mapillary.cache().sequences.forImage['2'] = '-';
|
||||
mapillary.cache().sequences.forImageKey['0'] = '-';
|
||||
mapillary.cache().sequences.forImageKey['1'] = '-';
|
||||
mapillary.cache().sequences.forImageKey['2'] = '-';
|
||||
|
||||
var res = mapillary.sequences(context.projection);
|
||||
expect(res).to.deep.eql([gj]);
|
||||
@@ -430,10 +430,10 @@ describe('iD.serviceMapillary', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectedImage', function() {
|
||||
it('sets and gets selected image', function() {
|
||||
mapillary.selectedImage('foo');
|
||||
expect(mapillary.selectedImage()).to.eql('foo');
|
||||
describe('#selectImage', function() {
|
||||
it('gets and sets the selected image', function() {
|
||||
mapillary.selectImage('foo');
|
||||
expect(mapillary.getSelectedImage()).to.eql('foo');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -62,12 +62,12 @@ describe('iD.serviceOpenstreetcam', function() {
|
||||
|
||||
describe('#reset', function() {
|
||||
it('resets cache and image', function() {
|
||||
openstreetcam.cache({foo: 'bar'});
|
||||
openstreetcam.selectedImage('baz');
|
||||
openstreetcam.cache().foo = 'bar';
|
||||
openstreetcam.selectImage({key: 'baz'});
|
||||
|
||||
openstreetcam.reset();
|
||||
expect(openstreetcam.cache()).to.not.have.property('foo');
|
||||
expect(openstreetcam.selectedImage()).to.be.null;
|
||||
expect(openstreetcam.getSelectedImage()).to.be.null;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -251,27 +251,27 @@ describe('iD.serviceOpenstreetcam', function() {
|
||||
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 } }
|
||||
{ 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 }
|
||||
{ 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 } }
|
||||
{ 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);
|
||||
@@ -284,9 +284,9 @@ describe('iD.serviceOpenstreetcam', 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, 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 } }
|
||||
{ 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);
|
||||
@@ -295,15 +295,16 @@ describe('iD.serviceOpenstreetcam', function() {
|
||||
var res = openstreetcam.sequences(context.projection);
|
||||
expect(res).to.deep.eql([{
|
||||
type: 'LineString',
|
||||
coordinates: [[10,0], [10,0], [10,1]]
|
||||
coordinates: [[10,0], [10,0], [10,1]],
|
||||
properties: { key: '100' }
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectedImage', function() {
|
||||
it('sets and gets selected image', function() {
|
||||
openstreetcam.selectedImage('foo');
|
||||
expect(openstreetcam.selectedImage()).to.eql('foo');
|
||||
openstreetcam.selectImage('foo');
|
||||
expect(openstreetcam.getSelectedImage()).to.eql('foo');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user