From 3facc289288ba169c01e65fd55ef1194f50aac47 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 9 Feb 2016 17:19:06 -0500 Subject: [PATCH] Improve layer transitions, debounce redraws, other goodness --- js/id/renderer/mapillary_image_layer.js | 120 +++++++++++++---------- js/id/renderer/mapillary_sign_layer.js | 121 ++++++++++++++---------- js/id/services/mapillary.js | 27 ++++-- 3 files changed, 161 insertions(+), 107 deletions(-) diff --git a/js/id/renderer/mapillary_image_layer.js b/js/id/renderer/mapillary_image_layer.js index d8d584219..4272a49c2 100644 --- a/js/id/renderer/mapillary_image_layer.js +++ b/js/id/renderer/mapillary_image_layer.js @@ -1,28 +1,51 @@ iD.MapillaryImageLayer = function(context) { var mapillary = iD.services.mapillary(), + debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), rtree = rbush(), enabled = false, - selectedImage, layer; - function show(image) { + function showThumbnail(imageKey) { + var thumb = mapillary.selectedThumbnail(); layer.selectAll('g') - .classed('selected', function(d) { - return selectedImage && d.key === selectedImage.key; - }); + .classed('selected', function(d) { return d.key === thumb; }); - mapillary.showThumbnail(context.container(), image); + mapillary.showThumbnail(context.container(), imageKey); } - function hide() { - selectedImage = undefined; + function hideThumbnail() { layer.selectAll('g') .classed('selected', false); mapillary.hideThumbnail(context.container()); } + function showLayer() { + layer + .style('display', 'block') + .style('opacity', 0) + .transition() + .duration(500) + .style('opacity', 1) + .each('end', debouncedRedraw); + } + + function hideLayer() { + debouncedRedraw.cancel(); + hideThumbnail(); + layer + .transition() + .duration(500) + .style('opacity', 0) + .each('end', function() { + layer + .style('display', 'none') + .selectAll('g') + .remove(); + }); + } + function transform(d) { var t = iD.svg.PointTransform(context.projection)(d); if (d.ca) t += ' rotate(' + Math.floor(d.ca) + ',0,0)'; @@ -30,34 +53,36 @@ iD.MapillaryImageLayer = function(context) { } function imagesLoaded(data) { + if (!data.features.length) return; + var images = [], - sequence, loc; + image, loc; for (var i = 0; i < data.features.length; i++) { - sequence = data.features[i]; - for (var j = 0; j < sequence.geometry.coordinates.length; j++) { - loc = sequence.geometry.coordinates[j]; - images.push([loc[0], loc[1], loc[0], loc[1], { - key: sequence.properties.keys[j], - ca: sequence.properties.cas[j], - loc: loc - }]); - } + image = data.features[i]; + loc = image.geometry.coordinates; + images.push([loc[0], loc[1], loc[0], loc[1], { + key: image.properties.key, + ca: image.properties.ca, + loc: loc + }]); } rtree.load(images); + debouncedRedraw(); } - function update() { - var images = rtree + function drawMarkers() { + var data = rtree .search(context.map().extent().rectangle()) .map(function(d) { return d[4]; }); - var g = layer.selectAll('g') - .data(images, function(d) { return d.key; }); + var markers = layer.selectAll('g') + .data(data, function(d) { return d.key; }); // Enter - var enter = g.enter().append('g') + var enter = markers.enter() + .append('g') .attr('class', 'image'); enter.append('path') @@ -71,9 +96,11 @@ iD.MapillaryImageLayer = function(context) { .attr('r', '6'); // Update - g.attr('transform', transform); + markers + .attr('transform', transform); - g.exit() + // Exit + markers.exit() .remove(); } @@ -85,40 +112,30 @@ iD.MapillaryImageLayer = function(context) { // Enter layer.enter() .append('svg') - .on('click', function() { - var image = d3.event.target.__data__; - if (selectedImage === image) { - hide(); + .style('display', enabled ? 'block' : 'none') + .on('click', function() { // deselect/select + var imageKey = d3.event.target.__data__.key; + if (imageKey === mapillary.selectedThumbnail()) { + hideThumbnail(); } else { - selectedImage = image; - show(image); + mapillary.selectedThumbnail(imageKey); + showThumbnail(imageKey); } }) .on('mouseover', function() { - show(d3.event.target.__data__); + showThumbnail(d3.event.target.__data__.key); }) .on('mouseout', function() { - if (selectedImage) { - show(selectedImage); + var thumb = mapillary.selectedThumbnail(); + if (thumb) { + showThumbnail(thumb); } else { - hide(); + hideThumbnail(); } }); - - // Update - layer - .style('display', enabled ? 'block' : 'none'); - - if (!enabled) { - hide(); - layer.selectAll('g') - .transition() - .duration(200) - .style('opacity', 0) - .remove(); - } else { - update(); + if (enabled) { + drawMarkers(); mapillary.loadImages(context.projection, layer.dimensions()); } } @@ -126,6 +143,11 @@ iD.MapillaryImageLayer = function(context) { render.enable = function(_) { if (!arguments.length) return enabled; enabled = _; + if (enabled) { + showLayer(); + } else { + hideLayer(); + } return render; }; diff --git a/js/id/renderer/mapillary_sign_layer.js b/js/id/renderer/mapillary_sign_layer.js index 05b578b9a..fb1473cbf 100644 --- a/js/id/renderer/mapillary_sign_layer.js +++ b/js/id/renderer/mapillary_sign_layer.js @@ -1,29 +1,54 @@ iD.MapillarySignLayer = function(context) { var mapillary = iD.services.mapillary(), + debouncedRedraw = _.debounce(function () { context.pan([0,0]); }, 1000), rtree = rbush(), enabled = false, - selectedImage, layer; - function show(image) { + function showThumbnail(imageKey) { + var thumb = mapillary.selectedThumbnail(); layer.selectAll('.icon-sign') - .classed('selected', function(d) { - return selectedImage && d.key === selectedImage.key; - }); + .classed('selected', function(d) { return d.key === thumb; }); - mapillary.showThumbnail(context.container(), image); + mapillary.showThumbnail(context.container(), imageKey); } - function hide() { - selectedImage = undefined; + function hideThumbnail() { layer.selectAll('.icon-sign') .classed('selected', false); mapillary.hideThumbnail(context.container()); } + function showLayer() { + layer + .style('display', 'block') + .style('opacity', 0) + .transition() + .duration(500) + .style('opacity', 1) + .each('end', debouncedRedraw); + } + + function hideLayer() { + debouncedRedraw.cancel(); + hideThumbnail(); + layer + .transition() + .duration(500) + .style('opacity', 0) + .each('end', function() { + layer + .style('display', 'none') + .selectAll('.icon-sign') + .remove(); + }); + } + function signsLoaded(data) { + if (!data.features.length) return; + var signs = [], sign, loc; @@ -38,84 +63,78 @@ iD.MapillarySignLayer = function(context) { } rtree.load(signs); + debouncedRedraw(); } + function drawSigns() { + var data = rtree + .search(context.map().extent().rectangle()) + .map(function(d) { return d[4]; }); - function update() { - var signs = rtree - .search(context.map().extent().rectangle()) - .map(function(d) { return d[4]; }); - - var signGroups = layer.selectAll('.mapillary-sign') - .data(signs, function(d) { return d.key; }); + var signs = layer.select('.mapillary-sign-offset') + .selectAll('.icon-sign') + .data(data, function(d) { return d.key; }); // Enter - var enter = signGroups.enter() - .append('g') - .attr('class', 'mapillary-sign') - .attr('transform', 'translate(-15, -15)') + signs.enter() .append('foreignObject') .attr('class', 'icon-sign') .append('xhtml:body') - .html(mapillary.signHTML); - - enter - .on('click', function(d) { - if (d === selectedImage) { - hide(); + .html(mapillary.signHTML) + .on('click', function(d) { // deselect/select + if (d.key === mapillary.selectedThumbnail()) { + hideThumbnail(); } else { - selectedImage = d; - show(d); + mapillary.selectedThumbnail(d.key); + showThumbnail(d.key); } }) - .on('mouseover', show) + .on('mouseover', function(d) { + showThumbnail(d.key); + }) .on('mouseout', function() { - if (selectedImage) { - show(selectedImage); + var thumb = mapillary.selectedThumbnail(); + if (thumb) { + showThumbnail(thumb); } else { - hide(); + hideThumbnail(); } }); // Update - signGroups - .select('.icon-sign') + signs .attr('transform', iD.svg.PointTransform(context.projection)); - signGroups.exit() + // Exit + signs.exit() .remove(); } - function render(selection) { layer = selection.selectAll('svg') .data([0]); - // Enter layer.enter() - .append('svg'); + .append('svg') + .style('display', enabled ? 'block' : 'none') + .append('g') + .attr('class', 'mapillary-sign-offset') + .attr('transform', 'translate(-15, -15)'); // center signs on loc - // Update - layer - .style('display', enabled ? 'block' : 'none'); - - if (!enabled) { - hide(); - layer.selectAll('.mapillary-sign') - .transition() - .duration(200) - .style('opacity', 0) - .remove(); - } else { - update(); + if (enabled) { + drawSigns(); mapillary.loadSigns(context, context.projection, layer.dimensions()); } } - render.enable = function(_) { if (!arguments.length) return enabled; enabled = _; + if (enabled) { + showLayer(); + } else { + hideLayer(); + } return render; }; diff --git a/js/id/services/mapillary.js b/js/id/services/mapillary.js index ac844c625..599acbc2c 100644 --- a/js/id/services/mapillary.js +++ b/js/id/services/mapillary.js @@ -5,7 +5,9 @@ iD.services.mapillary = function() { urlImage = 'https://www.mapillary.com/map/im/', urlThumb = 'https://d1cuyjsrcm0gby.cloudfront.net/', clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi', - tileZoom = 17; + tileZoom = 14, + selectedThumbnail; + function loadSignDefs(context) { if (!iD.services.mapillary.sign_defs) { @@ -67,6 +69,9 @@ iD.services.mapillary = function() { if (which.loaded[id] || which.inflight[id]) return; + var what = (which === cache.images ? 'images' : 'signs'); + console.log('requesting ' + what + ' tile ' + id); + which.inflight[id] = d3.json(url + iD.util.qsString({ geojson: 'true', @@ -91,7 +96,7 @@ iD.services.mapillary = function() { mapillary.loadImages = function(projection, dimensions) { var cache = iD.services.mapillary.cache, - url = apibase + 'search/s/geojson?'; + url = apibase + 'search/im/geojson?'; loadTiles(cache.images, url, projection, dimensions); }; @@ -112,8 +117,8 @@ iD.services.mapillary = function() { return iD.services.mapillary.sign_defs[country][type]; }; - mapillary.showThumbnail = function(selection, image) { - if (!(image && image.key)) return; + mapillary.showThumbnail = function(selection, imageKey) { + if (!imageKey) return; var thumbnail = selection.selectAll('.mapillary-image') .data([0]); @@ -123,7 +128,9 @@ iD.services.mapillary = function() { .attr('class', 'mapillary-image'); enter.append('button') - .on('click', function () { mapillary.hideThumbnail(selection) }) + .on('click', function () { + mapillary.hideThumbnail(selection); + }) .append('div') .call(iD.svg.Icon('#icon-close')); @@ -143,14 +150,15 @@ iD.services.mapillary = function() { .style('opacity', 1); thumbnail.selectAll('img') - .attr('src', urlThumb + image.key + '/thumb-320.jpg'); + .attr('src', urlThumb + imageKey + '/thumb-320.jpg'); thumbnail.selectAll('a') - .attr('href', urlImage + image.key); + .attr('href', urlImage + imageKey); }; mapillary.hideThumbnail = function(selection) { + selectedThumbnail = null; selection.selectAll('.mapillary-image') .transition() .duration(200) @@ -158,6 +166,11 @@ iD.services.mapillary = function() { .remove(); }; + mapillary.selectedThumbnail = function(imageKey) { + if (!arguments.length) return selectedThumbnail; + selectedThumbnail = imageKey; + }; + mapillary.reset = function() { var cache = iD.services.mapillary.cache;