mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Merge branch 'master' into dynamic-layers
Conflicts: js/id/renderer/background_source.js js/id/ui/layerswitcher.js
This commit is contained in:
@@ -90,7 +90,6 @@
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/add_way.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
<script src='js/id/behavior/drag_midpoint.js'></script>
|
||||
<script src='js/id/behavior/drag_node.js'></script>
|
||||
<script src='js/id/behavior/draw.js'></script>
|
||||
<script src='js/id/behavior/draw_way.js'></script>
|
||||
|
||||
+33
-59
@@ -181,95 +181,72 @@ path.shadow.selected {
|
||||
}
|
||||
|
||||
path.area.stroke,
|
||||
path.multipolygon {
|
||||
path.line.member-type-multipolygon.stroke {
|
||||
stroke-width:2;
|
||||
stroke:#fff;
|
||||
}
|
||||
path.area.fill,
|
||||
path.multipolygon {
|
||||
fill:#fff;
|
||||
fill-opacity:0.3;
|
||||
}
|
||||
|
||||
path.multipolygon {
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
path.area.fill.member-type-multipolygon {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
path.area.stroke.selected {
|
||||
path.area.stroke.selected,
|
||||
path.line.member-type-multipolygon.stroke.selected {
|
||||
stroke-width:4 !important;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-natural,
|
||||
path.multipolygon.tag-natural {
|
||||
path.area.stroke {
|
||||
stroke:#fff;
|
||||
}
|
||||
path.area.fill {
|
||||
fill:#fff;
|
||||
fill-opacity:0.3;
|
||||
fill-rule: evenodd;
|
||||
}
|
||||
|
||||
path.stroke.tag-natural {
|
||||
stroke: #b6e199;
|
||||
stroke-width:1;
|
||||
}
|
||||
path.area.fill.tag-natural,
|
||||
path.multipolygon.tag-natural {
|
||||
path.fill.tag-natural {
|
||||
fill: #b6e199;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-natural-water,
|
||||
path.multipolygon.tag-natural-water {
|
||||
path.stroke.tag-natural-water {
|
||||
stroke: #77d3de;
|
||||
}
|
||||
path.area.fill.tag-natural-water,
|
||||
path.multipolygon.tag-natural-water {
|
||||
path.fill.tag-natural-water {
|
||||
fill: #77d3de;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-building,
|
||||
path.multipolygon.tag-building {
|
||||
path.stroke.tag-building {
|
||||
stroke: #e06e5f;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-building,
|
||||
path.multipolygon.tag-building {
|
||||
path.fill.tag-building {
|
||||
fill: #e06e5f;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-landuse,
|
||||
path.area.stroke.tag-natural-wood,
|
||||
path.area.stroke.tag-natural-tree,
|
||||
path.area.stroke.tag-natural-grassland,
|
||||
path.area.stroke.tag-leisure-park,
|
||||
path.multipolygon.tag-landuse,
|
||||
path.multipolygon.tag-natural-wood,
|
||||
path.multipolygon.tag-natural-tree,
|
||||
path.multipolygon.tag-natural-grassland,
|
||||
path.multipolygon.tag-leisure-park {
|
||||
path.stroke.tag-landuse,
|
||||
path.stroke.tag-natural-wood,
|
||||
path.stroke.tag-natural-tree,
|
||||
path.stroke.tag-natural-grassland,
|
||||
path.stroke.tag-leisure-park {
|
||||
stroke: #8cd05f;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-landuse,
|
||||
path.area.fill.tag-natural-wood,
|
||||
path.area.fill.tag-natural-tree,
|
||||
path.area.fill.tag-natural-grassland,
|
||||
path.area.fill.tag-leisure-park,
|
||||
path.multipolygon.tag-landuse,
|
||||
path.multipolygon.tag-natural-wood,
|
||||
path.multipolygon.tag-natural-tree,
|
||||
path.multipolygon.tag-natural-grassland,
|
||||
path.multipolygon.tag-leisure-park {
|
||||
path.fill.tag-landuse,
|
||||
path.fill.tag-natural-wood,
|
||||
path.fill.tag-natural-tree,
|
||||
path.fill.tag-natural-grassland,
|
||||
path.fill.tag-leisure-park {
|
||||
fill: #8cd05f;
|
||||
fill-opacity: 0.2;
|
||||
}
|
||||
|
||||
path.area.stroke.tag-amenity-parking,
|
||||
path.multipolygon.tag-amenity-parking {
|
||||
path.stroke.tag-amenity-parking {
|
||||
stroke: #aaa;
|
||||
stroke-width: 1;
|
||||
}
|
||||
path.area.fill.tag-amenity-parking,
|
||||
path.multipolygon.tag-amenity-parking {
|
||||
path.fill.tag-amenity-parking {
|
||||
fill: #aaa;
|
||||
}
|
||||
|
||||
path.multipolygon.tag-boundary {
|
||||
path.fill.tag-boundary {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
@@ -526,7 +503,7 @@ path.casing.tag-railway-subway {
|
||||
|
||||
/* waterways */
|
||||
|
||||
path.area.fill.tag-waterway {
|
||||
path.fill.tag-waterway {
|
||||
fill: #77d3de;
|
||||
}
|
||||
|
||||
@@ -686,9 +663,7 @@ text.point {
|
||||
}
|
||||
|
||||
.mode-select .area,
|
||||
.mode-browse .area,
|
||||
.mode-select .multipolygon,
|
||||
.mode-browse .multipolygon {
|
||||
.mode-browse .area {
|
||||
cursor: url(../img/cursor-select-area.png), pointer;
|
||||
}
|
||||
|
||||
@@ -701,7 +676,6 @@ text.point {
|
||||
.vertex:active,
|
||||
.line:active,
|
||||
.area:active,
|
||||
.multipolygon:active,
|
||||
.midpoint:active,
|
||||
.mode-select .selected {
|
||||
cursor: url(../img/cursor-select-acting.png), pointer;
|
||||
|
||||
+11
-2
@@ -49,7 +49,6 @@
|
||||
<script src="js/id/svg/lines.js"></script>
|
||||
<script src="js/id/svg/member_classes.js"></script>
|
||||
<script src="js/id/svg/midpoints.js"></script>
|
||||
<script src="js/id/svg/multipolygons.js"></script>
|
||||
<script src="js/id/svg/points.js"></script>
|
||||
<script src="js/id/svg/surface.js"></script>
|
||||
<script src="js/id/svg/tag_classes.js"></script>
|
||||
@@ -73,6 +72,7 @@
|
||||
<script src='js/id/ui/flash.js'></script>
|
||||
<script src='js/id/ui/save.js'></script>
|
||||
<script src='js/id/ui/splash.js'></script>
|
||||
<script src='js/id/ui/restore.js'></script>
|
||||
<script src='js/id/ui/tag_reference.js'></script>
|
||||
<script src='js/id/ui/key_reference.js'></script>
|
||||
|
||||
@@ -99,7 +99,6 @@
|
||||
<script src='js/id/behavior.js'></script>
|
||||
<script src='js/id/behavior/add_way.js'></script>
|
||||
<script src='js/id/behavior/drag.js'></script>
|
||||
<script src='js/id/behavior/drag_midpoint.js'></script>
|
||||
<script src='js/id/behavior/drag_node.js'></script>
|
||||
<script src='js/id/behavior/draw.js'></script>
|
||||
<script src='js/id/behavior/draw_way.js'></script>
|
||||
@@ -156,6 +155,8 @@
|
||||
.call(id.ui())
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- google analytics -->
|
||||
<script type='text/javascript'>
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-38039653-2']);
|
||||
@@ -166,5 +167,13 @@ _gaq.push(['_trackPageview']);
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- crazyegg -->
|
||||
<script type="text/javascript">
|
||||
setTimeout(function(){var a=document.createElement("script");
|
||||
var b=document.getElementsByTagName("script")[0];
|
||||
a.src=document.location.protocol+"//dnn506yrbagrg.cloudfront.net/pages/scripts/0013/6714.js?"+Math.floor(new Date().getTime()/3600000);
|
||||
a.async=true;a.type="text/javascript";b.parentNode.insertBefore(a,b)}, 1);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* Delegation is supported via the `delegate` function.
|
||||
|
||||
*/
|
||||
iD.behavior.drag = function () {
|
||||
iD.behavior.drag = function() {
|
||||
function d3_eventCancel() {
|
||||
d3.event.stopPropagation();
|
||||
d3.event.preventDefault();
|
||||
@@ -50,21 +50,21 @@ iD.behavior.drag = function () {
|
||||
moved = 0;
|
||||
|
||||
var w = d3.select(window)
|
||||
.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
|
||||
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
|
||||
.on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", dragmove)
|
||||
.on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", dragend, true);
|
||||
|
||||
if (origin) {
|
||||
offset = origin.apply(target, arguments);
|
||||
offset = [ offset[0] - origin_[0], offset[1] - origin_[1] ];
|
||||
offset = [offset[0] - origin_[0], offset[1] - origin_[1]];
|
||||
} else {
|
||||
offset = [ 0, 0 ];
|
||||
offset = [0, 0];
|
||||
}
|
||||
|
||||
if (touchId == null) d3_eventCancel();
|
||||
if (touchId === null) d3_eventCancel();
|
||||
|
||||
function point() {
|
||||
var p = target.parentNode;
|
||||
return touchId != null ? d3.touches(p).filter(function (p) {
|
||||
return touchId !== null ? d3.touches(p).filter(function(p) {
|
||||
return p.identifier === touchId;
|
||||
})[0] : d3.mouse(p);
|
||||
}
|
||||
@@ -103,8 +103,8 @@ iD.behavior.drag = function () {
|
||||
if (d3.event.target === eventTarget) w.on("click.drag", click, true);
|
||||
}
|
||||
|
||||
w.on(touchId != null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
|
||||
.on(touchId != null ? "touchend.drag-" + touchId : "mouseup.drag", null);
|
||||
w.on(touchId !== null ? "touchmove.drag-" + touchId : "mousemove.drag", null)
|
||||
.on(touchId !== null ? "touchend.drag-" + touchId : "mouseup.drag", null);
|
||||
}
|
||||
|
||||
function click() {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
iD.behavior.DragMidpoint = function(context) {
|
||||
var behavior = iD.behavior.drag()
|
||||
.delegate(".midpoint")
|
||||
.origin(function(d) {
|
||||
return context.projection(d.loc);
|
||||
})
|
||||
.on('start', function(d) {
|
||||
var node = iD.Node();
|
||||
|
||||
context.perform(iD.actions.AddMidpoint(d, node));
|
||||
|
||||
var vertex = context.surface().selectAll('.vertex')
|
||||
.filter(function(data) { return data.id === node.id; });
|
||||
|
||||
behavior.target(vertex.node(), vertex.datum());
|
||||
})
|
||||
.on('move', function(d) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
context.replace(
|
||||
iD.actions.MoveNode(d.id, context.projection.invert(d3.event.point)));
|
||||
})
|
||||
.on('end', function() {
|
||||
context.replace(
|
||||
iD.actions.Noop(),
|
||||
t('operations.add.annotation.vertex'));
|
||||
});
|
||||
|
||||
return behavior;
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
iD.behavior.DragNode = function(context) {
|
||||
var nudgeInterval;
|
||||
var nudgeInterval,
|
||||
wasMidpoint;
|
||||
|
||||
function edge(point, size) {
|
||||
var pad = [30, 100, 30, 100];
|
||||
@@ -35,6 +36,23 @@ iD.behavior.DragNode = function(context) {
|
||||
}
|
||||
|
||||
function start(entity) {
|
||||
|
||||
wasMidpoint = entity.type === 'midpoint';
|
||||
if (wasMidpoint) {
|
||||
var midpoint = entity;
|
||||
entity = iD.Node();
|
||||
context.perform(iD.actions.AddMidpoint(midpoint, entity));
|
||||
|
||||
var vertex = context.surface()
|
||||
.selectAll('.vertex')
|
||||
.filter(function(d) { return d.id === entity.id; });
|
||||
behavior.target(vertex.node(), entity);
|
||||
|
||||
} else {
|
||||
context.perform(
|
||||
iD.actions.Noop());
|
||||
}
|
||||
|
||||
var activeIDs = _.pluck(context.graph().parentWays(entity), 'id');
|
||||
activeIDs.push(entity.id);
|
||||
|
||||
@@ -43,9 +61,6 @@ iD.behavior.DragNode = function(context) {
|
||||
.selectAll('.node, .way')
|
||||
.filter(function (d) { return activeIDs.indexOf(d.id) >= 0; })
|
||||
.classed('active', true);
|
||||
|
||||
context.perform(
|
||||
iD.actions.Noop());
|
||||
}
|
||||
|
||||
function datum() {
|
||||
@@ -66,7 +81,7 @@ iD.behavior.DragNode = function(context) {
|
||||
var loc = context.map().mouseCoordinates();
|
||||
|
||||
var d = datum();
|
||||
if (d.type === 'node') {
|
||||
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;
|
||||
@@ -96,6 +111,11 @@ iD.behavior.DragNode = function(context) {
|
||||
iD.actions.Connect([entity.id, d.id]),
|
||||
connectAnnotation(d));
|
||||
|
||||
} else if (wasMidpoint) {
|
||||
context.replace(
|
||||
iD.actions.Noop(),
|
||||
t('operations.add.annotation.vertex'));
|
||||
|
||||
} else {
|
||||
context.replace(
|
||||
iD.actions.Noop(),
|
||||
@@ -103,10 +123,12 @@ iD.behavior.DragNode = function(context) {
|
||||
}
|
||||
}
|
||||
|
||||
return iD.behavior.drag()
|
||||
.delegate("g.node")
|
||||
var behavior = iD.behavior.drag()
|
||||
.delegate("g.node, g.midpoint")
|
||||
.origin(origin)
|
||||
.on('start', start)
|
||||
.on('move', move)
|
||||
.on('end', end);
|
||||
|
||||
return behavior;
|
||||
};
|
||||
|
||||
+31
-17
@@ -1,23 +1,39 @@
|
||||
iD.behavior.Draw = function(context) {
|
||||
var event = d3.dispatch('move', 'click', 'clickWay', 'clickNode', 'undo', 'cancel', 'finish'),
|
||||
var event = d3.dispatch('move', 'click', 'clickWay',
|
||||
'clickNode', 'undo', 'cancel', 'finish'),
|
||||
keybinding = d3.keybinding('draw'),
|
||||
hover = iD.behavior.Hover();
|
||||
hover = iD.behavior.Hover(),
|
||||
closeTolerance = 4,
|
||||
tolerance = 12;
|
||||
|
||||
function datum() {
|
||||
if (d3.event.altKey) {
|
||||
return {};
|
||||
} else {
|
||||
return d3.event.target.__data__ || {};
|
||||
}
|
||||
if (d3.event.altKey) return {};
|
||||
else return d3.event.target.__data__ || {};
|
||||
}
|
||||
|
||||
function mousedown() {
|
||||
var selection = d3.select(this);
|
||||
selection.on('mousemove.draw', null);
|
||||
|
||||
d3.select(window)
|
||||
.on('mouseup.draw', function() {
|
||||
selection.on('mousemove.draw', mousemove);
|
||||
function point() {
|
||||
var p = target.node().parentNode;
|
||||
return touchId !== null ? d3.touches(p).filter(function(p) {
|
||||
return p.identifier === touchId;
|
||||
})[0] : d3.mouse(p);
|
||||
}
|
||||
|
||||
var target = d3.select(this),
|
||||
touchId = d3.event.touches ? d3.event.changedTouches[0].identifier : null,
|
||||
time = +new Date(),
|
||||
pos = point();
|
||||
|
||||
target.on('mousemove.draw', null);
|
||||
|
||||
d3.select(window).on('click.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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,8 +93,7 @@ iD.behavior.Draw = function(context) {
|
||||
|
||||
selection
|
||||
.on('mousedown.draw', mousedown)
|
||||
.on('mousemove.draw', mousemove)
|
||||
.on('click.draw', click);
|
||||
.on('mousemove.draw', mousemove);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding)
|
||||
@@ -93,10 +108,9 @@ iD.behavior.Draw = function(context) {
|
||||
|
||||
selection
|
||||
.on('mousedown.draw', null)
|
||||
.on('mousemove.draw', null)
|
||||
.on('click.draw', null);
|
||||
.on('mousemove.draw', null);
|
||||
|
||||
d3.select(window).on('mouseup.draw', null);
|
||||
d3.select(window).on('click.draw', null);
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding.off)
|
||||
|
||||
@@ -28,7 +28,13 @@ iD.behavior.DrawWay = function(context, wayId, index, mode, baseGraph) {
|
||||
function move(datum) {
|
||||
var loc = context.map().mouseCoordinates();
|
||||
|
||||
if (datum.type === 'node') {
|
||||
if (datum.id === end.id || datum.id === segment.id) {
|
||||
context.surface().selectAll('.way, .node')
|
||||
.filter(function (d) {
|
||||
return d.id === end.id || d.id === segment.id;
|
||||
})
|
||||
.classed('active', true);
|
||||
} else if (datum.type === 'node') {
|
||||
loc = datum.loc;
|
||||
} else if (datum.type === 'way') {
|
||||
loc = iD.geo.chooseIndex(datum, d3.mouse(context.surface().node()), context).loc;
|
||||
|
||||
@@ -29,7 +29,7 @@ iD.behavior.Hash = function(context) {
|
||||
var move = _.throttle(function() {
|
||||
var s1 = formatter(context.map());
|
||||
if (s0 !== s1) location.replace(s0 = s1); // don't recenter the map!
|
||||
}, 100);
|
||||
}, 500);
|
||||
|
||||
function hashchange() {
|
||||
if (location.hash === s0) return; // ignore spurious hashchange events
|
||||
|
||||
+57
-14
@@ -1,23 +1,66 @@
|
||||
iD.behavior.Select = function(context) {
|
||||
function click() {
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
if (d3.event.shiftKey) {
|
||||
context.enter(iD.modes.Select(context, context.selection().concat([datum.id])));
|
||||
} else {
|
||||
context.enter(iD.modes.Select(context, [datum.id]));
|
||||
}
|
||||
} else if (!d3.event.shiftKey) {
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
}
|
||||
|
||||
var behavior = function(selection) {
|
||||
selection.on('click.select', click);
|
||||
|
||||
var timeout = null,
|
||||
// the position of the first mousedown
|
||||
pos = null;
|
||||
|
||||
function click(event) {
|
||||
d3.event = event;
|
||||
var datum = d3.select(d3.event.target).datum();
|
||||
if (datum instanceof iD.Entity) {
|
||||
if (d3.event.shiftKey) {
|
||||
context.enter(iD.modes.Select(context, context.selection().concat([datum.id])));
|
||||
} else {
|
||||
context.enter(iD.modes.Select(context, [datum.id]));
|
||||
}
|
||||
} else if (!d3.event.shiftKey) {
|
||||
context.enter(iD.modes.Browse(context));
|
||||
}
|
||||
}
|
||||
|
||||
function mousedown() {
|
||||
pos = d3.mouse(context.surface().node());
|
||||
selection
|
||||
.on('mousemove.select', mousemove)
|
||||
.on('touchmove.select', mousemove);
|
||||
|
||||
// we've seen a mousedown within 400ms of this one, so ignore
|
||||
// both because they will be a double click
|
||||
if (timeout !== null) {
|
||||
window.clearTimeout(timeout);
|
||||
selection.on('mousemove.select', null);
|
||||
timeout = null;
|
||||
} else {
|
||||
// queue the click handler to fire in 400ms if no other clicks
|
||||
// are detected
|
||||
timeout = window.setTimeout((function(event) {
|
||||
return function() {
|
||||
click(event);
|
||||
timeout = null;
|
||||
selection.on('mousemove.select', null);
|
||||
};
|
||||
// save the event for the click handler
|
||||
})(d3.event), 400);
|
||||
}
|
||||
}
|
||||
|
||||
// allow mousemoves to cancel the click
|
||||
function mousemove() {
|
||||
if (iD.geo.dist(d3.mouse(context.surface().node()), pos) > 4) {
|
||||
window.clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
}
|
||||
|
||||
selection
|
||||
.on('mousedown.select', mousedown)
|
||||
.on('touchstart.select', mousedown);
|
||||
};
|
||||
|
||||
behavior.off = function(selection) {
|
||||
selection.on('click.select', null);
|
||||
selection.on('mousedown.select', null);
|
||||
};
|
||||
|
||||
return behavior;
|
||||
|
||||
+44
-1
@@ -227,10 +227,53 @@ iD.Graph.prototype = {
|
||||
var items = [];
|
||||
for (var i in this.entities) {
|
||||
var entity = this.entities[i];
|
||||
if (entity && entity.intersects(extent, this)) {
|
||||
if (entity && this.hasAllChildren(entity) && entity.intersects(extent, this)) {
|
||||
items.push(entity);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
},
|
||||
|
||||
hasAllChildren: function(entity) {
|
||||
// we're only checking changed entities, since we assume fetched data
|
||||
// must have all children present
|
||||
if (this.entities.hasOwnProperty(entity.id)) {
|
||||
if (entity.type === 'way') {
|
||||
for (i = 0; i < entity.nodes.length; i++) {
|
||||
if (!this.entities[entity.nodes[i]]) return false;
|
||||
}
|
||||
} else if (entity.type === 'relation') {
|
||||
for (i = 0; i < entity.members.length; i++) {
|
||||
if (!this.entities[entity.members[i].id]) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
// Obliterates any existing entities
|
||||
load: function(entities) {
|
||||
|
||||
var base = this.base(),
|
||||
i, entity, prefix;
|
||||
this.entities = Object.create(base.entities);
|
||||
|
||||
for (i in entities) {
|
||||
entity = entities[i];
|
||||
prefix = i[0];
|
||||
|
||||
if (prefix == 'n') {
|
||||
this.entities[i] = new iD.Node(entity);
|
||||
|
||||
} else if (prefix == 'w') {
|
||||
this.entities[i] = new iD.Way(entity);
|
||||
|
||||
} else if (prefix == 'r') {
|
||||
this.entities[i] = new iD.Relation(entity);
|
||||
}
|
||||
this._updateCalculated(base.entities[i], this.entities[i]);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
+65
-2
@@ -1,7 +1,8 @@
|
||||
iD.History = function() {
|
||||
iD.History = function(context) {
|
||||
var stack, index,
|
||||
imagery_used = 'Bing',
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone');
|
||||
dispatch = d3.dispatch('change', 'undone', 'redone'),
|
||||
lock = false;
|
||||
|
||||
function perform(actions) {
|
||||
actions = Array.prototype.slice.call(actions);
|
||||
@@ -26,6 +27,10 @@ iD.History = function() {
|
||||
return difference;
|
||||
}
|
||||
|
||||
function getKey(n) {
|
||||
return 'iD_' + window.location.origin + '_' + n;
|
||||
}
|
||||
|
||||
var history = {
|
||||
graph: function() {
|
||||
return stack[index].graph;
|
||||
@@ -149,7 +154,65 @@ iD.History = function() {
|
||||
stack = [{graph: iD.Graph()}];
|
||||
index = 0;
|
||||
dispatch.change();
|
||||
},
|
||||
|
||||
save: function() {
|
||||
if (!lock) return;
|
||||
context.storage(getKey('lock'), null);
|
||||
|
||||
if (!stack.length) {
|
||||
context.storage(getKey('history'), null);
|
||||
context.storage(getKey('nextIDs'), null);
|
||||
context.storage(getKey('index'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
var json = JSON.stringify(stack.map(function(i) {
|
||||
return _.extend(i, {
|
||||
graph: i.graph.entities
|
||||
});
|
||||
}));
|
||||
|
||||
context.storage(getKey('history'), json);
|
||||
context.storage(getKey('nextIDs'), JSON.stringify(iD.Entity.id.next));
|
||||
context.storage(getKey('index'), index);
|
||||
},
|
||||
|
||||
lock: function() {
|
||||
if (context.storage(getKey('lock'))) return false;
|
||||
context.storage(getKey('lock'), true);
|
||||
lock = true;
|
||||
return lock;
|
||||
},
|
||||
|
||||
restorableChanges: function() {
|
||||
if (!this.lock()) return false;
|
||||
return !!context.storage(getKey('history'));
|
||||
},
|
||||
|
||||
load: function() {
|
||||
if (!lock) return;
|
||||
|
||||
var json = context.storage(getKey('history')),
|
||||
nextIDs = context.storage(getKey('nextIDs')),
|
||||
index_ = context.storage(getKey('index'));
|
||||
|
||||
if (!json) return;
|
||||
if (nextIDs) iD.Entity.id.next = JSON.parse(nextIDs);
|
||||
if (index_ !== null) index = parseInt(index_, 10);
|
||||
|
||||
context.storage(getKey('history', null));
|
||||
context.storage(getKey('nextIDs', null));
|
||||
context.storage(getKey('index', null));
|
||||
|
||||
stack = JSON.parse(json).map(function(d, i) {
|
||||
d.graph = iD.Graph(stack[0].graph).load(d.graph);
|
||||
return d;
|
||||
});
|
||||
dispatch.change();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
history.reset();
|
||||
|
||||
+29
-6
@@ -26,7 +26,7 @@ _.extend(iD.Relation.prototype, {
|
||||
},
|
||||
|
||||
geometry: function() {
|
||||
return 'relation';
|
||||
return this.isMultipolygon() ? 'area' : 'relation';
|
||||
},
|
||||
|
||||
// Return the first member with the given role. A copy of the member object
|
||||
@@ -83,6 +83,31 @@ _.extend(iD.Relation.prototype, {
|
||||
return r;
|
||||
},
|
||||
|
||||
asGeoJSON: function(resolver) {
|
||||
if (this.isMultipolygon()) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'MultiPolygon',
|
||||
coordinates: this.multipolygon(resolver)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'FeatureCollection',
|
||||
properties: this.tags,
|
||||
features: this.members.map(function(member) {
|
||||
return _.extend({role: member.role}, resolver.entity(member.id).asGeoJSON(resolver));
|
||||
})
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
isMultipolygon: function() {
|
||||
return this.tags.type === 'multipolygon';
|
||||
},
|
||||
|
||||
isRestriction: function() {
|
||||
return !!(this.tags.type && this.tags.type.match(/^restriction:?/));
|
||||
},
|
||||
@@ -145,22 +170,20 @@ _.extend(iD.Relation.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
return joined;
|
||||
return joined.map(function (nodes) { return _.pluck(nodes, 'loc'); });
|
||||
}
|
||||
|
||||
function findOuter(inner) {
|
||||
var o, outer;
|
||||
|
||||
inner = _.pluck(inner, 'loc');
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonContainsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
for (o = 0; o < outers.length; o++) {
|
||||
outer = _.pluck(outers[o], 'loc');
|
||||
outer = outers[o];
|
||||
if (iD.geo.polygonIntersectsPolygon(outer, inner))
|
||||
return o;
|
||||
}
|
||||
|
||||
+20
-8
@@ -49,6 +49,7 @@ _.extend(iD.Way.prototype, {
|
||||
isArea: function() {
|
||||
return this.tags.area === 'yes' ||
|
||||
(this.isClosed() &&
|
||||
!_.isEmpty(this.tags) &&
|
||||
this.tags.area !== 'no' &&
|
||||
!this.tags.highway &&
|
||||
!this.tags.barrier);
|
||||
@@ -103,13 +104,24 @@ _.extend(iD.Way.prototype, {
|
||||
},
|
||||
|
||||
asGeoJSON: function(resolver) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: _.pluck(resolver.childNodes(this), 'loc')
|
||||
}
|
||||
};
|
||||
if (this.isArea()) {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'Polygon',
|
||||
coordinates: [_.pluck(resolver.childNodes(this), 'loc')]
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'Feature',
|
||||
properties: this.tags,
|
||||
geometry: {
|
||||
type: 'LineString',
|
||||
coordinates: _.pluck(resolver.childNodes(this), 'loc')
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
+9
-7
@@ -1,18 +1,20 @@
|
||||
window.iD = function () {
|
||||
var context = {},
|
||||
history = iD.History(),
|
||||
storage = localStorage || {},
|
||||
storage = localStorage || {};
|
||||
|
||||
context.storage = function(k, v) {
|
||||
if (arguments.length === 1) return storage[k];
|
||||
else if (v === null) delete storage[k];
|
||||
else storage[k] = v;
|
||||
};
|
||||
|
||||
var history = iD.History(context),
|
||||
dispatch = d3.dispatch('enter', 'exit'),
|
||||
mode,
|
||||
container,
|
||||
ui = iD.ui(context),
|
||||
map = iD.Map(context);
|
||||
|
||||
context.storage = function(k, v) {
|
||||
if (arguments.length === 1) return storage[k];
|
||||
else storage[k] = v;
|
||||
};
|
||||
|
||||
// the connection requires .storage() to be available on calling.
|
||||
var connection = iD.Connection(context);
|
||||
|
||||
|
||||
@@ -10,8 +10,7 @@ iD.modes.Browse = function(context) {
|
||||
var behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(context),
|
||||
iD.behavior.DragNode(context),
|
||||
iD.behavior.DragMidpoint(context)];
|
||||
iD.behavior.DragNode(context)];
|
||||
|
||||
mode.enter = function() {
|
||||
behaviors.forEach(function(behavior) {
|
||||
|
||||
+35
-7
@@ -6,7 +6,8 @@ iD.modes.MoveWay = function(context, wayId) {
|
||||
var keybinding = d3.keybinding('move-way');
|
||||
|
||||
mode.enter = function() {
|
||||
var origin = point(),
|
||||
var origin = context.map().mouseCoordinates(),
|
||||
nudgeInterval,
|
||||
annotation = t('operations.move.annotation.' + context.geometry(wayId));
|
||||
|
||||
// If intiated via keyboard
|
||||
@@ -16,17 +17,44 @@ iD.modes.MoveWay = function(context, wayId) {
|
||||
iD.actions.Noop(),
|
||||
annotation);
|
||||
|
||||
function edge(point, size) {
|
||||
var pad = [30, 100, 30, 100];
|
||||
if (point[0] > size[0] - pad[0]) return [-10, 0];
|
||||
else if (point[0] < pad[2]) return [10, 0];
|
||||
else if (point[1] > size[1] - pad[1]) return [0, -10];
|
||||
else if (point[1] < pad[3]) return [0, 10];
|
||||
return null;
|
||||
}
|
||||
|
||||
function startNudge(nudge) {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = window.setInterval(function() {
|
||||
context.map().pan(nudge).redraw();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function stopNudge() {
|
||||
if (nudgeInterval) window.clearInterval(nudgeInterval);
|
||||
nudgeInterval = null;
|
||||
}
|
||||
|
||||
function point() {
|
||||
return d3.mouse(context.surface().node());
|
||||
return d3.mouse(context.map().surface.node());
|
||||
}
|
||||
|
||||
function move() {
|
||||
var p = point(),
|
||||
delta = origin ?
|
||||
[p[0] - origin[0], p[1] - origin[1]] :
|
||||
[0, 0];
|
||||
var p = point();
|
||||
|
||||
origin = p;
|
||||
var delta = origin ?
|
||||
[p[0] - context.projection(origin)[0],
|
||||
p[1] - context.projection(origin)[1]] :
|
||||
[0, 0];
|
||||
|
||||
var nudge = edge(p, context.map().size());
|
||||
if (nudge) startNudge(nudge);
|
||||
else stopNudge();
|
||||
|
||||
origin = context.map().mouseCoordinates();
|
||||
|
||||
context.replace(
|
||||
iD.actions.MoveWay(wayId, delta, context.projection),
|
||||
|
||||
+34
-6
@@ -9,8 +9,7 @@ iD.modes.Select = function(context, selection, initial) {
|
||||
behaviors = [
|
||||
iD.behavior.Hover(),
|
||||
iD.behavior.Select(context),
|
||||
iD.behavior.DragNode(context),
|
||||
iD.behavior.DragMidpoint(context)],
|
||||
iD.behavior.DragNode(context)],
|
||||
radialMenu;
|
||||
|
||||
function changeTags(d, tags) {
|
||||
@@ -113,9 +112,29 @@ iD.modes.Select = function(context, selection, initial) {
|
||||
d3.mouse(context.surface().node()), context),
|
||||
node = iD.Node({ loc: choice.loc });
|
||||
|
||||
context.perform(
|
||||
iD.actions.AddEntity(node),
|
||||
iD.actions.AddVertex(datum.id, node.id, choice.index),
|
||||
var prev = datum.nodes[choice.index - 1],
|
||||
next = datum.nodes[choice.index],
|
||||
prevParents = context.graph().parentWays({ id: prev }),
|
||||
ways = [];
|
||||
|
||||
|
||||
for (var i = 0; i < prevParents.length; i++) {
|
||||
var p = prevParents[i];
|
||||
for (var k = 0; k < p.nodes.length; k++) {
|
||||
if (p.nodes[k] === prev) {
|
||||
if (p.nodes[k-1] === next) {
|
||||
ways.push({ id: p.id, index: k});
|
||||
break;
|
||||
} else if (p.nodes[k+1] === next) {
|
||||
ways.push({ id: p.id, index: k+1});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.perform(iD.actions.AddEntity(node),
|
||||
iD.actions.AddMidpoint({ ways: ways, loc: node.loc }, node),
|
||||
t('operations.add.annotation.vertex'));
|
||||
|
||||
d3.event.preventDefault();
|
||||
@@ -123,13 +142,22 @@ iD.modes.Select = function(context, selection, initial) {
|
||||
}
|
||||
}
|
||||
|
||||
function selected(entity) {
|
||||
if (!entity) return false;
|
||||
if (selection.indexOf(entity.id) >= 0) return true;
|
||||
return d3.select(this).classed('stroke') &&
|
||||
_.any(context.graph().parentRelations(entity), function(parent) {
|
||||
return selection.indexOf(parent.id) >= 0;
|
||||
});
|
||||
}
|
||||
|
||||
d3.select(document)
|
||||
.call(keybinding);
|
||||
|
||||
context.surface()
|
||||
.on('dblclick.select', dblclick)
|
||||
.selectAll("*")
|
||||
.filter(function(d) { return d && selection.indexOf(d.id) >= 0; })
|
||||
.filter(selected)
|
||||
.classed('selected', true);
|
||||
|
||||
radialMenu = iD.ui.RadialMenu(operations);
|
||||
|
||||
@@ -130,8 +130,6 @@ iD.Background = function() {
|
||||
.on('load', load);
|
||||
|
||||
image.style(transformProp, imageTransform);
|
||||
|
||||
if (Object.keys(cache).length > 100) cache = {};
|
||||
}
|
||||
|
||||
background.offset = function(_) {
|
||||
|
||||
@@ -19,7 +19,6 @@ iD.Map = function(context) {
|
||||
vertices = iD.svg.Vertices(roundedProjection),
|
||||
lines = iD.svg.Lines(roundedProjection),
|
||||
areas = iD.svg.Areas(roundedProjection),
|
||||
multipolygons = iD.svg.Multipolygons(roundedProjection),
|
||||
midpoints = iD.svg.Midpoints(roundedProjection),
|
||||
labels = iD.svg.Labels(roundedProjection),
|
||||
tail = d3.tail(),
|
||||
@@ -87,7 +86,6 @@ iD.Map = function(context) {
|
||||
.call(vertices, graph, all, filter)
|
||||
.call(lines, graph, all, filter)
|
||||
.call(areas, graph, all, filter)
|
||||
.call(multipolygons, graph, all, filter)
|
||||
.call(midpoints, graph, all, filter)
|
||||
.call(labels, graph, all, filter, dimensions, !difference);
|
||||
}
|
||||
|
||||
@@ -27,5 +27,17 @@ iD.svg = {
|
||||
return projection(n.loc);
|
||||
}).join('L'));
|
||||
};
|
||||
},
|
||||
|
||||
MultipolygonMemberTags: function (graph) {
|
||||
return function (entity) {
|
||||
var tags = entity.tags;
|
||||
graph.parentRelations(entity).forEach(function (relation) {
|
||||
if (relation.isMultipolygon()) {
|
||||
tags = _.extend({}, relation.tags, tags);
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+20
-14
@@ -1,38 +1,39 @@
|
||||
iD.svg.Areas = function(projection) {
|
||||
return function drawAreas(surface, graph, entities, filter) {
|
||||
var areas = [];
|
||||
var path = d3.geo.path().projection(projection),
|
||||
areas = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (entity.geometry(graph) === 'area') {
|
||||
var points = graph.childNodes(entity).map(function(n) {
|
||||
return projection(n.loc);
|
||||
});
|
||||
|
||||
areas.push({
|
||||
entity: entity,
|
||||
area: entity.isDegenerate() ? 0 : Math.abs(d3.geom.polygon(points).area())
|
||||
area: Math.abs(path.area(entity.asGeoJSON(graph)))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
areas.sort(function(a, b) { return b.area - a.area; });
|
||||
|
||||
var lineString = iD.svg.LineString(projection, graph);
|
||||
function drawPaths(group, areas, filter, klass) {
|
||||
var tagClasses = iD.svg.TagClasses();
|
||||
|
||||
if (klass === 'stroke') {
|
||||
tagClasses.tags(iD.svg.MultipolygonMemberTags(graph));
|
||||
}
|
||||
|
||||
function drawPaths(group, areas, filter, classes) {
|
||||
var paths = group.selectAll('path.area')
|
||||
.filter(filter)
|
||||
.data(areas, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
.attr('class', function (d) { return d.type + ' area ' + klass; });
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.attr('d', function (entity) { return path(entity.asGeoJSON(graph)); })
|
||||
.call(tagClasses)
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
@@ -43,9 +44,14 @@ iD.svg.Areas = function(projection) {
|
||||
|
||||
areas = _.pluck(areas, 'entity');
|
||||
|
||||
var strokes = areas.filter(function (area) {
|
||||
return area.type === 'way';
|
||||
});
|
||||
|
||||
var fill = surface.select('.layer-fill'),
|
||||
stroke = surface.select('.layer-stroke'),
|
||||
fills = drawPaths(fill, areas, filter, 'way area fill'),
|
||||
strokes = drawPaths(stroke, areas, filter, 'way area stroke');
|
||||
stroke = surface.select('.layer-stroke');
|
||||
|
||||
drawPaths(fill, areas, filter, 'fill');
|
||||
drawPaths(stroke, strokes, filter, 'stroke');
|
||||
};
|
||||
};
|
||||
|
||||
+2
-3
@@ -335,9 +335,8 @@ iD.svg.Labels = function(projection) {
|
||||
}
|
||||
|
||||
function getAreaLabel(entity, width, height) {
|
||||
var nodes = _.pluck(graph.childNodes(entity), 'loc')
|
||||
.map(iD.svg.RoundProjection(projection)),
|
||||
centroid = d3.geom.polygon(nodes).centroid(),
|
||||
var path = d3.geo.path().projection(projection),
|
||||
centroid = path.centroid(entity.asGeoJSON(graph)),
|
||||
extent = entity.extent(graph),
|
||||
entitywidth = projection(extent[1])[0] - projection(extent[0])[0];
|
||||
|
||||
|
||||
+19
-10
@@ -34,19 +34,25 @@ iD.svg.Lines = function(projection) {
|
||||
}
|
||||
|
||||
return function drawLines(surface, graph, entities, filter) {
|
||||
function drawPaths(group, lines, filter, classes, lineString) {
|
||||
function drawPaths(group, lines, filter, klass, lineString) {
|
||||
var tagClasses = iD.svg.TagClasses();
|
||||
|
||||
if (klass === 'stroke') {
|
||||
tagClasses.tags(iD.svg.MultipolygonMemberTags(graph));
|
||||
}
|
||||
|
||||
var paths = group.selectAll('path.line')
|
||||
.filter(filter)
|
||||
.data(lines, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
.attr('class', 'way line ' + klass);
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.call(tagClasses)
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
@@ -56,13 +62,16 @@ iD.svg.Lines = function(projection) {
|
||||
}
|
||||
|
||||
if (!alength) {
|
||||
var arrow = surface.append('text').text(arrowtext);
|
||||
var container = surface.append('g')
|
||||
.attr('class', 'oneway'),
|
||||
arrow = container.append('text')
|
||||
.attr('class', 'textpath')
|
||||
.text(arrowtext);
|
||||
alength = arrow.node().getComputedTextLength();
|
||||
arrow.remove();
|
||||
container.remove();
|
||||
}
|
||||
|
||||
var lines = [],
|
||||
lineStrings = {};
|
||||
var lines = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
@@ -80,9 +89,9 @@ iD.svg.Lines = function(projection) {
|
||||
stroke = surface.select('.layer-stroke'),
|
||||
defs = surface.select('defs'),
|
||||
text = surface.select('.layer-text'),
|
||||
shadows = drawPaths(shadow, lines, filter, 'way line shadow', lineString),
|
||||
casings = drawPaths(casing, lines, filter, 'way line casing', lineString),
|
||||
strokes = drawPaths(stroke, lines, filter, 'way line stroke', lineString);
|
||||
shadows = drawPaths(shadow, lines, filter, 'shadow', lineString),
|
||||
casings = drawPaths(casing, lines, filter, 'casing', lineString),
|
||||
strokes = drawPaths(stroke, lines, filter, 'stroke', lineString);
|
||||
|
||||
// Determine the lengths of oneway paths
|
||||
var lengths = {},
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
iD.svg.Multipolygons = function(projection) {
|
||||
return function(surface, graph, entities, filter) {
|
||||
var multipolygons = [];
|
||||
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
var entity = entities[i];
|
||||
if (entity.geometry(graph) === 'relation' && entity.tags.type === 'multipolygon') {
|
||||
multipolygons.push(entity);
|
||||
}
|
||||
}
|
||||
|
||||
var lineStrings = {};
|
||||
|
||||
function lineString(entity) {
|
||||
if (lineStrings[entity.id] !== undefined) {
|
||||
return lineStrings[entity.id];
|
||||
}
|
||||
|
||||
var multipolygon = entity.multipolygon(graph);
|
||||
if (entity.members.length === 0 || !multipolygon) {
|
||||
return (lineStrings[entity.id] = null);
|
||||
}
|
||||
|
||||
multipolygon = _.flatten(multipolygon, true);
|
||||
return (lineStrings[entity.id] =
|
||||
multipolygon.map(function (ring) {
|
||||
return 'M' + ring.map(function (node) { return projection(node.loc); }).join('L');
|
||||
}).join(""));
|
||||
}
|
||||
|
||||
function drawPaths(group, multipolygons, filter, classes) {
|
||||
var paths = group.selectAll('path.multipolygon')
|
||||
.filter(filter)
|
||||
.data(multipolygons, iD.Entity.key);
|
||||
|
||||
paths.enter()
|
||||
.append('path')
|
||||
.attr('class', classes);
|
||||
|
||||
paths
|
||||
.order()
|
||||
.attr('d', lineString)
|
||||
.call(iD.svg.TagClasses())
|
||||
.call(iD.svg.MemberClasses(graph));
|
||||
|
||||
paths.exit()
|
||||
.remove();
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
var fill = surface.select('.layer-fill'),
|
||||
paths = drawPaths(fill, multipolygons, filter, 'relation multipolygon');
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -79,7 +79,7 @@ iD.svg.Points.imageIndex = [
|
||||
},
|
||||
{
|
||||
tags: { man_made: 'lighthouse' },
|
||||
icon: 'lighthouselevel_crossing'
|
||||
icon: 'lighthouse'
|
||||
},
|
||||
{
|
||||
tags: { natural: 'peak' },
|
||||
|
||||
@@ -3,10 +3,11 @@ iD.svg.TagClasses = function() {
|
||||
'highway', 'railway', 'waterway', 'power', 'motorway', 'amenity',
|
||||
'natural', 'landuse', 'building', 'oneway', 'bridge', 'boundary',
|
||||
'leisure', 'construction'
|
||||
]), tagClassRe = /^tag-/;
|
||||
]), tagClassRe = /^tag-/,
|
||||
tags = function(entity) { return entity.tags; };
|
||||
|
||||
return function tagClassesSelection(selection) {
|
||||
selection.each(function tagClassesEach(d, i) {
|
||||
var tagClasses = function(selection) {
|
||||
selection.each(function tagClassesEach(entity) {
|
||||
var classes, value = this.className;
|
||||
|
||||
if (value.baseVal !== undefined) value = value.baseVal;
|
||||
@@ -15,11 +16,10 @@ iD.svg.TagClasses = function() {
|
||||
return name.length && !tagClassRe.test(name);
|
||||
}).join(' ');
|
||||
|
||||
var tags = d.tags;
|
||||
for (var k in tags) {
|
||||
var t = tags(entity);
|
||||
for (var k in t) {
|
||||
if (!keys[k]) continue;
|
||||
classes += ' tag-' + k + ' ' +
|
||||
'tag-' + k + '-' + tags[k];
|
||||
classes += ' tag-' + k + ' ' + 'tag-' + k + '-' + t[k];
|
||||
}
|
||||
|
||||
classes = classes.trim();
|
||||
@@ -29,4 +29,12 @@ iD.svg.TagClasses = function() {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
tagClasses.tags = function(_) {
|
||||
if (!arguments.length) return tags;
|
||||
tags = _;
|
||||
return tagClasses;
|
||||
};
|
||||
|
||||
return tagClasses;
|
||||
};
|
||||
|
||||
+7
-1
@@ -195,6 +195,7 @@ iD.ui = function(context) {
|
||||
|
||||
history.on('change.editor', function() {
|
||||
window.onbeforeunload = history.hasChanges() ? function() {
|
||||
history.save();
|
||||
return 'You have unsaved changes.';
|
||||
} : null;
|
||||
|
||||
@@ -253,8 +254,13 @@ iD.ui = function(context) {
|
||||
context.enter(iD.modes.Browse(context));
|
||||
|
||||
if (!context.storage('sawSplash')) {
|
||||
iD.ui.splash();
|
||||
iD.ui.splash(context.container());
|
||||
context.storage('sawSplash', true);
|
||||
}
|
||||
|
||||
if (history.restorableChanges()) {
|
||||
iD.ui.restore(context.container(), history);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -108,7 +108,13 @@ iD.ui.layerswitcher = function(context) {
|
||||
d = configured;
|
||||
}
|
||||
context.background().source(d);
|
||||
context.history().imagery_used(d.data.sourcetag || d.data.name);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
iD.ui.restore = function(selection, history) {
|
||||
var modal = iD.ui.modal(selection);
|
||||
|
||||
modal.select('.modal')
|
||||
.attr('class', 'modal-splash modal');
|
||||
|
||||
var introModal = modal.select('.content')
|
||||
.append('div')
|
||||
.attr('class', 'modal-section fillL')
|
||||
.text('You have unsaved changes from a previous editing session. Do you wish to restore these changes?');
|
||||
|
||||
buttons = introModal
|
||||
.append('div')
|
||||
.attr('class', 'buttons cf')
|
||||
.append('div')
|
||||
.attr('class', 'button-wrap joined col4');
|
||||
|
||||
buttons.append('button')
|
||||
.attr('class', 'save action button col6')
|
||||
.text('Restore')
|
||||
.on('click', function() {
|
||||
history.load();
|
||||
modal.remove();
|
||||
});
|
||||
|
||||
buttons.append('button')
|
||||
.attr('class', 'cancel button col6')
|
||||
.text('Reset')
|
||||
.on('click', function() {
|
||||
modal.remove();
|
||||
});
|
||||
|
||||
return modal;
|
||||
};
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
iD.ui.splash = function() {
|
||||
var modal = iD.ui.modal();
|
||||
iD.ui.splash = function(selection) {
|
||||
var modal = iD.ui.modal(selection);
|
||||
|
||||
modal.select('.modal')
|
||||
.attr('class', 'modal-splash modal');
|
||||
|
||||
+1
-4
@@ -52,7 +52,6 @@
|
||||
<script src="../js/id/svg/lines.js"></script>
|
||||
<script src="../js/id/svg/member_classes.js"></script>
|
||||
<script src="../js/id/svg/midpoints.js"></script>
|
||||
<script src="../js/id/svg/multipolygons.js"></script>
|
||||
<script src="../js/id/svg/points.js"></script>
|
||||
<script src="../js/id/svg/surface.js"></script>
|
||||
<script src="../js/id/svg/tag_classes.js"></script>
|
||||
@@ -96,7 +95,6 @@
|
||||
<script src='../js/id/behavior.js'></script>
|
||||
<script src='../js/id/behavior/add_way.js'></script>
|
||||
<script src='../js/id/behavior/drag.js'></script>
|
||||
<script src='../js/id/behavior/drag_midpoint.js'></script>
|
||||
<script src='../js/id/behavior/drag_node.js'></script>
|
||||
<script src='../js/id/behavior/draw.js'></script>
|
||||
<script src='../js/id/behavior/draw_way.js'></script>
|
||||
@@ -142,7 +140,7 @@
|
||||
iD.debug = true;
|
||||
mocha.setup({
|
||||
ui: 'bdd',
|
||||
globals: ['__onresize.tail-size']
|
||||
globals: ['__onresize.tail-size', '__onmousemove.zoom', '__onmouseup.zoom', '__onclick.draw']
|
||||
});
|
||||
var expect = chai.expect;
|
||||
</script>
|
||||
@@ -187,7 +185,6 @@
|
||||
<script src="spec/svg/lines.js"></script>
|
||||
<script src="spec/svg/member_classes.js"></script>
|
||||
<script src="spec/svg/midpoints.js"></script>
|
||||
<script src="spec/svg/multipolygons.js"></script>
|
||||
<script src="spec/svg/points.js"></script>
|
||||
<script src="spec/svg/vertices.js"></script>
|
||||
<script src="spec/svg/tag_classes.js"></script>
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
<script src="spec/svg/lines.js"></script>
|
||||
<script src="spec/svg/member_classes.js"></script>
|
||||
<script src="spec/svg/midpoints.js"></script>
|
||||
<script src="spec/svg/multipolygons.js"></script>
|
||||
<script src="spec/svg/points.js"></script>
|
||||
<script src="spec/svg/vertices.js"></script>
|
||||
<script src="spec/svg/tag_classes.js"></script>
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
<script src="../js/id/svg/lines.js"></script>
|
||||
<script src="../js/id/svg/member_classes.js"></script>
|
||||
<script src="../js/id/svg/midpoints.js"></script>
|
||||
<script src="../js/id/svg/multipolygons.js"></script>
|
||||
<script src="../js/id/svg/points.js"></script>
|
||||
<script src="../js/id/svg/surface.js"></script>
|
||||
<script src="../js/id/svg/tag_classes.js"></script>
|
||||
|
||||
@@ -29,21 +29,31 @@ describe("iD.behavior.Select", function() {
|
||||
container.remove();
|
||||
});
|
||||
|
||||
specify("click on entity selects the entity", function() {
|
||||
happen.click(context.surface().select('.' + a.id).node());
|
||||
expect(context.selection()).to.eql([a.id]);
|
||||
specify("click on entity selects the entity", function(done) {
|
||||
happen.mousedown(context.surface().select('.' + a.id).node());
|
||||
window.setTimeout(function() {
|
||||
expect(context.selection()).to.eql([a.id]);
|
||||
done();
|
||||
}, 600);
|
||||
});
|
||||
|
||||
specify("click on empty space clears the selection", function() {
|
||||
specify("click on empty space clears the selection", function(done) {
|
||||
context.enter(iD.modes.Select(context, [a.id]));
|
||||
happen.click(context.surface().node());
|
||||
expect(context.selection()).to.eql([]);
|
||||
happen.mousedown(context.surface().node());
|
||||
window.setTimeout(function() {
|
||||
expect(context.selection()).to.eql([]);
|
||||
done();
|
||||
}, 600);
|
||||
});
|
||||
|
||||
specify("shift-click on entity adds the entity to the selection", function() {
|
||||
specify("shift-click on entity adds the entity to the selection", function(done) {
|
||||
context.enter(iD.modes.Select(context, [a.id]));
|
||||
happen.click(context.surface().select('.' + b.id).node(), {shiftKey: true});
|
||||
expect(context.selection()).to.eql([a.id, b.id]);
|
||||
happen.mousedown(context.surface().select('.' + b.id).node(), {shiftKey: true});
|
||||
window.setTimeout(function() {
|
||||
expect(context.selection()).to.eql([a.id, b.id]);
|
||||
done();
|
||||
}, 600);
|
||||
});
|
||||
|
||||
specify("shift-click on empty space leaves the selection unchanged", function() {
|
||||
|
||||
+81
-53
@@ -146,107 +146,135 @@ describe('iD.Relation', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#asGeoJSON", function (){
|
||||
it('converts a multipolygon to a GeoJSON MultiPolygon feature', function() {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]),
|
||||
json = r.asGeoJSON(g);
|
||||
|
||||
expect(json.type).to.equal('Feature');
|
||||
expect(json.properties).to.eql({type: 'multipolygon'});
|
||||
expect(json.geometry.type).to.equal('MultiPolygon');
|
||||
expect(json.geometry.coordinates).to.eql([[[[1, 1], [2, 2], [3, 3], [1, 1]]]]);
|
||||
});
|
||||
|
||||
it('converts a relation to a GeoJSON FeatureCollection', function() {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
r = iD.Relation({tags: {type: 'type'}, members: [{id: a.id, role: 'role'}]}),
|
||||
g = iD.Graph([a, r]),
|
||||
json = r.asGeoJSON(g);
|
||||
|
||||
expect(json.type).to.equal('FeatureCollection');
|
||||
expect(json.properties).to.eql({type: 'type'});
|
||||
expect(json.features).to.eql([_.extend({role: 'role'}, a.asGeoJSON(g))]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#multipolygon", function () {
|
||||
specify("single polygon consisting of a single way", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify("single polygon consisting of multiple ways", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way({nodes: [c.id, d.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d, a]]]); // TODO: not the only valid ordering
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc, a.loc]]]); // TODO: not the only valid ordering
|
||||
});
|
||||
|
||||
specify("single polygon consisting of multiple ways, one needing reversal", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way({nodes: [a.id, d.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d, a]]]); // TODO: not the only valid ordering
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc, a.loc]]]); // TODO: not the only valid ordering
|
||||
});
|
||||
|
||||
specify("multiple polygons consisting of single ways", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
e = iD.Node(),
|
||||
f = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
e = iD.Node({loc: [5, 5]}),
|
||||
f = iD.Node({loc: [6, 6]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
w2 = iD.Way({nodes: [d.id, e.id, f.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a]], [[d, e, f, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]], [[d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unclosed ring consisting of a single way", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unclosed ring consisting of multiple ways", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way({nodes: [c.id, d.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unclosed ring consisting of multiple ways, alternate order", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [c.id, d.id]}),
|
||||
w2 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unclosed ring consisting of multiple ways, one needing reversal", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
d = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
d = iD.Node({loc: [4, 4]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way({nodes: [d.id, c.id]}),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unclosed ring consisting of multiple ways, one needing reversal, alternate order", function () {
|
||||
@@ -259,7 +287,7 @@ describe('iD.Relation', function () {
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, w1, w2, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with single single-way inner", function () {
|
||||
@@ -274,7 +302,7 @@ describe('iD.Relation', function () {
|
||||
r = iD.Relation({members: [{id: outer.id, type: 'way'}, {id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, d, e, f, outer, inner, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a], [d, e, f, d]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with single multi-way inner", function () {
|
||||
@@ -293,7 +321,7 @@ describe('iD.Relation', function () {
|
||||
{id: inner1.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d]]]);
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]]]);
|
||||
});
|
||||
|
||||
specify("single polygon with multiple single-way inners", function () {
|
||||
@@ -315,7 +343,7 @@ describe('iD.Relation', function () {
|
||||
{id: inner1.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer, inner1, inner2, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d], [g, h, i, g]]]);
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc], [g.loc, h.loc, i.loc, g.loc]]]);
|
||||
});
|
||||
|
||||
specify("multiple polygons with single single-way inner", function () {
|
||||
@@ -337,30 +365,30 @@ describe('iD.Relation', function () {
|
||||
{id: inner.id, role: 'inner', type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, d, e, f, g, h, i, outer1, outer2, inner, r]);
|
||||
|
||||
expect(r.multipolygon(graph)).to.eql([[[a, b, c, a], [d, e, f, d]], [[g, h, i, g]]]);
|
||||
expect(r.multipolygon(graph)).to.eql([[[a.loc, b.loc, c.loc, a.loc], [d.loc, e.loc, f.loc, d.loc]], [[g.loc, h.loc, i.loc, g.loc]]]);
|
||||
});
|
||||
|
||||
specify("invalid geometry: unmatched inner", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id, role: 'inner', type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c, a]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc, a.loc]]]);
|
||||
});
|
||||
|
||||
specify("incomplete relation", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w1 = iD.Way({nodes: [a.id, b.id, c.id]}),
|
||||
w2 = iD.Way(),
|
||||
r = iD.Relation({members: [{id: w2.id, type: 'way'}, {id: w1.id, type: 'way'}]}),
|
||||
g = iD.Graph([a, b, c, w1, r]);
|
||||
|
||||
expect(r.multipolygon(g)).to.eql([[[a, b, c]]]);
|
||||
expect(r.multipolygon(g)).to.eql([[[a.loc, b.loc, c.loc]]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+21
-3
@@ -95,8 +95,12 @@ describe('iD.Way', function() {
|
||||
expect(iD.Way({tags: { area: 'yes' }}).isArea()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns true if the way is closed and has no tags', function() {
|
||||
expect(iD.Way({nodes: ['n1', 'n1']}).isArea()).to.equal(true);
|
||||
it('returns false if the way is closed and has no tags', function() {
|
||||
expect(iD.Way({nodes: ['n1', 'n1']}).isArea()).to.equal(false);
|
||||
});
|
||||
|
||||
it('returns true if the way is closed and has tags', function() {
|
||||
expect(iD.Way({nodes: ['n1', 'n1'], tags: {a: 'b'}}).isArea()).to.equal(true);
|
||||
});
|
||||
|
||||
it('returns false if the way is closed and has tag area=no', function() {
|
||||
@@ -207,7 +211,7 @@ describe('iD.Way', function() {
|
||||
});
|
||||
|
||||
describe("#asGeoJSON", function () {
|
||||
it("converts to a GeoJSON LineString features", function () {
|
||||
it("converts a line to a GeoJSON LineString features", function () {
|
||||
var a = iD.Node({loc: [1, 2]}),
|
||||
b = iD.Node({loc: [3, 4]}),
|
||||
w = iD.Way({tags: {highway: 'residential'}, nodes: [a.id, b.id]}),
|
||||
@@ -219,5 +223,19 @@ describe('iD.Way', function() {
|
||||
expect(json.geometry.type).to.equal('LineString');
|
||||
expect(json.geometry.coordinates).to.eql([[1, 2], [3, 4]]);
|
||||
});
|
||||
|
||||
it("converts an area to a GeoJSON Polygon features", function () {
|
||||
var a = iD.Node({loc: [1, 2]}),
|
||||
b = iD.Node({loc: [3, 4]}),
|
||||
c = iD.Node({loc: [5, 6]}),
|
||||
w = iD.Way({tags: {area: 'yes'}, nodes: [a.id, b.id, c.id, a.id]}),
|
||||
graph = iD.Graph([a, b, c, w]),
|
||||
json = w.asGeoJSON(graph);
|
||||
|
||||
expect(json.type).to.equal('Feature');
|
||||
expect(json.properties).to.eql({area: 'yes'});
|
||||
expect(json.geometry.type).to.equal('Polygon');
|
||||
expect(json.geometry.coordinates).to.eql([[[1, 2], [3, 4], [5, 6], [1, 2]]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
describe("iD.modes.AddPoint", function () {
|
||||
describe("iD.modes.AddPoint", function() {
|
||||
var context;
|
||||
|
||||
beforeEach(function () {
|
||||
beforeEach(function() {
|
||||
var container = d3.select(document.createElement('div'));
|
||||
|
||||
context = iD()
|
||||
@@ -15,20 +15,22 @@ describe("iD.modes.AddPoint", function () {
|
||||
});
|
||||
|
||||
describe("clicking the map", function () {
|
||||
it("adds a node", function () {
|
||||
happen.click(context.surface().node(), {});
|
||||
it("adds a node", function() {
|
||||
happen.mousedown(context.surface().node(), {});
|
||||
happen.click(window, {});
|
||||
expect(context.changes().created).to.have.length(1);
|
||||
});
|
||||
|
||||
it("selects the node", function () {
|
||||
happen.click(context.surface().node(), {});
|
||||
it("selects the node", function() {
|
||||
happen.mousedown(context.surface().node(), {});
|
||||
happen.click(window, {});
|
||||
expect(context.mode().id).to.equal('select');
|
||||
expect(context.mode().selection()).to.eql([context.changes().created[0].id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("pressing ⎋", function () {
|
||||
it("exits to browse mode", function () {
|
||||
describe("pressing ⎋", function() {
|
||||
it("exits to browse mode", function() {
|
||||
happen.keydown(document, {keyCode: 27});
|
||||
expect(context.mode().id).to.equal('browse');
|
||||
});
|
||||
|
||||
@@ -69,4 +69,46 @@ describe("iD.svg.Areas", function () {
|
||||
expect(surface.select('.area:nth-child(1)')).to.be.classed('tag-landuse-park');
|
||||
expect(surface.select('.area:nth-child(2)')).to.be.classed('tag-building-yes');
|
||||
});
|
||||
|
||||
it("renders fills for multipolygon areas", function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, w, r]),
|
||||
areas = [w, r];
|
||||
|
||||
surface.call(iD.svg.Areas(projection), graph, areas, filter);
|
||||
|
||||
expect(surface.select('.fill')).to.be.classed('relation');
|
||||
});
|
||||
|
||||
it("renders no strokes for multipolygon areas", function () {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({tags: {type: 'multipolygon'}, members: [{id: w.id, type: 'way'}]}),
|
||||
graph = iD.Graph([a, b, c, w, r]),
|
||||
areas = [w, r];
|
||||
|
||||
surface.call(iD.svg.Areas(projection), graph, areas, filter);
|
||||
|
||||
expect(surface.selectAll('.stroke')[0].length).to.equal(0);
|
||||
});
|
||||
|
||||
it("adds stroke classes for the tags of the parent relation of multipolygon members", function() {
|
||||
var a = iD.Node({loc: [1, 1]}),
|
||||
b = iD.Node({loc: [2, 2]}),
|
||||
c = iD.Node({loc: [3, 3]}),
|
||||
w = iD.Way({tags: {area: 'yes'}, nodes: [a.id, b.id, c.id, a.id]}),
|
||||
r = iD.Relation({members: [{id: w.id}], tags: {type: 'multipolygon', natural: 'wood'}}),
|
||||
graph = iD.Graph([a, b, c, w, r]);
|
||||
|
||||
surface.call(iD.svg.Areas(projection), graph, [w], filter);
|
||||
|
||||
expect(surface.select('.stroke')).to.be.classed('tag-natural-wood');
|
||||
expect(surface.select('.fill')).not.to.be.classed('tag-natural-wood');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,6 +39,16 @@ describe("iD.svg.Lines", function () {
|
||||
expect(surface.select('.line')).to.be.classed('member-type-route');
|
||||
});
|
||||
|
||||
it("adds stroke classes for the tags of the parent relation of multipolygon members", function() {
|
||||
var line = iD.Way(),
|
||||
relation = iD.Relation({members: [{id: line.id}], tags: {type: 'multipolygon', natural: 'wood'}}),
|
||||
graph = iD.Graph([line, relation]);
|
||||
|
||||
surface.call(iD.svg.Lines(projection), graph, [line], filter);
|
||||
|
||||
expect(surface.select('.stroke')).to.be.classed('tag-natural-wood');
|
||||
});
|
||||
|
||||
it("preserves non-line paths", function () {
|
||||
var line = iD.Way(),
|
||||
graph = iD.Graph([line]);
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
describe("iD.svg.Multipolygons", function () {
|
||||
var surface,
|
||||
projection = Object,
|
||||
filter = d3.functor(true);
|
||||
|
||||
beforeEach(function () {
|
||||
surface = d3.select(document.createElementNS('http://www.w3.org/2000/svg', 'svg'))
|
||||
.call(iD.svg.Surface());
|
||||
});
|
||||
|
||||
it("adds relation and multipolygon classes", function () {
|
||||
var relation = iD.Relation({tags: {type: 'multipolygon'}}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
surface.call(iD.svg.Multipolygons(projection), graph, [relation], filter);
|
||||
|
||||
expect(surface.select('path')).to.be.classed('relation');
|
||||
expect(surface.select('path')).to.be.classed('multipolygon');
|
||||
});
|
||||
|
||||
it("adds tag classes", function () {
|
||||
var relation = iD.Relation({tags: {type: 'multipolygon', boundary: "administrative"}}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
surface.call(iD.svg.Multipolygons(projection), graph, [relation], filter);
|
||||
|
||||
expect(surface.select('.relation')).to.be.classed('tag-boundary');
|
||||
expect(surface.select('.relation')).to.be.classed('tag-boundary-administrative');
|
||||
});
|
||||
|
||||
it("preserves non-multipolygon paths", function () {
|
||||
var relation = iD.Relation({tags: {type: 'multipolygon'}}),
|
||||
graph = iD.Graph([relation]);
|
||||
|
||||
surface.select('.layer-fill')
|
||||
.append('path')
|
||||
.attr('class', 'other');
|
||||
|
||||
surface.call(iD.svg.Multipolygons(projection), graph, [relation], filter);
|
||||
|
||||
expect(surface.selectAll('.other')[0].length).to.equal(1);
|
||||
});
|
||||
});
|
||||
@@ -19,6 +19,13 @@ describe("iD.svg.TagClasses", function () {
|
||||
expect(selection.attr('class')).to.equal('tag-highway tag-highway-primary');
|
||||
});
|
||||
|
||||
it('adds tags based on the result of the `tags` accessor', function() {
|
||||
selection
|
||||
.datum(iD.Entity())
|
||||
.call(iD.svg.TagClasses().tags(d3.functor({highway: 'primary'})));
|
||||
expect(selection.attr('class')).to.equal('tag-highway tag-highway-primary');
|
||||
});
|
||||
|
||||
it('removes classes for tags that are no longer present', function() {
|
||||
selection
|
||||
.attr('class', 'tag-highway tag-highway-primary')
|
||||
|
||||
Reference in New Issue
Block a user