Merge branch 'master' into discard-tags

Inline discardTags

Conflicts:
	index.html
This commit is contained in:
Tom MacWright
2013-02-07 16:51:38 -05:00
42 changed files with 1624 additions and 481 deletions
+1 -5
View File
@@ -30,11 +30,7 @@ iD.actions.Connect = function(nodeIds) {
});
graph.parentRelations(node).forEach(function (parent) {
var memberA = parent.memberById(survivor.id),
memberB = parent.memberById(node.id);
if (!memberA) {
graph = graph.replace(parent.addMember({id: survivor.id, role: memberB.role, type: 'node'}));
}
graph = graph.replace(parent.replaceMember(node, survivor));
});
survivor = survivor.mergeTags(node.tags);
-5
View File
@@ -1,5 +0,0 @@
iD.actions.DiscardTags = function(entity) {
return entity.update({
tags: _.omit(entity.tags, iD.data.discarded)
});
};
+23 -12
View File
@@ -6,17 +6,26 @@
// https://github.com/systemed/potlatch2/blob/master/net/systemeD/halcyon/connection/actions/MergeWaysAction.as
// https://github.com/openstreetmap/josm/blob/mirror/src/org/openstreetmap/josm/actions/CombineWayAction.java
//
iD.actions.Join = function(idA, idB) {
iD.actions.Join = function(ids) {
var idA = ids[0],
idB = ids[1];
function groupEntitiesByGeometry(graph) {
var entities = ids.map(function(id) { return graph.entity(id); });
return _.extend({line: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
}
var action = function(graph) {
var a = graph.entity(idA),
b = graph.entity(idB),
nodes, tags;
nodes;
if (a.first() === b.first()) {
// a <-- b ==> c
// Expected result:
// a <-- b <-- c
nodes = b.nodes.slice().reverse().concat(a.nodes.slice(1));
b = iD.actions.Reverse(idB)(graph).entity(idB);
nodes = b.nodes.slice().concat(a.nodes.slice(1));
} else if (a.first() === b.last()) {
// a <-- b <== c
@@ -34,17 +43,13 @@ iD.actions.Join = function(idA, idB) {
// a --> b <== c
// Expected result:
// a --> b --> c
nodes = a.nodes.concat(b.nodes.slice().reverse().slice(1));
b = iD.actions.Reverse(idB)(graph).entity(idB);
nodes = a.nodes.concat(b.nodes.slice().slice(1));
}
graph.parentRelations(b)
.forEach(function (parent) {
var memberA = parent.memberById(idA),
memberB = parent.memberById(idB);
if (!memberA) {
graph = graph.replace(parent.addMember({id: idA, role: memberB.role, type: 'way'}));
}
});
graph.parentRelations(b).forEach(function (parent) {
graph = graph.replace(parent.replaceMember(b, a));
});
graph = graph.replace(a.mergeTags(b.tags).update({nodes: nodes}));
graph = iD.actions.DeleteWay(idB)(graph);
@@ -53,8 +58,14 @@ iD.actions.Join = function(idA, idB) {
};
action.enabled = function(graph) {
var geometries = groupEntitiesByGeometry(graph);
if (ids.length !== 2 || ids.length !== geometries['line'].length)
return false;
var a = graph.entity(idA),
b = graph.entity(idB);
return a.first() === b.first() ||
a.first() === b.last() ||
a.last() === b.first() ||
+35
View File
@@ -0,0 +1,35 @@
iD.actions.Merge = function(ids) {
function groupEntitiesByGeometry(graph) {
var entities = ids.map(function(id) { return graph.entity(id); });
return _.extend({point: [], area: []}, _.groupBy(entities, function(entity) { return entity.geometry(graph); }));
}
var action = function(graph) {
var geometries = groupEntitiesByGeometry(graph),
area = geometries.area[0],
points = geometries.point;
points.forEach(function (point) {
area = area.mergeTags(point.tags);
graph.parentRelations(point).forEach(function (parent) {
graph = graph.replace(parent.replaceMember(point, area));
});
graph = graph.remove(point);
});
graph = graph.replace(area);
return graph;
};
action.enabled = function(graph) {
var geometries = groupEntitiesByGeometry(graph);
return geometries.area.length === 1 &&
geometries.point.length > 0 &&
(geometries.area.length + geometries.point.length) === ids.length;
};
return action;
};
+12 -35
View File
@@ -1,9 +1,14 @@
/*
* Based on https://github.com/openstreetmap/potlatch2/blob/master/net/systemeD/potlatch2/tools/Quadrilateralise.as
*/
iD.actions.Orthogonalize = function(wayId, projection) {
var action = function(graph) {
var way = graph.entity(wayId),
nodes = graph.childNodes(way),
points = nodes.map(function(n) { return projection(n.loc); }),
quad_nodes = [], i, j;
quad_nodes = [],
best, i, j;
var score = squareness();
for (i = 0; i < 1000; i++) {
@@ -12,46 +17,18 @@ iD.actions.Orthogonalize = function(wayId, projection) {
points[j] = addPoints(points[j],motions[j]);
}
var newScore = squareness();
if (newScore > score) {
return graph;
if (newScore < score) {
best = _.clone(points);
score = newScore;
}
score = newScore;
if (score < 1.0e-8) {
break;
}
}
points = best;
for (i = 0; i < points.length - 1; i++) {
quad_nodes.push(iD.Node({ loc: projection.invert(points[i]) }));
}
for (i = 0; i < nodes.length; i++) {
if (graph.parentWays(nodes[i]).length > 1) {
var closest, closest_dist = Infinity, dist;
for (j = 0; j < quad_nodes.length; j++) {
dist = iD.geo.dist(quad_nodes[j].loc, nodes[i].loc);
if (dist < closest_dist) {
closest_dist = dist;
closest = j;
}
}
quad_nodes.splice(closest, 1, nodes[i]);
}
}
for (i = 0; i < quad_nodes.length; i++) {
graph = graph.replace(quad_nodes[i]);
}
var ids = _.pluck(quad_nodes, 'id'),
difference = _.difference(_.uniq(way.nodes), ids);
ids.push(ids[0]);
graph = graph.replace(way.update({nodes: ids}));
for (i = 0; i < difference.length; i++) {
graph = iD.actions.DeleteNode(difference[i])(graph);
graph = graph.replace(graph.entity(nodes[i].id).move(projection.invert(points[i])));
}
return graph;
@@ -62,7 +39,7 @@ iD.actions.Orthogonalize = function(wayId, projection) {
p = subtractPoints(a, b),
q = subtractPoints(c, b);
var scale = p.length + q.length;
var scale = iD.geo.dist(p, [0, 0]) + iD.geo.dist(q, [0, 0]);
p = normalizePoint(p, 1.0);
q = normalizePoint(q, 1.0);
+50 -19
View File
@@ -15,37 +15,55 @@ iD.actions.Split = function(nodeId, newWayId) {
parents = graph.parentWays(node);
return parents.filter(function (parent) {
return parent.first() !== nodeId &&
parent.last() !== nodeId;
return parent.isClosed() ||
(parent.first() !== nodeId &&
parent.last() !== nodeId);
});
}
var action = function(graph) {
if (!action.enabled(graph))
return graph;
var wayA = candidateWays(graph)[0],
wayB = iD.Way({id: newWayId, tags: wayA.tags}),
nodesA,
nodesB,
isArea = wayA.isArea();
var way = candidateWays(graph)[0],
idx = _.indexOf(way.nodes, nodeId);
if (wayA.isClosed()) {
var nodes = wayA.nodes.slice(0, -1),
idxA = _.indexOf(nodes, nodeId),
idxB = idxA + Math.floor(nodes.length / 2);
// Create a 'b' way that contains all of the tags in the second
// half of this way
var newWay = iD.Way({id: newWayId, tags: way.tags, nodes: way.nodes.slice(idx)});
graph = graph.replace(newWay);
if (idxB >= nodes.length) {
idxB %= nodes.length;
nodesA = nodes.slice(idxA).concat(nodes.slice(0, idxB + 1));
nodesB = nodes.slice(idxB, idxA + 1);
} else {
nodesA = nodes.slice(idxA, idxB + 1);
nodesB = nodes.slice(idxB).concat(nodes.slice(0, idxA + 1));
}
} else {
var idx = _.indexOf(wayA.nodes, nodeId);
nodesA = wayA.nodes.slice(0, idx + 1);
nodesB = wayA.nodes.slice(idx);
}
// Reduce the original way to only contain the first set of nodes
graph = graph.replace(way.update({nodes: way.nodes.slice(0, idx + 1)}));
wayA = wayA.update({nodes: nodesA});
wayB = wayB.update({nodes: nodesB});
graph.parentRelations(way).forEach(function(relation) {
graph = graph.replace(wayA);
graph = graph.replace(wayB);
graph.parentRelations(wayA).forEach(function(relation) {
if (relation.isRestriction()) {
var via = relation.memberByRole('via');
if (via && newWay.contains(via.id)) {
relation = relation.updateMember({id: newWay.id}, relation.memberById(way.id).index);
if (via && wayB.contains(via.id)) {
relation = relation.updateMember({id: wayB.id}, relation.memberById(wayA.id).index);
graph = graph.replace(relation);
}
} else {
var role = relation.memberById(way.id).role,
last = newWay.last(),
i = relation.memberById(way.id).index,
var role = relation.memberById(wayA.id).role,
last = wayB.last(),
i = relation.memberById(wayA.id).index,
j;
for (j = 0; j < relation.members.length; j++) {
@@ -55,11 +73,24 @@ iD.actions.Split = function(nodeId, newWayId) {
}
}
relation = relation.addMember({id: newWay.id, type: 'way', role: role}, i <= j ? i + 1 : i);
relation = relation.addMember({id: wayB.id, type: 'wayA', role: role}, i <= j ? i + 1 : i);
graph = graph.replace(relation);
}
});
if (isArea) {
var multipolygon = iD.Relation({
tags: _.extend({}, wayA.tags, {type: 'multipolygon'}),
members: [
{id: wayA.id, role: 'outer', type: 'way'},
{id: wayB.id, role: 'outer', type: 'way'}
]});
graph = graph.replace(multipolygon);
graph = graph.replace(wayA.update({tags: {}}));
graph = graph.replace(wayB.update({tags: {}}));
}
return graph;
};
+16 -7
View File
@@ -84,7 +84,11 @@ iD.behavior.DragNode = function(context) {
if (d.type === 'node' && d.id !== entity.id) {
loc = d.loc;
} else if (d.type === 'way') {
loc = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context).loc;
var point = d3.mouse(context.surface().node()),
index = iD.geo.chooseIndex(d, point, context);
if (iD.geo.dist(point, context.projection(index.loc)) < 10) {
loc = index.loc;
}
}
context.replace(iD.actions.MoveNode(entity.id, loc));
@@ -100,13 +104,18 @@ iD.behavior.DragNode = function(context) {
var d = datum();
if (d.type === 'way') {
var choice = iD.geo.chooseIndex(d, d3.mouse(context.surface().node()), context);
context.replace(
iD.actions.MoveNode(entity.id, choice.loc),
iD.actions.AddVertex(d.id, entity.id, choice.index),
connectAnnotation(d));
var point = d3.mouse(context.surface().node()),
choice = iD.geo.chooseIndex(d, point, context);
if (iD.geo.dist(point, context.projection(choice.loc)) < 10) {
context.replace(
iD.actions.MoveNode(entity.id, choice.loc),
iD.actions.AddVertex(d.id, entity.id, choice.index),
connectAnnotation(d));
return;
}
}
} else if (d.type === 'node' && d.id !== entity.id) {
if (d.type === 'node' && d.id !== entity.id) {
context.replace(
iD.actions.Connect([entity.id, d.id]),
connectAnnotation(d));
+8 -2
View File
@@ -27,13 +27,19 @@ iD.behavior.Draw = function(context) {
target.on('mousemove.draw', null);
d3.select(window).on('click.draw', function() {
d3.select(window).on('mouseup.draw', function() {
target.on('mousemove.draw', mousemove);
if (iD.geo.dist(pos, point()) < closeTolerance ||
(iD.geo.dist(pos, point()) < tolerance &&
(+new Date() - time) < 500)) {
click();
}
if (target.node() === d3.event.target) {
d3.select(window).on('click.draw', function() {
d3.select(window).on('click.draw', null);
d3.event.stopPropagation();
});
}
});
}
@@ -110,7 +116,7 @@ iD.behavior.Draw = function(context) {
.on('mousedown.draw', null)
.on('mousemove.draw', null);
d3.select(window).on('click.draw', null);
d3.select(window).on('mouseup.draw', null);
d3.select(document)
.call(keybinding.off)
+64
View File
@@ -0,0 +1,64 @@
iD.behavior.Lasso = function(context) {
var behavior = function(selection) {
var timeout = null,
// the position of the first mousedown
pos = null,
lasso;
function mousedown() {
if (d3.event.shiftKey === true) {
lasso = iD.ui.lasso().a(d3.mouse(context.surface().node()));
context.surface().call(lasso);
selection
.on('mousemove.lasso', mousemove)
.on('mouseup.lasso', mouseup);
d3.event.stopPropagation();
d3.event.preventDefault();
}
}
function mousemove() {
lasso.b(d3.mouse(context.surface().node()));
}
function normalize(a, b) {
return [
[Math.min(a[0], b[0]), Math.min(a[1], b[1])],
[Math.max(a[0], b[0]), Math.max(a[1], b[1])]];
}
function mouseup() {
var extent = iD.geo.Extent(
normalize(context.projection.invert(lasso.a()),
context.projection.invert(lasso.b())));
lasso.close();
var selected = context.graph().intersects(extent);
selection
.on('mousemove.lasso', null)
.on('mouseup.lasso', null);
if (selected.length) {
context.enter(iD.modes.Select(context, _.pluck(selected, 'id')));
}
}
selection
.on('mousedown.lasso', mousedown);
};
behavior.off = function(selection) {
selection.on('mousedown.lasso', null);
};
return behavior;
};
+3
View File
@@ -189,6 +189,9 @@ iD.Graph.prototype = {
},
replace: function(entity) {
if (this.entities[entity.id] === entity)
return this;
return this.update(function () {
this._updateCalculated(this.entities[entity.id], entity);
this.entities[entity.id] = entity;
+9 -2
View File
@@ -132,9 +132,16 @@ iD.History = function(context) {
changes: function() {
var difference = history.difference();
function discardTags(entity) {
return entity.update({
tags: _.omit(entity.tags, iD.data.discarded)
});
}
return {
modified: difference.modified().map(iD.actions.DiscardTags),
created: difference.created().map(iD.actions.DiscardTags),
modified: difference.modified().map(discardTags),
created: difference.created().map(discardTags),
deleted: difference.deleted()
};
},
+32
View File
@@ -49,6 +49,16 @@ _.extend(iD.Relation.prototype, {
}
},
// Return the first member with the given id and role. A copy of the member object
// is returned, extended with an 'index' property whose value is the member index.
memberByIdAndRole: function(id, role) {
for (var i = 0; i < this.members.length; i++) {
if (this.members[i].id === id && this.members[i].role === role) {
return _.extend({}, this.members[i], {index: i});
}
}
},
addMember: function(member, index) {
var members = this.members.slice();
members.splice(index === undefined ? members.length : index, 0, member);
@@ -66,6 +76,28 @@ _.extend(iD.Relation.prototype, {
return this.update({members: members});
},
// Wherever a member appears with id `needle.id`, replace it with a member
// with id `replacement.id`, type `replacement.type`, and the original role,
// unless a member already exists with that id and role. Return an updated
// relation.
replaceMember: function(needle, replacement) {
if (!this.memberById(needle.id))
return this;
var members = [];
for (var i = 0; i < this.members.length; i++) {
var member = this.members[i];
if (member.id !== needle.id) {
members.push(member);
} else if (!this.memberByIdAndRole(replacement.id, member.role)) {
members.push({id: replacement.id, type: replacement.type, role: member.role});
}
}
return this.update({members: members});
},
asJXON: function(changeset_id) {
var r = {
relation: {
+3 -1
View File
@@ -93,7 +93,9 @@ window.iD = function () {
};
context.background()
.source(iD.BackgroundSource.Bing);
.source(_.find(iD.layers, function(l) {
return l.data.name === 'Bing aerial imagery';
}));
return d3.rebind(context, dispatch, 'on');
};
+1
View File
@@ -10,6 +10,7 @@ iD.modes.Browse = function(context) {
var behaviors = [
iD.behavior.Hover(),
iD.behavior.Select(context),
iD.behavior.Lasso(context),
iD.behavior.DragNode(context)];
mode.enter = function() {
+4
View File
@@ -42,6 +42,10 @@ iD.modes.Select = function(context, selection, initial) {
.filter(function(o) { return o.available(); });
operations.unshift(iD.operations.Delete(selection, context));
keybinding.on('⎋', function() {
context.enter(iD.modes.Browse(context));
});
operations.forEach(function(operation) {
keybinding.on(operation.key, function() {
if (operation.enabled()) {
+14 -7
View File
@@ -1,21 +1,28 @@
iD.operations.Merge = function(selection, context) {
var action = iD.actions.Join(selection[0], selection[1]);
var join = iD.actions.Join(selection),
merge = iD.actions.Merge(selection);
var operation = function() {
var annotation = t('operations.merge.annotation', {n: selection.length}),
difference = context.perform(action, annotation);
action;
if (join.enabled(context.graph())) {
action = join;
} else {
action = merge;
}
var difference = context.perform(action, annotation);
context.enter(iD.modes.Select(context, difference.extantIDs()));
};
operation.available = function() {
return selection.length === 2 &&
_.all(selection, function (id) {
return context.geometry(id) === 'line';
});
return selection.length >= 2;
};
operation.enabled = function() {
return action.enabled(context.graph());
return join.enabled(context.graph()) ||
merge.enabled(context.graph());
};
operation.id = "merge";
+11 -26
View File
@@ -1,8 +1,7 @@
iD.BackgroundSource = {};
// derive the url of a 'quadkey' style tile from a coordinate object
iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
scaleExtent = scaleExtent || [0, 18];
iD.BackgroundSource.template = function(data) {
var generator = function(coord) {
var u = '';
for (var zoom = coord[2]; zoom > 0; zoom--) {
@@ -12,19 +11,17 @@ iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
if ((coord[1] & mask) !== 0) byte += 2;
u += byte.toString();
}
// distribute requests against multiple domains
var t = subdomains ?
subdomains[coord[2] % subdomains.length] : '';
return template
.replace('{t}', t)
return data.template
.replace('{t}', data.subdomains ?
data.subdomains[coord[2] % data.subdomains.length] : '')
.replace('{u}', u)
.replace('{x}', coord[0])
.replace('{y}', coord[1])
.replace('{z}', coord[2]);
};
generator.scaleExtent = scaleExtent;
generator.template = template;
generator.data = data;
return generator;
};
@@ -32,21 +29,9 @@ iD.BackgroundSource.template = function(template, subdomains, scaleExtent) {
iD.BackgroundSource.Custom = function() {
var template = window.prompt('Enter a tile template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.');
if (!template) return null;
return iD.BackgroundSource.template(template, null, [0, 20]);
return iD.BackgroundSource.template({
template: template,
name: 'Custom (customized)'
});
};
iD.BackgroundSource.Bing = iD.BackgroundSource.template(
'http://ecn.t{t}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z',
[0, 1, 2, 3], [0, 20]);
iD.BackgroundSource.Tiger2012 = iD.BackgroundSource.template(
'http://{t}.tile.openstreetmap.us/tiger2012_roads_expanded/{z}/{x}/{y}.png',
['a', 'b', 'c'], [0, 17]);
iD.BackgroundSource.OSM = iD.BackgroundSource.template(
'http://{t}.tile.openstreetmap.org/{z}/{x}/{y}.png',
['a', 'b', 'c'], [0, 18]);
iD.BackgroundSource.MapBox = iD.BackgroundSource.template(
'http://{t}.tiles.mapbox.com/v3/openstreetmap.map-4wvf9l0l/{z}/{x}/{y}.jpg70',
['a', 'b', 'c'], [0, 16]);
iD.BackgroundSource.Custom.data = { 'name': 'Custom' };
+15
View File
@@ -0,0 +1,15 @@
iD.layers = iD.data.imagery.map(iD.BackgroundSource.template);
iD.layers.push((function() {
function custom() {
var template = window.prompt('Enter a tile template. Valid tokens are {z}, {x}, {y} for Z/X/Y scheme and {u} for quadtile scheme.');
if (!template) return null;
if (template.match(/google/g)) return null;
return iD.BackgroundSource.template({
template: template,
name: 'Custom (customized)'
});
}
custom.data = { name: 'Custom' };
return custom;
})());
+1 -1
View File
@@ -106,7 +106,7 @@ iD.Map = function(context) {
}
if (Math.log(d3.event.scale / Math.LN2 - 8) < minzoom + 1) {
iD.ui.flash()
iD.ui.flash(context.container())
.select('.content')
.text('Cannot zoom out further in current mode.');
return map.zoom(16);
+3 -2
View File
@@ -161,8 +161,9 @@ iD.ui = function(context) {
var imagery = linkList.append('li').attr('id', 'attribution');
imagery.append('span').text('imagery');
imagery.append('a').attr('target', '_blank')
.attr('href', 'http://opengeodata.org/microsoft-imagery-details').text(' provided by bing');
imagery
.append('span')
.attr('class', 'provided-by');
linkList.append('li').attr('class', 'source-switch').append('a').attr('href', '#')
.text('dev')
+1 -1
View File
@@ -12,7 +12,7 @@ iD.ui.geocoder = function() {
if (err) return hide();
hide();
if (!resp.length) {
return iD.ui.flash()
return iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text('No location found for "' + searchVal + '"');
+1 -1
View File
@@ -163,7 +163,7 @@ iD.ui.inspector = function() {
})
.call(iD.keyReference(context));
} else {
iD.ui.flash()
iD.ui.flash(context.container())
.select('.content')
.append('h3')
.text(t('inspector.no_documentation_key'));
+63
View File
@@ -0,0 +1,63 @@
iD.ui.lasso = function() {
var center, box,
group,
a = [0, 0],
b = [0, 0];
function lasso(selection) {
group = selection.append('g')
.attr('class', 'lasso')
.attr('opacity', 0);
box = group.append('rect')
.attr('class', 'lasso-box');
group.transition()
.style('opacity', 1);
}
// top-left
function topLeft(d) {
return 'translate(' +
[Math.min(d[0][0], d[1][0]), Math.min(d[0][1], d[1][1])] + ')';
}
function width(d) { return Math.abs(d[0][0] - d[1][0]); }
function height(d) { return Math.abs(d[0][1] - d[1][1]); }
function draw() {
if (box) {
box.data([[a, b]])
.attr('transform', topLeft)
.attr('width', width)
.attr('height', height);
}
}
lasso.a = function(_) {
if (!arguments.length) return a;
a = _;
draw();
return lasso;
};
lasso.b = function(_) {
if (!arguments.length) return b;
b = _;
draw();
return lasso;
};
lasso.close = function(selection) {
if (group) {
group.transition()
.attr('opacity', 0)
.remove();
}
};
return lasso;
};
+81 -62
View File
@@ -1,31 +1,17 @@
iD.ui.layerswitcher = function(context) {
var event = d3.dispatch('cancel', 'save'),
sources = [{
name: 'Bing',
source: iD.BackgroundSource.Bing,
description: 'Satellite imagery.',
link: 'http://opengeodata.org/microsoft-imagery-details'
}, {
name: 'TIGER 2012',
source: iD.BackgroundSource.Tiger2012,
description: 'Public domain road data from the US Government.'
}, {
name: 'OSM',
source: iD.BackgroundSource.OSM,
description: 'The default OpenStreetMap layer.',
link: 'http://www.openstreetmap.org/'
}, {
name: 'MapBox',
source: iD.BackgroundSource.MapBox,
description: 'Satellite and aerial imagery.',
link: 'http://mapbox.com'
}, {
name: 'Custom',
source: iD.BackgroundSource.Custom,
description: 'A custom layer (requires configuration).'
}],
opacities = [1, 0.5, 0];
var layers = iD.layers;
function getSources() {
var ext = context.map().extent();
return layers.filter(function(layer) {
return !layer.data.extent ||
iD.geo.Extent(layer.data.extent).intersects(ext);
});
}
function layerswitcher(selection) {
var content = selection
@@ -52,7 +38,7 @@ iD.ui.layerswitcher = function(context) {
selection.on('click.layerswitcher-inside', function() {
return d3.event.stopPropagation();
});
d3.select('body').on('click.layerswitcher-outside', hide);
context.container().on('click.layerswitcher-outside', hide);
}
var opa = content
@@ -64,6 +50,17 @@ iD.ui.layerswitcher = function(context) {
var opacityList = opa.append('ul')
.attr('class', 'opacity-options');
function setOpacity(d) {
context.map().tilesurface
.transition()
.style('opacity', d)
.attr('data-opacity', d);
opacityList.selectAll('li')
.classed('selected', false);
d3.select(this)
.classed('selected', true);
}
opacityList.selectAll('div.opacity')
.data(opacities)
.enter()
@@ -71,69 +68,91 @@ iD.ui.layerswitcher = function(context) {
.attr('data-original-title', function(d) {
return t('layerswitcher.percent_brightness', { opacity: (d * 100) });
})
.on('click.set-opacity', function(d) {
context.map().tilesurface
.transition()
.style('opacity', d)
.attr('data-opacity', d);
opacityList.selectAll('li')
.classed('selected', false);
d3.select(this)
.classed('selected', true);
})
.on('click.set-opacity', setOpacity)
.html("<div class='select-box'></div>")
.call(bootstrap.tooltip().placement('top'))
.append('div')
.attr('class', 'opacity')
.style('opacity', function(d) {
return d;
});
.style('opacity', String);
// Make sure there is an active selection by default
d3.select('.opacity-options li:nth-child(2)').classed('selected', true);
opa.select('.opacity-options li:nth-child(2)').classed('selected', true);
function selectLayer(d) {
content.selectAll('a.layer')
.classed('selected', function(d) {
return d.source === context.background().source();
return d === context.background().source();
});
d3.select('#attribution a')
.attr('href', d.link)
.text('provided by ' + d.name);
var provided_by = context.container().select('#attribution .provided-by')
.html('');
if (d.data.terms_url) {
provided_by.append('a')
.attr('href', (d.data.terms_url || ''))
.classed('disabled', !d.data.terms_url)
.text(' provided by ' + (d.data.sourcetag || d.data.name));
} else {
provided_by
.text(' provided by ' + (d.data.sourcetag || d.data.name));
}
}
content
function clickSetSource(d) {
d3.event.preventDefault();
if (d.data.name === 'Custom') {
var configured = d();
if (!configured) return;
d = configured;
}
context.background().source(d);
if (d.data.name === 'Custom (customized)') {
context.history()
.imagery_used('Custom (' + d.data.template + ')');
} else {
context.history()
.imagery_used(d.data.sourcetag || d.data.name);
}
context.redraw();
selectLayer(d);
}
var layerList = content
.append('ul')
.attr('class', 'toggle-list fillL')
.selectAll('a.layer')
.data(sources)
.enter()
.attr('class', 'toggle-list fillL');
function update() {
var layerLinks = layerList.selectAll('a.layer')
.data(getSources(), function(d) {
return d.data.name;
});
layerLinks.exit().remove();
layerLinks.enter()
.append('li')
.append('a')
.attr('data-original-title', function(d) {
return d.description;
return d.data.description || '';
})
.attr('href', '#')
.attr('class', 'layer')
.text(function(d) {
return d.name;
return d.data.name;
})
.call(bootstrap.tooltip().placement('right'))
.on('click.set-source', function(d) {
d3.event.preventDefault();
if (d.name === 'Custom') {
var configured = d.source();
if (!configured) return;
d.source = configured;
d.name = 'Custom (' + d.source.template + ')';
.each(function(d) {
// only set tooltips for layers with tooltips
if (d.data.description) {
d3.select(this).call(bootstrap.tooltip().placement('right'));
}
context.background().source(d.source);
context.history().imagery_used(d.name);
context.redraw();
selectLayer(d);
})
.on('click.set-source', clickSetSource)
.insert('span')
.attr('class','icon toggle');
selectLayer(context.background().source());
}
context.map().on('move.layerswitcher-update', _.debounce(update, 1000));
var adjustments = content
.append('div')
+1 -1
View File
@@ -30,7 +30,7 @@ iD.ui.save = function(context) {
function click() {
function commit(e) {
d3.select('.shaded').remove();
context.container().select('.shaded').remove();
var l = iD.ui.loading(context.container(), t('uploading_changes'), true);
connection.putChangeset(history.changes(),