Rewrite as a separate layer

This commit is contained in:
John Firebaugh
2014-09-14 14:28:08 -07:00
parent 3c9ec76b33
commit 0b1ec49402
18 changed files with 242 additions and 341 deletions
+11 -18
View File
@@ -717,48 +717,41 @@ a:hover .icon.out-link { background-position: -500px -14px;}
bottom: 0;
}
#mapillaryImage {
.mapillary-image {
position: absolute;
right: 0;
bottom: 30px;
width: 330px;
height: 250px;
padding: 5px;
background-color: #fff;
}
#mapillaryImage > div {
position: relative;
}
#mapillaryImage > a {
.mapillary-image a {
display: block;
width: 100%;
height: auto;
color: white;
position: relative;
}
#mapillaryImage .link {
background-color: rgba(0,0,0,.5);
position: absolute;
height: auto;
background-color: rgba(0,0,0,.5);
bottom: 0;
right: 0;
padding: 5px 10px;
}
#mapillaryImage img {
.mapillary-image img {
width: 100%;
height: auto;
display: block;
}
#mapillaryImage.hidden {
visibility: hidden;
.mapillary-image.hidden {
visibility: hidden;
}
#mapillaryImage.temp button {
.mapillary-image.temp button {
display: none;
}
#mapillaryImage button {
.mapillary-image button {
border-radius: 0;
padding: 5px;
position: absolute;
+21 -19
View File
@@ -1140,32 +1140,34 @@ text.gpx {
fill:#FF26D4;
}
/* Mapillary Sequences */
/* Mapillary Layer */
g.image.point {
cursor: pointer; /* Opera */
cursor: url(img/cursor-select-mapillary.png) 6 1, pointer; /* FF */
.layer-mapillary {
pointer-events: none;
}
g.image.point path,
g.image.point circle {
stroke-width: 2;
stroke: #ffc600;
fill: #ffc600;
.layer-mapillary g {
pointer-events: visible;
cursor: pointer; /* Opera */
cursor: url(img/cursor-select-mapillary.png) 6 1, pointer; /* FF */
}
g.image.point:hover circle,
g.image.point:hover path {
fill: #ff9900;
stroke-width: 2;
stroke: #ff9900;
.layer-mapillary g * {
stroke-width: 2;
stroke: #ffc600;
fill: #ffc600;
}
g.image.point.selected circle,
g.image.point.selected path {
fill: #ff5800;
stroke-width: 4;
stroke: #ff5800;
.layer-mapillary g:hover * {
stroke-width: 2;
stroke: #ff9900;
fill: #ff9900;
}
.layer-mapillary g.selected * {
stroke-width: 4;
stroke: #ff5800;
fill: #ff5800;
}
/* Modes */
+2 -7
View File
@@ -19,9 +19,6 @@ en:
tail: Click to add nodes to your area. Click the first node to finish the area.
draw_line:
tail: "Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."
selectImage:
title: Photos
description: "Choose and fix a Mapillary image for mapping. Shortcut: 'm'"
operations:
add:
annotation:
@@ -296,11 +293,9 @@ en:
zoom: "Zoom to GPX track"
browse: "Browse for a .gpx file"
mapillary:
tooltip: "Mapillary street photos"
title: "Photo overlay"
tooltip: "Street-level photos from Mapillary"
title: "Photo Overlay (Mapillary)"
view_on_mapillary: "View this image on Mapillary"
no_image_found: |
No image found on <a href="http://mapillary.com/" target="_blank">Mapillary</a>. Go and take some!
help:
title: "Help"
help: |
+3 -8
View File
@@ -24,10 +24,6 @@
},
"draw_line": {
"tail": "Click to add more nodes to the line. Click on other lines to connect to them, and double-click to end the line."
},
"selectImage": {
"title": "Photos",
"description": "Choose and fix a Mapillary image for mapping. Shortcut: 'm'"
}
},
"operations": {
@@ -359,10 +355,9 @@
"browse": "Browse for a .gpx file"
},
"mapillary": {
"tooltip": "Mapillary street photos",
"title": "Photo overlay",
"view_on_mapillary": "View this image on Mapillary",
"no_image_found": "No image found on <a href=\"http://mapillary.com/\" target=\"_blank\">Mapillary</a>. Go and take some!\n"
"tooltip": "Street-level photos from Mapillary",
"title": "Photo Overlay (Mapillary)",
"view_on_mapillary": "View this image on Mapillary"
},
"help": {
"title": "Help",
+1 -3
View File
@@ -58,6 +58,7 @@
<script src='js/id/renderer/gpx_layer.js'></script>
<script src='js/id/renderer/tile_layer.js'></script>
<script src='js/id/renderer/map.js'></script>
<script src='js/id/renderer/mapillary_layer.js'></script>
<script src="js/id/svg.js"></script>
<script src="js/id/svg/areas.js"></script>
@@ -66,7 +67,6 @@
<script src="js/id/svg/lines.js"></script>
<script src="js/id/svg/midpoints.js"></script>
<script src="js/id/svg/points.js"></script>
<script src="js/id/svg/sequences.js"></script>
<script src="js/id/svg/surface.js"></script>
<script src="js/id/svg/tag_classes.js"></script>
<script src="js/id/svg/turns.js"></script>
@@ -77,7 +77,6 @@
<script src='js/id/ui/attribution.js'></script>
<script src='js/id/ui/radial_menu.js'></script>
<script src='js/id/ui/inspector.js'></script>
<script src='js/id/ui/image_view.js'></script>
<script src='js/id/ui/modal.js'></script>
<script src='js/id/ui/cmd.js'></script>
<script src='js/id/ui/confirm.js'></script>
@@ -190,7 +189,6 @@
<script src='js/id/modes/rotate_way.js'></script>
<script src='js/id/modes/save.js'></script>
<script src='js/id/modes/select.js'></script>
<script src='js/id/modes/select_image.js'></script>
<script src='js/id/operations.js'></script>
<script src='js/id/operations/continue.js'></script>
-7
View File
@@ -214,13 +214,6 @@ window.iD = function () {
context.zoomIn = map.zoomIn;
context.zoomOut = map.zoomOut;
/* Mapillary image view */
var imageView = iD.ui.ImageView(context);
context.imageView = function() {
return imageView;
};
context.surfaceRect = function() {
// Work around a bug in Firefox.
// http://stackoverflow.com/questions/18153989/
-97
View File
@@ -1,97 +0,0 @@
iD.modes.SelectImage = function (context) {
var mode = {
button: 'selectImage',
id: 'selectImage',
title: t('modes.selectImage.title'),
description: t('modes.selectImage.description'),
key: 'm'
}, imageView, currentImage;
function click() {
var datum = d3.event.target.__data__;
if (isImage(datum)) {
if (currentImage === datum) {
context.surface().selectAll('.image.point')
.classed('selected', false);
currentImage = undefined;
} else {
currentImage = datum;
context.surface().selectAll('.image.point')
.classed('selected', function(d) {
return d === datum;
});
context.container()
.select('#mapillaryImage')
.classed('temp', false);
imageView.show(currentImage);
}
}
}
function isImage(datum) {
return datum &&
datum.properties !== undefined &&
datum.properties.entityType === 'image';
}
mode.enter = function () {
context.map().enableSequences(true);
context.container()
.select('#select_image_checkbox')
.attr('checked','checked');
// Get focus on the body.
if (document.activeElement && document.activeElement.blur) {
document.activeElement.blur();
}
imageView = context.imageView();
imageView.showEmpty();
context.surface()
.on('click.image', click)
.on('mouseover.image', function () {
var datum = d3.event.target.__data__;
if (isImage(datum)) {
imageView.show(datum);
if (currentImage !== datum) {
context.container()
.select('#mapillaryImage')
.classed('temp', true);
}
}
})
.on('mouseout.image', function () {
var datum = d3.event.target.__data__;
if (isImage(datum)) {
if (currentImage) {
imageView.show(currentImage);
} else {
imageView.showEmpty();
}
}
});
};
mode.exit = function () {
context.map().enableSequences(false);
context.container().select('#select_image_checkbox')
.attr('checked', null);
if (!currentImage) {
context.container()
.select('#mapillaryImage')
.classed('hidden', true)
.classed('temp', false);
}
context.surface().select('defs').selectAll('marker.arrow')
.remove();
context.surface().select('.layer-hit').selectAll('g.image')
.remove();
context.surface().select('.layer-hit').selectAll('g.sequence')
.remove();
};
return mode;
};
+19
View File
@@ -4,6 +4,7 @@ iD.Background = function(context) {
.projection(context.projection),
gpxLayer = iD.GpxLayer(context, dispatch)
.projection(context.projection),
mapillaryLayer = iD.MapillaryLayer(context),
overlayLayers = [];
var backgroundSources = iD.data.imagery.map(function(source) {
@@ -91,6 +92,14 @@ iD.Background = function(context) {
overlays.exit()
.remove();
var mapillary = selection.selectAll('.layer-mapillary')
.data([0]);
mapillary.enter().insert('div')
.attr('class', 'layer-layer layer-mapillary');
mapillary.call(mapillaryLayer);
}
background.sources = function(extent) {
@@ -102,6 +111,7 @@ iD.Background = function(context) {
background.dimensions = function(_) {
baseLayer.dimensions(_);
gpxLayer.dimensions(_);
mapillaryLayer.dimensions(_);
overlayLayers.forEach(function(layer) {
layer.dimensions(_);
@@ -166,6 +176,15 @@ iD.Background = function(context) {
dispatch.change();
};
background.showsMapillaryLayer = function() {
return mapillaryLayer.enable();
};
background.toggleMapillaryLayer = function() {
mapillaryLayer.enable(!mapillaryLayer.enable());
dispatch.change();
};
background.showsLayer = function(d) {
return d === baseLayer.source() ||
(d.id === 'custom' && baseLayer.source().id === 'custom') ||
+2 -8
View File
@@ -18,7 +18,6 @@ iD.Map = function(context) {
areas = iD.svg.Areas(projection),
midpoints = iD.svg.Midpoints(roundedProjection, context),
labels = iD.svg.Labels(projection, context),
sequences = iD.svg.Sequences(projection, context),
supersurface, surface,
mouse,
mousemove;
@@ -34,8 +33,6 @@ iD.Map = function(context) {
supersurface = selection.append('div')
.attr('id', 'supersurface');
supersurface.call(context.background());
// Need a wrapper div because Opera can't cope with an absolutely positioned
// SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
var dataLayer = supersurface.append('div')
@@ -53,6 +50,8 @@ iD.Map = function(context) {
.attr('id', 'surface')
.call(iD.svg.Surface(context));
supersurface.call(context.background());
surface.on('mousemove.map', function() {
mousemove = d3.event;
});
@@ -114,7 +113,6 @@ iD.Map = function(context) {
.call(vertices, graph, all, filter, map.extent(), map.zoom())
.call(lines, graph, all, filter)
.call(areas, graph, all, filter)
.call(sequences, surface)
.call(midpoints, graph, all, filter, map.trimmedExtent())
.call(labels, graph, all, filter, dimensions, !difference && !extent);
@@ -402,9 +400,5 @@ iD.Map = function(context) {
return map;
};
map.enableSequences = function (enable) {
sequences.enable(enable);
};
return d3.rebind(map, dispatch, 'on');
};
+159
View File
@@ -0,0 +1,159 @@
iD.MapillaryLayer = function (context) {
var enable = false,
currentImage,
svg, div, request;
function show(image) {
svg.selectAll('g')
.classed('selected', function(d) {
return currentImage && d.key === currentImage.key;
});
div.classed('hidden', false)
.classed('temp', image !== currentImage);
div.selectAll('img')
.attr('src', 'https://d1cuyjsrcm0gby.cloudfront.net/' + image.key + '/thumb-320.jpg');
div.selectAll('a')
.attr('href', 'http://mapillary.com/map/im/' + image.key);
}
function hide() {
currentImage = undefined;
svg.selectAll('g')
.classed('selected', false);
div.classed('hidden', true);
}
function transform(image) {
var t = 'translate(' + context.projection(image.loc) + ')';
if (image.ca) t += 'rotate(' + image.ca + ',0,0)';
return t;
}
function render(selection) {
svg = selection.selectAll('svg')
.data([0]);
svg.enter().append('svg')
.on('click', function() {
var image = d3.event.target.__data__;
if (currentImage === image) {
hide();
} else {
currentImage = image;
show(image);
}
})
.on('mouseover', function() {
show(d3.event.target.__data__);
})
.on('mouseout', function() {
if (currentImage) {
show(currentImage);
} else {
hide();
}
});
svg.style('display', enable ? 'block' : 'none');
div = context.container().selectAll('.mapillary-image')
.data([0]);
var enter = div.enter().append('div')
.attr('class', 'mapillary-image');
enter.append('button')
.on('click', hide)
.append('div')
.attr('class', 'icon close');
enter.append('img');
var link = enter.append('a')
.attr('class', 'link')
.attr('target', '_blank');
link.append('span')
.attr('class', 'icon icon-pre-text out-link');
link.append('span')
.text(t('mapillary.view_on_mapillary'));
if (!enable) {
hide();
svg.selectAll('g')
.remove();
return;
}
// Update existing images while waiting for new ones to load.
svg.selectAll('g')
.attr('transform', transform);
var extent = context.map().extent();
if (request)
request.abort();
request = d3.json('https://mapillary-read-api.herokuapp.com/v1/s/search?min-lat=' +
extent[0][1] + '&max-lat=' + extent[1][1] + '&min-lon=' +
extent[0][0] + '&max-lon=' + extent[1][0] + '&max-results=100&geojson=true',
function (error, data) {
if (error) return;
var images = [];
for (var i = 0; i < data.features.length; i++) {
var sequence = data.features[i];
for (var j = 0; j < sequence.geometry.coordinates.length; j++) {
images.push({
key: sequence.properties.keys[j],
ca: sequence.properties.cas[j],
loc: sequence.geometry.coordinates[j]
});
if (images.length >= 1000) break;
}
}
var g = svg.selectAll('g')
.data(images, function(d) { return d.key; });
var enter = g.enter().append('g')
.attr('class', 'image');
enter.append('path')
.attr('d', 'M 0,-5 l 0,-20 l -5,30 l 10,0 l -5,-30');
enter.append('circle')
.attr('dx', '0')
.attr('dy', '0')
.attr('r', '8');
g.attr('transform', transform);
g.exit()
.remove();
});
}
render.enable = function(_) {
if (!arguments.length) return enable;
enable = _;
return render;
};
render.dimensions = function(_) {
if (!arguments.length) return svg.dimensions();
svg.dimensions(_);
return render;
};
return render;
};
-100
View File
@@ -1,100 +0,0 @@
iD.svg.Sequences = function (projection, context) {
var surface, enabled = false;
function drawSequences(_surface) {
surface = _surface;
if (enabled) {
drawSequences.reloadMapillaryImages();
} else {
drawSequences.removeAll();
}
}
drawSequences.removeAll = function () {
var hit_layer = surface.select('.layer-hit');
if (hit_layer) {
hit_layer.selectAll('g.image').remove();
hit_layer.selectAll('g.sequence').remove();
}
};
drawSequences.enable = function (enable) {
enabled = enable;
drawSequences(surface);
};
drawSequences.plotSequences = function (surface, context, sequences) {
var imagePoints = drawSequences.images(sequences, 1000);
var images = surface.select('.layer-hit').selectAll('g.image')
.data(imagePoints);
var pointTransform = iD.svg.PointTransform(context.projection);
var image = images.enter()
.append('g')
.attr('class', 'image point')
.attr('transform', function (d) {
var translate = pointTransform({ loc: d.geometry.coordinates });
if (d.properties.ca) {
return translate + 'rotate(' + d.properties.ca + ',0,0)';
}
return translate;
});
image.append('path')
.call(drawSequences.markerPath, 'stroke');
image.append('circle')
.attr('dx', '0')
.attr('dy', '0')
.attr('r', '8');
// Selecting the following implicitly
// sets the data (point entity) on the element
images.select('.shadow');
images.select('.stroke');
};
drawSequences.reloadMapillaryImages = function () {
var extent = context.map().extent();
d3.json('https://mapillary-read-api.herokuapp.com/v1/s/search?min-lat=' +
extent[0][1] + '&max-lat=' + extent[1][1] + '&min-lon=' +
extent[0][0] + '&max-lon=' + extent[1][0] +
'&max-results=100&geojson=true', function (error, data) {
drawSequences.plotSequences(context.surface(), context, data);
});
};
drawSequences.images = function (sequences, limit) {
var images = [];
for (var i = 0; i < sequences.features.length; i++) {
var sequence = sequences.features[i];
for (var j = 0; j < sequence.geometry.coordinates.length; j++) {
images.push({
geometry: {
type: 'Point',
coordinates: sequence.geometry.coordinates[j]
},
properties: {
key: sequence.properties.keys[j],
ca: sequence.properties.cas[j],
entityType: 'image'
}
});
if (limit && images.length >= limit) break;
}
}
return images;
};
drawSequences.markerPath = function (selection, klass) {
selection
.attr('class', klass)
.attr('transform', 'translate(0, 0)')
.attr('d', 'M 0,-5 l 0,-20 l -5,30 l 10,0 l -5,-30');
};
return drawSequences;
};
-4
View File
@@ -31,10 +31,6 @@ iD.ui = function(context) {
var m = content.append('div')
.attr('id', 'map')
.call(map);
content.append('div')
.attr('id', 'mapillaryImage')
.classed('hidden', true)
.call(iD.ui.ImageView(context));
bar.append('div')
.attr('class', 'spacer col4');
+17 -15
View File
@@ -77,6 +77,11 @@ iD.ui.Background = function(context) {
update();
}
function clickMapillary() {
context.background().toggleMapillaryLayer();
update();
}
function drawList(layerList, type, change, filter) {
var sources = context.background()
.sources(context.map().extent())
@@ -124,6 +129,13 @@ iD.ui.Background = function(context) {
.property('disabled', !hasGpx)
.property('checked', showsGpx);
var showsMapillary = context.background().showsMapillaryLayer();
mapillaryLayerItem
.classed('active', showsMapillary)
.selectAll('input')
.property('checked', showsMapillary);
selectLayer();
var source = context.background().baseLayerSource();
@@ -266,26 +278,16 @@ iD.ui.Background = function(context) {
var mapillaryLayerItem = overlayList.append('li');
var mapillaryLabel = mapillaryLayerItem.append('label')
label = mapillaryLayerItem.append('label')
.call(bootstrap.tooltip()
.title(t('modes.selectImage.description'))
.title(t('mapillary.tooltip'))
.placement('top'));
mapillaryLabel.append('input')
label.append('input')
.attr('type', 'checkbox')
.attr('id', 'select_image_checkbox')
.on('change', function(){
if (this.checked) {
mapillaryLayerItem.classed('active',true);
context.enter(iD.modes.SelectImage(context));
} else {
mapillaryLayerItem.classed('active',false);
context.enter(iD.modes.Browse(context));
}
update();
});
.on('change', clickMapillary);
mapillaryLabel.append('span')
label.append('span')
.text(t('mapillary.title'));
var gpxLayerItem = content.append('ul')
-50
View File
@@ -1,50 +0,0 @@
iD.ui.ImageView = function (context) {
function imageView() { }
imageView.showEmpty = function () {
var imageWrapper = context.container()
.select('#mapillaryImage');
imageWrapper.html('');
var content = imageWrapper
.append('div');
content.append('div')
.on('click', function(){
imageWrapper.classed('hidden', true);
});
content.append('div')
.html(marked(t('mapillary.no_image_found')));
};
imageView.show = function (imageToShow) {
var key = imageToShow.properties.key;
var imageWrapper = context.container().select('#mapillaryImage');
imageWrapper.classed('hidden', false);
imageWrapper.html('');
var content = imageWrapper
.append('div');
content.append('button')
.on('click', function(){
imageWrapper.classed('hidden', true);
})
.append('div')
.attr('class', 'icon close');
var wrap = content.append('div');
wrap.append('div')
.append('img')
.attr('src', 'https://d1cuyjsrcm0gby.cloudfront.net/KEY/thumb-320.jpg'.replace('KEY', key));
var wrapLink = wrap.append('a')
.attr('class', 'link')
.attr('target', '_blank')
.attr('src', 'http://mapillary.com/map/im/KEY'.replace('KEY', key));
wrapLink.append('span')
.attr('class','icon icon-pre-text out-link');
wrapLink.append('span')
.text(t('mapillary.view_on_mapillary'));
};
return imageView;
};
+1 -3
View File
@@ -55,12 +55,12 @@
<script src='../js/id/renderer/map.js'></script>
<script src='../js/id/renderer/gpx_layer.js'></script>
<script src='../js/id/renderer/tile_layer.js'></script>
<script src='../js/id/renderer/mapillary_layer.js'></script>
<script src="../js/id/svg.js"></script>
<script src="../js/id/svg/areas.js"></script>
<script src="../js/id/svg/labels.js"></script>
<script src="../js/id/svg/lines.js"></script>
<script src="../js/id/svg/sequences.js"></script>
<script src="../js/id/svg/midpoints.js"></script>
<script src="../js/id/svg/points.js"></script>
<script src="../js/id/svg/surface.js"></script>
@@ -72,7 +72,6 @@
<script src='../js/id/ui/attribution.js'></script>
<script src='../js/id/ui/radial_menu.js'></script>
<script src='../js/id/ui/inspector.js'></script>
<script src='../js/id/ui/image_view.js'></script>
<script src='../js/id/ui/modal.js'></script>
<script src='../js/id/ui/cmd.js'></script>
<script src='../js/id/ui/confirm.js'></script>
@@ -166,7 +165,6 @@
<script src='../js/id/modes/draw_area.js'></script>
<script src='../js/id/modes/draw_line.js'></script>
<script src='../js/id/modes/move.js'></script>
<script src='../js/id/modes/select_image.js'></script>
<script src='../js/id/modes/rotate_way.js'></script>
<script src='../js/id/modes/select.js'></script>
+1
View File
@@ -5,6 +5,7 @@ describe('iD.behavior.Hash', function () {
beforeEach(function () {
context = iD();
context.container(d3.select(document.createElement('div')));
// Neuter connection
context.connection().loadTiles = function () {};
+1
View File
@@ -3,6 +3,7 @@ describe("iD.behavior.Lasso", function () {
beforeEach(function () {
context = iD();
context.container(d3.select(document.createElement('div')));
// Neuter connection
context.connection().loadTiles = function () {};
+4 -2
View File
@@ -1,8 +1,10 @@
describe('iD.Map', function() {
var map;
var context, map;
beforeEach(function() {
map = iD().map();
context = iD();
context.container(d3.select(document.createElement('div')));
map = context.map();
d3.select(document.createElement('div'))
.call(map);
});