mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-13 04:44:50 +02:00
Merge branch 'master' into presets
Conflicts: js/id/util.js
This commit is contained in:
+151
-112
@@ -246,7 +246,6 @@ form.hide {
|
||||
|
||||
button {
|
||||
line-height:20px;
|
||||
position: relative;
|
||||
border:0;
|
||||
color:#222;
|
||||
background: white;
|
||||
@@ -346,16 +345,16 @@ button.save .count {
|
||||
button.save.has-count .count {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 115%;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255,255,255,.5);
|
||||
background: rgba(255, 255, 255, .5);
|
||||
color: #333;
|
||||
padding: 10px;
|
||||
height: 30px;
|
||||
line-height: 12px;
|
||||
border-radius: 4px;
|
||||
margin: auto;
|
||||
margin-left: 8.3333%;
|
||||
}
|
||||
|
||||
button.save.has-count .count::before {
|
||||
@@ -929,50 +928,6 @@ div.typeahead a:first-child {
|
||||
left:0px; right:0px; top:0px; bottom:0px;
|
||||
}
|
||||
|
||||
.commit-modal .user-info {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.commit-modal .commit-info {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.commit-modal .user-info img {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.commit-modal h3 small.count {
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
float: left;
|
||||
height: 12px;
|
||||
min-width: 12px;
|
||||
font-size:12px;
|
||||
line-height: 12px;
|
||||
border-radius:24px;
|
||||
padding:5px;
|
||||
background:#7092ff;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.commit-modal .changeset-list {
|
||||
overflow: auto;
|
||||
border:1px solid #ccc;
|
||||
background:#fff;
|
||||
max-height: 160px;
|
||||
}
|
||||
|
||||
.commit-modal .warning-section .changeset-list {
|
||||
margin-right: 20px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.commit-section.modal-section {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.commit-section.modal-section:last-child { padding-bottom: 20px;}
|
||||
|
||||
.modal-section {
|
||||
padding: 20px;
|
||||
}
|
||||
@@ -1007,6 +962,56 @@ div.typeahead a:first-child {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.loading-modal {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Commit Modal
|
||||
------------------------------------------------------- */
|
||||
|
||||
.commit-modal .user-info {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.commit-modal .commit-info {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.commit-modal .user-info img {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.commit-modal h3 small.count {
|
||||
margin-right: 10px;
|
||||
text-align: center;
|
||||
float: left;
|
||||
height: 12px;
|
||||
min-width: 12px;
|
||||
font-size:12px;
|
||||
line-height: 12px;
|
||||
border-radius:24px;
|
||||
padding:5px;
|
||||
background:#7092ff;
|
||||
color:#fff;
|
||||
}
|
||||
|
||||
.commit-modal .changeset-list {
|
||||
overflow: auto;
|
||||
border:1px solid #ccc;
|
||||
background:#fff;
|
||||
max-height: 160px;
|
||||
}
|
||||
|
||||
.commit-modal .warning-section .changeset-list button {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.commit-section.modal-section {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.commit-section.modal-section:last-child { padding-bottom: 20px;}
|
||||
|
||||
.changeset-list li {
|
||||
border-top:1px solid #ccc;
|
||||
padding:5px 10px;
|
||||
@@ -1029,10 +1034,6 @@ div.typeahead a:first-child {
|
||||
font:normal 12px/20px 'Helvetica Neue', Arial, sans-serif;
|
||||
}
|
||||
|
||||
.loading-modal {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Success
|
||||
------------------------------------------------------- */
|
||||
a.success-action {
|
||||
@@ -1051,7 +1052,8 @@ a.success-action {
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.notice .notice-inner {
|
||||
.notice .zoom-to {
|
||||
width:100%;
|
||||
height: 40px;
|
||||
border-radius: 5px;
|
||||
line-height: 40px;
|
||||
@@ -1060,22 +1062,27 @@ a.success-action {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.notice .notice-inner .zoom-to {
|
||||
width:40px;
|
||||
height:40px;
|
||||
.notice .zoom-to:hover {
|
||||
background: #bde5aa;
|
||||
}
|
||||
|
||||
.notice .zoom-to .icon {
|
||||
margin-top:10px;
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
.icon.zoom-in-invert {
|
||||
background-position: -240px -40px;
|
||||
}
|
||||
|
||||
/* Tooltips
|
||||
------------------------------------------------------- */
|
||||
|
||||
.tooltip {
|
||||
white-space: normal;
|
||||
width: 200px;
|
||||
position: absolute;
|
||||
left: 0; right: 0; margin: auto;
|
||||
z-index: -1000;
|
||||
height: 0;
|
||||
padding: 5px;
|
||||
opacity: 0;
|
||||
display: block;
|
||||
}
|
||||
@@ -1087,98 +1094,130 @@ a.success-action {
|
||||
}
|
||||
|
||||
.tooltip.top {
|
||||
margin-top: -5px;
|
||||
margin-top: -10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip.right {
|
||||
margin-left: 5px;
|
||||
margin-left: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tooltip.bottom {
|
||||
margin-top: 5px;
|
||||
margin-top: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tooltip.left {
|
||||
margin-left: -5px;
|
||||
margin-left: -10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tooltip-inner {
|
||||
text-align: left;
|
||||
width: 200px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
padding: 5px 10px;
|
||||
color: #333;
|
||||
background-color: white;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
padding: 5px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
background-color: white;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.tooltip.top .tooltip-arrow {
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-top-color: white;
|
||||
border-width: 5px 5px 0;
|
||||
bottom: -5px;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-top-color: white;
|
||||
border-width: 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.right .tooltip-arrow {
|
||||
top: 50%;
|
||||
left: 0;
|
||||
margin-top: -5px;
|
||||
border-right-color: white;
|
||||
border-width: 5px 5px 5px 0;
|
||||
top: 50%;
|
||||
left: -5px;
|
||||
margin-top: -5px;
|
||||
border-right-color: white;
|
||||
border-width: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
.tooltip.left .tooltip-arrow {
|
||||
top: 50%;
|
||||
right: 0;
|
||||
margin-top: -5px;
|
||||
border-left-color: white;
|
||||
border-width: 5px 0 5px 5px;
|
||||
top: 50%;
|
||||
right: 5px;
|
||||
margin-top: -5px;
|
||||
border-left-color: white;
|
||||
border-width: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
.tooltip.bottom .tooltip-arrow {
|
||||
top: 0;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-bottom-color: white;
|
||||
border-width: 0 5px 5px;
|
||||
top: -5px;
|
||||
left: 50%;
|
||||
margin-left: -5px;
|
||||
border-bottom-color: white;
|
||||
border-width: 0 5px 5px;
|
||||
}
|
||||
|
||||
.Browse .tooltip {
|
||||
left: -20px !important; }
|
||||
.Browse .tooltip .tooltip-arrow {
|
||||
left: 30px;
|
||||
left: 60px;
|
||||
}
|
||||
|
||||
.tooltip .keyhint-wrap {
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.tooltip .keyhint {
|
||||
float: right;
|
||||
background: #eee;
|
||||
display: block;
|
||||
color: #222;
|
||||
font-size: 10px;
|
||||
padding: 0 4px;
|
||||
background:#aaa;
|
||||
color:#fff;
|
||||
padding: 0px 7px;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
margin-left: 4px;
|
||||
border: 1px solid #CCC;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.tooltip .keyhint .keyhint-label{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tooltip .keyhint::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 2px;
|
||||
height: 10px;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
bottom: -4px;
|
||||
left: -1px;
|
||||
border: 1px solid #CCC;
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.tail {
|
||||
pointer-events:none;
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
max-width: 250px;
|
||||
margin-top: -15px;
|
||||
padding: 5px;
|
||||
-webkit-border-radius: 4px;
|
||||
pointer-events:none;
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
max-width: 250px;
|
||||
margin-top: -15px;
|
||||
padding: 5px;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="4"
|
||||
inkscape:cx="230.7911"
|
||||
inkscape:cy="190.13176"
|
||||
inkscape:cx="332.2911"
|
||||
inkscape:cy="175.13176"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer12"
|
||||
showgrid="false"
|
||||
@@ -733,7 +733,7 @@
|
||||
inkscape:export-xdpi="90"
|
||||
inkscape:export-ydpi="90" />
|
||||
<path
|
||||
style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
style="opacity:1;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.79999375000000006;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m 416.5,3.9999947 c -2.48528,0 -4.5,2.0147207 -4.5,4.5 0,0.7234907 0.19662,1.3943635 0.50001,2.0000053 L 409,13.999995 l 0,1.999999 2.00001,0 3.49999,-3.499997 c 0.60565,0.303377 1.27651,0.499995 2,0.499995 2.48528,0 4.5,-2.014712 4.5,-4.4999973 0,-2.4852793 -2.01472,-4.5 -4.5,-4.5 z m 0,1.9999993 c 1.38071,0 2.5,1.1192914 2.5,2.5000007 0,1.3807157 -1.11929,2.5000003 -2.5,2.5000003 -1.38071,0 -2.5,-1.1192846 -2.5,-2.5000003 0,-1.3807093 1.11929,-2.5000007 2.5,-2.5000007 z"
|
||||
id="path47528"
|
||||
inkscape:connector-curvature="0" />
|
||||
|
||||
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
@@ -75,6 +75,7 @@
|
||||
<script src='js/id/ui/preset.js'></script>
|
||||
|
||||
<script src='js/id/actions.js'></script>
|
||||
<script src="js/id/actions/add_midpoint.js"></script>
|
||||
<script src='js/id/actions/add_node.js'></script>
|
||||
<script src='js/id/actions/add_way.js'></script>
|
||||
<script src='js/id/actions/add_way_node.js'></script>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
iD.actions.AddMidpoint = function(midpoint, node) {
|
||||
return function(graph) {
|
||||
graph = graph.replace(node.move(midpoint.loc));
|
||||
|
||||
midpoint.ways.forEach(function(way) {
|
||||
graph = graph.replace(graph.entity(way.id).addNode(node.id, way.index));
|
||||
});
|
||||
|
||||
return graph;
|
||||
};
|
||||
};
|
||||
@@ -1,25 +1,17 @@
|
||||
iD.behavior.AddWay = function(mode) {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
event = d3.dispatch('startFromNode', 'startFromWay', 'start'),
|
||||
draw;
|
||||
|
||||
function add(datum) {
|
||||
if (datum.type === 'node') {
|
||||
event.startFromNode(datum);
|
||||
} else if (datum.type === 'way') {
|
||||
var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
event.startFromWay(datum, choice.loc, choice.index);
|
||||
} else if (datum.midpoint) {
|
||||
var way = history.graph().entity(datum.way);
|
||||
event.startFromWay(way, datum.loc, datum.index);
|
||||
} else {
|
||||
event.start(map.mouseCoordinates());
|
||||
}
|
||||
}
|
||||
event = d3.dispatch('start', 'startFromWay', 'startFromNode', 'startFromMidpoint'),
|
||||
draw = iD.behavior.Draw(map);
|
||||
|
||||
var addWay = function(surface) {
|
||||
draw.on('click', event.start)
|
||||
.on('clickWay', event.startFromWay)
|
||||
.on('clickNode', event.startFromNode)
|
||||
.on('clickMidpoint', event.startFromMidpoint)
|
||||
.on('cancel', addWay.cancel)
|
||||
.on('finish', addWay.cancel);
|
||||
|
||||
map.fastEnable(false)
|
||||
.minzoom(16)
|
||||
.dblclickEnable(false);
|
||||
@@ -43,10 +35,5 @@ iD.behavior.AddWay = function(mode) {
|
||||
controller.exit();
|
||||
};
|
||||
|
||||
draw = iD.behavior.Draw()
|
||||
.on('add', add)
|
||||
.on('cancel', addWay.cancel)
|
||||
.on('finish', addWay.cancel);
|
||||
|
||||
return d3.rebind(addWay, event, 'on');
|
||||
};
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
iD.behavior.DragMidpoint = function(mode) {
|
||||
var history = mode.history,
|
||||
projection = mode.map.projection,
|
||||
behavior = iD.behavior.drag()
|
||||
projection = mode.map.projection;
|
||||
|
||||
var behavior = iD.behavior.drag()
|
||||
.delegate(".midpoint")
|
||||
.origin(function(d) {
|
||||
return projection(d.loc);
|
||||
})
|
||||
.on('start', function(d) {
|
||||
var w, nds;
|
||||
d.node = iD.Node({loc: d.loc});
|
||||
var args = [iD.actions.AddNode(d.node)];
|
||||
for (var i = 0; i < d.ways.length; i++) {
|
||||
w = d.ways[i], nds = w.nodes;
|
||||
for (var j = 0; j < nds.length; j++) {
|
||||
if ((nds[j] === d.nodes[0] && nds[j + 1] === d.nodes[1]) ||
|
||||
(nds[j] === d.nodes[1] && nds[j + 1] === d.nodes[0])) {
|
||||
args.push(iD.actions.AddWayNode(w.id, d.node.id, j + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
history.perform.apply(history, args);
|
||||
var node = d3.selectAll('.node.vertex')
|
||||
.filter(function(data) { return data.id === d.node.id; });
|
||||
behavior.target(node.node(), node.datum());
|
||||
var node = iD.Node();
|
||||
|
||||
history.perform(iD.actions.AddMidpoint(d, node));
|
||||
|
||||
var vertex = d3.selectAll('.vertex')
|
||||
.filter(function(data) { return data.id === node.id; });
|
||||
|
||||
behavior.target(vertex.node(), vertex.datum());
|
||||
})
|
||||
.on('move', function(d) {
|
||||
d3.event.sourceEvent.stopPropagation();
|
||||
@@ -33,7 +25,8 @@ iD.behavior.DragMidpoint = function(mode) {
|
||||
.on('end', function() {
|
||||
history.replace(
|
||||
iD.actions.Noop(),
|
||||
'added a node to a way');
|
||||
'Added a node to a way.');
|
||||
});
|
||||
|
||||
return behavior;
|
||||
};
|
||||
|
||||
+16
-3
@@ -1,5 +1,5 @@
|
||||
iD.behavior.Draw = function () {
|
||||
var event = d3.dispatch('move', 'add', 'undo', 'cancel', 'finish'),
|
||||
iD.behavior.Draw = function(map) {
|
||||
var event = d3.dispatch('move', 'click', 'clickWay', 'clickNode', 'clickMidpoint', 'undo', 'cancel', 'finish'),
|
||||
keybinding = d3.keybinding('draw'),
|
||||
down, surface, hover;
|
||||
|
||||
@@ -26,7 +26,20 @@ iD.behavior.Draw = function () {
|
||||
}
|
||||
|
||||
function click() {
|
||||
event.add(datum());
|
||||
var d = datum();
|
||||
if (d.type === 'way') {
|
||||
var choice = iD.geo.chooseIndex(d, d3.mouse(map.surface.node()), map);
|
||||
event.clickWay(d, choice.loc, choice.index);
|
||||
|
||||
} else if (d.type === 'node') {
|
||||
event.clickNode(d);
|
||||
|
||||
} else if (d.type === 'midpoint') {
|
||||
event.clickMidpoint(d);
|
||||
|
||||
} else {
|
||||
event.click(map.mouseCoordinates());
|
||||
}
|
||||
}
|
||||
|
||||
function keydown() {
|
||||
|
||||
+44
-41
@@ -1,11 +1,11 @@
|
||||
iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
iD.behavior.DrawWay = function(wayId, index, mode, baseGraph) {
|
||||
var map = mode.map,
|
||||
history = mode.history,
|
||||
controller = mode.controller,
|
||||
event = d3.dispatch('add', 'addHead', 'addTail', 'addNode', 'addWay'),
|
||||
way = mode.history.graph().entity(wayId),
|
||||
finished = false,
|
||||
draw;
|
||||
annotation = 'Added to a way.',
|
||||
draw = iD.behavior.Draw(map);
|
||||
|
||||
var node = iD.Node({loc: map.mouseCoordinates()}),
|
||||
nodeId = node.id;
|
||||
@@ -17,7 +17,7 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
function move(datum) {
|
||||
var loc = map.mouseCoordinates();
|
||||
|
||||
if (datum.type === 'node' || datum.midpoint) {
|
||||
if (datum.type === 'node' || datum.type === 'midpoint') {
|
||||
loc = datum.loc;
|
||||
} else if (datum.type === 'way') {
|
||||
loc = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map).loc;
|
||||
@@ -26,29 +26,20 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
history.replace(iD.actions.MoveNode(nodeId, loc));
|
||||
}
|
||||
|
||||
function add(datum) {
|
||||
if (datum.id === headId) {
|
||||
event.addHead(datum);
|
||||
} else if (datum.id === tailId) {
|
||||
event.addTail(datum);
|
||||
} else if (datum.type === 'node' && datum.id !== nodeId) {
|
||||
event.addNode(datum);
|
||||
} else if (datum.type === 'way') {
|
||||
var choice = iD.geo.chooseIndex(datum, d3.mouse(map.surface.node()), map);
|
||||
event.addWay(datum, choice.loc, choice.index);
|
||||
} else if (datum.midpoint) {
|
||||
var way = history.graph().entity(datum.way);
|
||||
event.addWay(way, datum.loc, datum.index);
|
||||
} else {
|
||||
event.add(map.mouseCoordinates());
|
||||
}
|
||||
}
|
||||
|
||||
function undone() {
|
||||
controller.enter(iD.modes.Browse());
|
||||
}
|
||||
|
||||
var drawWay = function(surface) {
|
||||
draw.on('move', move)
|
||||
.on('click', drawWay.add)
|
||||
.on('clickWay', drawWay.addWay)
|
||||
.on('clickNode', drawWay.addNode)
|
||||
.on('clickMidpoint', drawWay.addMidpoint)
|
||||
.on('undo', history.undo)
|
||||
.on('cancel', drawWay.cancel)
|
||||
.on('finish', drawWay.finish);
|
||||
|
||||
map.fastEnable(false)
|
||||
.minzoom(16)
|
||||
.dblclickEnable(false);
|
||||
@@ -80,6 +71,12 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
history.on('undone.draw', null);
|
||||
};
|
||||
|
||||
drawWay.annotation = function(_) {
|
||||
if (!arguments.length) return annotation;
|
||||
annotation = _;
|
||||
return drawWay;
|
||||
};
|
||||
|
||||
function ReplaceTemporaryNode(newNode) {
|
||||
return function(graph) {
|
||||
return graph
|
||||
@@ -88,10 +85,13 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
}
|
||||
}
|
||||
|
||||
// Connect the way to an existing node and continue drawing.
|
||||
drawWay.addNode = function(node, annotation) {
|
||||
history.perform(
|
||||
ReplaceTemporaryNode(node),
|
||||
// Accept the current position of the temporary node and continue drawing.
|
||||
drawWay.add = function(loc) {
|
||||
var newNode = iD.Node({loc: loc});
|
||||
|
||||
history.replace(
|
||||
iD.actions.AddNode(newNode),
|
||||
ReplaceTemporaryNode(newNode),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
@@ -99,7 +99,7 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
};
|
||||
|
||||
// Connect the way to an existing way.
|
||||
drawWay.addWay = function(way, loc, wayIndex, annotation) {
|
||||
drawWay.addWay = function(way, loc, wayIndex) {
|
||||
var newNode = iD.Node({loc: loc});
|
||||
|
||||
history.perform(
|
||||
@@ -112,13 +112,23 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
controller.enter(mode);
|
||||
};
|
||||
|
||||
// Accept the current position of the temporary node and continue drawing.
|
||||
drawWay.add = function(loc, annotation) {
|
||||
var newNode = iD.Node({loc: loc});
|
||||
// Connect the way to an existing node and continue drawing.
|
||||
drawWay.addNode = function(node) {
|
||||
history.perform(
|
||||
ReplaceTemporaryNode(node),
|
||||
annotation);
|
||||
|
||||
history.replace(
|
||||
iD.actions.AddNode(newNode),
|
||||
ReplaceTemporaryNode(newNode),
|
||||
finished = true;
|
||||
controller.enter(mode);
|
||||
};
|
||||
|
||||
// Add a midpoint, connect the way to it, and continue drawing.
|
||||
drawWay.addMidpoint = function(midpoint) {
|
||||
var node = iD.Node();
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
ReplaceTemporaryNode(node),
|
||||
annotation);
|
||||
|
||||
finished = true;
|
||||
@@ -143,18 +153,11 @@ iD.behavior.DrawWay = function(wayId, headId, tailId, index, mode, baseGraph) {
|
||||
drawWay.cancel = function() {
|
||||
history.perform(
|
||||
d3.functor(baseGraph),
|
||||
'cancelled drawing');
|
||||
'Cancelled drawing.');
|
||||
|
||||
finished = true;
|
||||
controller.enter(iD.modes.Browse());
|
||||
};
|
||||
|
||||
draw = iD.behavior.Draw()
|
||||
.on('move', move)
|
||||
.on('add', add)
|
||||
.on('undo', history.undo)
|
||||
.on('cancel', drawWay.cancel)
|
||||
.on('finish', drawWay.finish);
|
||||
|
||||
return d3.rebind(drawWay, event, 'on');
|
||||
};
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ iD.Connection = function() {
|
||||
}
|
||||
}
|
||||
|
||||
return iD.Graph(entities);
|
||||
return entities;
|
||||
}
|
||||
|
||||
function authenticated() {
|
||||
|
||||
+156
-69
@@ -1,19 +1,30 @@
|
||||
iD.Graph = function(entities, mutable) {
|
||||
if (!(this instanceof iD.Graph)) return new iD.Graph(entities, mutable);
|
||||
iD.Graph = function(other, mutable) {
|
||||
if (!(this instanceof iD.Graph)) return new iD.Graph(other, mutable);
|
||||
|
||||
if (other instanceof iD.Graph) {
|
||||
var base = other.base();
|
||||
this.entities = _.assign(Object.create(base.entities), other.entities);
|
||||
this._parentWays = _.assign(Object.create(base.parentWays), other._parentWays);
|
||||
this._parentRels = _.assign(Object.create(base.parentRels), other._parentRels);
|
||||
this.inherited = true;
|
||||
|
||||
if (_.isArray(entities)) {
|
||||
this.entities = {};
|
||||
for (var i = 0; i < entities.length; i++) {
|
||||
this.entities[entities[i].id] = entities[i];
|
||||
}
|
||||
} else {
|
||||
this.entities = entities || {};
|
||||
if (_.isArray(other)) {
|
||||
var entities = {};
|
||||
for (var i = 0; i < other.length; i++) {
|
||||
entities[other[i].id] = other[i];
|
||||
}
|
||||
other = entities;
|
||||
}
|
||||
this.entities = Object.create({});
|
||||
this._parentWays = Object.create({});
|
||||
this._parentRels = Object.create({});
|
||||
this.rebase(other || {});
|
||||
}
|
||||
|
||||
this.transients = {};
|
||||
this._parentWays = {};
|
||||
this._parentRels = {};
|
||||
this._childNodes = {};
|
||||
this.getEntity = _.bind(this.entity, this);
|
||||
|
||||
if (!mutable) {
|
||||
this.freeze();
|
||||
@@ -38,51 +49,21 @@ iD.Graph.prototype = {
|
||||
},
|
||||
|
||||
parentWays: function(entity) {
|
||||
var ent, id, parents;
|
||||
|
||||
if (!this._parentWays.calculated) {
|
||||
for (var i in this.entities) {
|
||||
ent = this.entities[i];
|
||||
if (ent && ent.type === 'way') {
|
||||
for (var j = 0; j < ent.nodes.length; j++) {
|
||||
id = ent.nodes[j];
|
||||
parents = this._parentWays[id] = this._parentWays[id] || [];
|
||||
if (parents.indexOf(ent) < 0) {
|
||||
parents.push(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._parentWays.calculated = true;
|
||||
}
|
||||
|
||||
return this._parentWays[entity.id] || [];
|
||||
return _.map(this._parentWays[entity.id], this.getEntity);
|
||||
},
|
||||
|
||||
isPoi: function(entity) {
|
||||
return this.parentWays(entity).length === 0;
|
||||
var parentWays = this._parentWays[entity.id];
|
||||
return !parentWays || parentWays.length === 0;
|
||||
},
|
||||
|
||||
isShared: function(entity) {
|
||||
var parentWays = this._parentWays[entity.id];
|
||||
return parentWays && parentWays.length > 1;
|
||||
},
|
||||
|
||||
parentRelations: function(entity) {
|
||||
var ent, id, parents;
|
||||
|
||||
if (!this._parentRels.calculated) {
|
||||
for (var i in this.entities) {
|
||||
ent = this.entities[i];
|
||||
if (ent && ent.type === 'relation') {
|
||||
for (var j = 0; j < ent.members.length; j++) {
|
||||
id = ent.members[j].id;
|
||||
parents = this._parentRels[id] = this._parentRels[id] || [];
|
||||
if (parents.indexOf(ent) < 0) {
|
||||
parents.push(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this._parentRels.calculated = true;
|
||||
}
|
||||
|
||||
return this._parentRels[entity.id] || [];
|
||||
return _.map(this._parentRels[entity.id], this.getEntity);
|
||||
},
|
||||
|
||||
childNodes: function(entity) {
|
||||
@@ -97,30 +78,132 @@ iD.Graph.prototype = {
|
||||
return (this._childNodes[entity.id] = nodes);
|
||||
},
|
||||
|
||||
merge: function(graph) {
|
||||
return this.update(function () {
|
||||
_.defaults(this.entities, graph.entities);
|
||||
});
|
||||
base: function() {
|
||||
return {
|
||||
'entities': iD.util.getPrototypeOf(this.entities),
|
||||
'parentWays': iD.util.getPrototypeOf(this._parentWays),
|
||||
'parentRels': iD.util.getPrototypeOf(this._parentRels)
|
||||
};
|
||||
},
|
||||
|
||||
// Unlike other graph methods, rebase mutates in place. This is because it
|
||||
// is used only during the history operation that merges newly downloaded
|
||||
// data into each state. To external consumers, it should appear as if the
|
||||
// graph always contained the newly downloaded data.
|
||||
rebase: function(entities) {
|
||||
var base = this.base(),
|
||||
i, k, child, id, keys;
|
||||
// Merging of data only needed if graph is the base graph
|
||||
if (!this.inherited) {
|
||||
for (i in entities) {
|
||||
if (!base.entities[i]) {
|
||||
base.entities[i] = entities[i];
|
||||
this._updateCalculated(undefined, entities[i],
|
||||
base.parentWays, base.parentRels);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys = Object.keys(this._parentWays);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
child = keys[i];
|
||||
if (base.parentWays[child]) {
|
||||
for (k = 0; k < base.parentWays[child].length; k++) {
|
||||
id = base.parentWays[child][k];
|
||||
if (this.entity(id) && !_.contains(this._parentWays[child], id)) {
|
||||
this._parentWays[child].push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys = Object.keys(this._parentRels);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
child = keys[i];
|
||||
if (base.parentRels[child]) {
|
||||
for (k = 0; k < base.parentRels[child].length; k++) {
|
||||
id = base.parentRels[child][k];
|
||||
if (this.entity(id) && !_.contains(this._parentRels[child], id)) {
|
||||
this._parentRels[child].push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Updates calculated properties (parentWays, parentRels) for the specified change
|
||||
_updateCalculated: function(oldentity, entity, parentWays, parentRels) {
|
||||
|
||||
parentWays = parentWays || this._parentWays;
|
||||
parentRels = parentRels || this._parentRels;
|
||||
|
||||
var type = entity && entity.type || oldentity && oldentity.type,
|
||||
removed, added, ways, rels, i;
|
||||
|
||||
|
||||
if (type === 'way') {
|
||||
|
||||
// Update parentWays
|
||||
if (oldentity && entity) {
|
||||
removed = _.difference(oldentity.nodes, entity.nodes);
|
||||
added = _.difference(entity.nodes, oldentity.nodes);
|
||||
} else if (oldentity) {
|
||||
removed = oldentity.nodes;
|
||||
added = [];
|
||||
} else if (entity) {
|
||||
removed = [];
|
||||
added = entity.nodes;
|
||||
}
|
||||
for (i = 0; i < removed.length; i++) {
|
||||
parentWays[removed[i]] = _.without(parentWays[removed[i]], oldentity.id);
|
||||
}
|
||||
for (i = 0; i < added.length; i++) {
|
||||
ways = _.without(parentWays[added[i]], entity.id);
|
||||
ways.push(entity.id);
|
||||
parentWays[added[i]] = ways;
|
||||
}
|
||||
} else if (type === 'node') {
|
||||
|
||||
} else if (type === 'relation') {
|
||||
|
||||
// Update parentRels
|
||||
if (oldentity && entity) {
|
||||
removed = _.difference(oldentity.members, entity.members);
|
||||
added = _.difference(entity.members, oldentity);
|
||||
} else if (oldentity) {
|
||||
removed = oldentity.members;
|
||||
added = [];
|
||||
} else if (entity) {
|
||||
removed = [];
|
||||
added = entity.members;
|
||||
}
|
||||
for (i = 0; i < removed.length; i++) {
|
||||
parentRels[removed[i].id] = _.without(parentRels[removed[i].id], oldentity.id);
|
||||
}
|
||||
for (i = 0; i < added.length; i++) {
|
||||
rels = _.without(parentRels[added[i].id], entity.id);
|
||||
rels.push(entity.id);
|
||||
parentRels[added[i].id] = rels;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
replace: function(entity) {
|
||||
return this.update(function () {
|
||||
this._updateCalculated(this.entities[entity.id], entity);
|
||||
this.entities[entity.id] = entity;
|
||||
});
|
||||
},
|
||||
|
||||
remove: function(entity) {
|
||||
return this.update(function () {
|
||||
if (entity.created()) {
|
||||
delete this.entities[entity.id];
|
||||
} else {
|
||||
this.entities[entity.id] = undefined;
|
||||
}
|
||||
this._updateCalculated(entity, undefined);
|
||||
this.entities[entity.id] = undefined;
|
||||
});
|
||||
},
|
||||
|
||||
update: function() {
|
||||
var graph = this.frozen ? iD.Graph(_.clone(this.entities), true) : this;
|
||||
var graph = this.frozen ? iD.Graph(this, true) : this;
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
arguments[i].call(graph, graph);
|
||||
@@ -133,7 +216,6 @@ iD.Graph.prototype = {
|
||||
this.frozen = true;
|
||||
|
||||
if (iD.debug) {
|
||||
Object.freeze(this);
|
||||
Object.freeze(this.entities);
|
||||
}
|
||||
|
||||
@@ -153,9 +235,12 @@ iD.Graph.prototype = {
|
||||
},
|
||||
|
||||
difference: function (graph) {
|
||||
var result = [], entity, oldentity, id;
|
||||
var result = [],
|
||||
keys = Object.keys(this.entities),
|
||||
entity, oldentity, id, i;
|
||||
|
||||
for (id in this.entities) {
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
id = keys[i];
|
||||
entity = this.entities[id];
|
||||
oldentity = graph.entities[id];
|
||||
if (entity !== oldentity) {
|
||||
@@ -177,7 +262,9 @@ iD.Graph.prototype = {
|
||||
}
|
||||
}
|
||||
|
||||
for (id in graph.entities) {
|
||||
keys = Object.keys(graph.entities);
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
id = keys[i];
|
||||
entity = graph.entities[id];
|
||||
if (entity && !this.entities.hasOwnProperty(id)) {
|
||||
result.push(id);
|
||||
@@ -189,25 +276,25 @@ iD.Graph.prototype = {
|
||||
},
|
||||
|
||||
modified: function() {
|
||||
var result = [];
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && entity.modified()) result.push(id);
|
||||
if (entity && base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
created: function() {
|
||||
var result = [];
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (entity && entity.created()) result.push(id);
|
||||
if (entity && !base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
deleted: function() {
|
||||
var result = [];
|
||||
var result = [], base = this.base().entities;
|
||||
_.each(this.entities, function(entity, id) {
|
||||
if (!entity) result.push(id);
|
||||
if (!entity && base[id]) result.push(id);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ iD.History = function() {
|
||||
return stack[index].graph;
|
||||
},
|
||||
|
||||
merge: function (graph) {
|
||||
merge: function (entities) {
|
||||
for (var i = 0; i < stack.length; i++) {
|
||||
stack[i].graph = stack[i].graph.merge(graph);
|
||||
stack[i].graph.rebase(entities);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+4
-4
@@ -22,7 +22,7 @@ window.iD = function(container) {
|
||||
}
|
||||
|
||||
function hintprefix(x, y) {
|
||||
return '<span class="keyhint">' + x + '</span> ' + y;
|
||||
return '<span>' + y + '</span>' + '<div class="keyhint-wrap"><span class="keyhint"> ' + x + '</span></div>';
|
||||
}
|
||||
|
||||
var m = container.append('div')
|
||||
@@ -44,10 +44,10 @@ window.iD = function(container) {
|
||||
.enter().append('button')
|
||||
.attr('tabindex', -1)
|
||||
.attr('class', function (mode) { return mode.title + ' add-button col3'; })
|
||||
.call(bootstrap.tooltip().placement('bottom').html(true))
|
||||
.attr('data-original-title', function (mode) {
|
||||
return hintprefix(mode.key, mode.description);
|
||||
})
|
||||
.call(bootstrap.tooltip().placement('bottom').html(true))
|
||||
.on('click.editor', function (mode) { controller.enter(mode); });
|
||||
|
||||
function disableTooHigh() {
|
||||
@@ -207,12 +207,12 @@ window.iD = function(container) {
|
||||
|
||||
limiter.select('#undo')
|
||||
.property('disabled', !undo)
|
||||
.attr('data-original-title', hintprefix('⌘Z', undo))
|
||||
.attr('data-original-title', hintprefix('⌘ + Z', undo))
|
||||
.call(refreshTooltip);
|
||||
|
||||
limiter.select('#redo')
|
||||
.property('disabled', !redo)
|
||||
.attr('data-original-title', hintprefix('⌘⇧Z', redo))
|
||||
.attr('data-original-title', hintprefix('⌘ + ⇧ + Z', redo))
|
||||
.call(refreshTooltip);
|
||||
});
|
||||
|
||||
|
||||
+21
-6
@@ -15,11 +15,13 @@ iD.modes.AddArea = function() {
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
function startFromNode(node) {
|
||||
function start(loc) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(way.id, node.id));
|
||||
@@ -42,13 +44,25 @@ iD.modes.AddArea = function() {
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
function start(loc) {
|
||||
function startFromNode(node) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawArea(way.id, graph));
|
||||
}
|
||||
|
||||
function startFromMidpoint(midpoint) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(way.id, node.id));
|
||||
@@ -57,9 +71,10 @@ iD.modes.AddArea = function() {
|
||||
}
|
||||
|
||||
behavior = iD.behavior.AddWay(mode)
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('start', start)
|
||||
.on('startFromWay', startFromWay)
|
||||
.on('start', start);
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('startFromMidpoint', startFromMidpoint);
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail('Click on the map to start drawing an area, like a park, lake, or building.', true);
|
||||
|
||||
+33
-19
@@ -15,6 +15,33 @@ iD.modes.AddLine = function() {
|
||||
history = mode.history,
|
||||
controller = mode.controller;
|
||||
|
||||
function start(loc) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(other.id, node.id, index));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function startFromNode(node) {
|
||||
var graph = history.graph(),
|
||||
parent = graph.parentWays(node)[0],
|
||||
@@ -37,27 +64,13 @@ iD.modes.AddLine = function() {
|
||||
}
|
||||
}
|
||||
|
||||
function startFromWay(other, loc, index) {
|
||||
function startFromMidpoint(midpoint) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
node = iD.Node(),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id),
|
||||
iD.actions.AddWayNode(other.id, node.id, index));
|
||||
|
||||
controller.enter(iD.modes.DrawLine(way.id, 'forward', graph));
|
||||
}
|
||||
|
||||
function start(loc) {
|
||||
var graph = history.graph(),
|
||||
node = iD.Node({loc: loc}),
|
||||
way = iD.Way({tags: defaultTags});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddMidpoint(midpoint, node),
|
||||
iD.actions.AddWay(way),
|
||||
iD.actions.AddWayNode(way.id, node.id));
|
||||
|
||||
@@ -65,9 +78,10 @@ iD.modes.AddLine = function() {
|
||||
}
|
||||
|
||||
behavior = iD.behavior.AddWay(mode)
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('start', start)
|
||||
.on('startFromWay', startFromWay)
|
||||
.on('start', start);
|
||||
.on('startFromNode', startFromNode)
|
||||
.on('startFromMidpoint', startFromMidpoint);
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail('Click on the map to start drawing an road, path, or route.', true);
|
||||
|
||||
@@ -16,22 +16,33 @@ iD.modes.AddPoint = function() {
|
||||
|
||||
map.tail('Click on the map to add a point.', true);
|
||||
|
||||
function add() {
|
||||
var node = iD.Node({loc: map.mouseCoordinates()});
|
||||
function add(loc) {
|
||||
var node = iD.Node({loc: loc});
|
||||
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
'added a point');
|
||||
'Added a point.');
|
||||
|
||||
controller.enter(iD.modes.Select(node, true));
|
||||
}
|
||||
|
||||
function addWay(way, loc, index) {
|
||||
add(loc);
|
||||
}
|
||||
|
||||
function addNode(node) {
|
||||
add(node.loc);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
controller.exit();
|
||||
}
|
||||
|
||||
behavior = iD.behavior.Draw()
|
||||
.on('add', add)
|
||||
behavior = iD.behavior.Draw(map)
|
||||
.on('click', add)
|
||||
.on('clickWay', addWay)
|
||||
.on('clickNode', addNode)
|
||||
.on('clickMidpoint', addNode)
|
||||
.on('cancel', cancel)
|
||||
.on('finish', cancel)
|
||||
(surface);
|
||||
|
||||
@@ -3,7 +3,7 @@ iD.modes.Browse = function() {
|
||||
button: 'browse',
|
||||
id: 'browse',
|
||||
title: 'Browse',
|
||||
description: 'Pan and zoom the map',
|
||||
description: 'Pan and zoom the map.',
|
||||
key: 'b'
|
||||
};
|
||||
|
||||
|
||||
+11
-23
@@ -8,33 +8,21 @@ iD.modes.DrawArea = function(wayId, baseGraph) {
|
||||
|
||||
mode.enter = function() {
|
||||
var way = mode.history.graph().entity(wayId),
|
||||
index = -1,
|
||||
headId = way.nodes[way.nodes.length - 2],
|
||||
tailId = way.first(),
|
||||
annotation = way.isDegenerate() ? 'started an area' : 'continued an area';
|
||||
tailId = way.first();
|
||||
|
||||
function addHeadTail() {
|
||||
behavior.finish();
|
||||
}
|
||||
behavior = iD.behavior.DrawWay(wayId, -1, mode, baseGraph)
|
||||
.annotation(way.isDegenerate() ? 'started an area' : 'continued an area');
|
||||
|
||||
function addNode(node) {
|
||||
behavior.addNode(node, annotation);
|
||||
}
|
||||
var addNode = behavior.addNode;
|
||||
|
||||
function addWay(way, loc, index) {
|
||||
behavior.addWay(way, loc, index, annotation);
|
||||
}
|
||||
|
||||
function add(loc) {
|
||||
behavior.add(loc, annotation);
|
||||
}
|
||||
|
||||
behavior = iD.behavior.DrawWay(wayId, headId, tailId, index, mode, baseGraph)
|
||||
.on('addHead', addHeadTail)
|
||||
.on('addTail', addHeadTail)
|
||||
.on('addNode', addNode)
|
||||
.on('addWay', addWay)
|
||||
.on('add', add);
|
||||
behavior.addNode = function(node) {
|
||||
if (node.id === headId || node.id === tailId) {
|
||||
behavior.finish();
|
||||
} else {
|
||||
addNode(node);
|
||||
}
|
||||
};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail('Click to add points to your area. Click the first point to finish the area.', true);
|
||||
|
||||
+10
-31
@@ -9,41 +9,20 @@ iD.modes.DrawLine = function(wayId, direction, baseGraph) {
|
||||
mode.enter = function() {
|
||||
var way = mode.history.graph().entity(wayId),
|
||||
index = (direction === 'forward') ? undefined : 0,
|
||||
headId = (direction === 'forward') ? way.last() : way.first(),
|
||||
tailId = (direction === 'forward') ? way.first() : way.last(),
|
||||
annotation = way.isDegenerate() ? 'started a line' : 'continued a line';
|
||||
headId = (direction === 'forward') ? way.last() : way.first();
|
||||
|
||||
function addHead() {
|
||||
behavior.finish();
|
||||
}
|
||||
behavior = iD.behavior.DrawWay(wayId, index, mode, baseGraph)
|
||||
.annotation(way.isDegenerate() ? 'Started a line.' : 'Continued a line.');
|
||||
|
||||
function addTail(node) {
|
||||
// connect the way in a loop
|
||||
if (way.nodes.length > 2) {
|
||||
behavior.addNode(node, annotation);
|
||||
var addNode = behavior.addNode;
|
||||
|
||||
behavior.addNode = function(node) {
|
||||
if (node.id === headId) {
|
||||
behavior.finish();
|
||||
} else {
|
||||
behavior.cancel();
|
||||
addNode(node);
|
||||
}
|
||||
}
|
||||
|
||||
function addNode(node) {
|
||||
behavior.addNode(node, annotation);
|
||||
}
|
||||
|
||||
function addWay(way, loc, index) {
|
||||
behavior.addWay(way, loc, index, annotation);
|
||||
}
|
||||
|
||||
function add(loc) {
|
||||
behavior.add(loc, annotation);
|
||||
}
|
||||
|
||||
behavior = iD.behavior.DrawWay(wayId, headId, tailId, index, mode, baseGraph)
|
||||
.on('addHead', addHead)
|
||||
.on('addTail', addTail)
|
||||
.on('addNode', addNode)
|
||||
.on('addWay', addWay)
|
||||
.on('add', add);
|
||||
};
|
||||
|
||||
mode.map.surface.call(behavior);
|
||||
mode.map.tail('Click to add more points to the line. ' +
|
||||
|
||||
@@ -18,7 +18,7 @@ iD.modes.MoveWay = function(wayId) {
|
||||
|
||||
history.perform(
|
||||
iD.actions.Noop(),
|
||||
'moved a way');
|
||||
'Moved a way.');
|
||||
|
||||
function move() {
|
||||
var p = d3.mouse(selection.node()),
|
||||
@@ -29,7 +29,7 @@ iD.modes.MoveWay = function(wayId) {
|
||||
|
||||
history.replace(
|
||||
iD.actions.MoveWay(wayId, delta, projection),
|
||||
'moved a way');
|
||||
'Moved a way.');
|
||||
}
|
||||
|
||||
function finish() {
|
||||
|
||||
@@ -14,7 +14,7 @@ iD.modes.Select = function(entity, initial) {
|
||||
if (!_.isEqual(entity.tags, tags)) {
|
||||
mode.history.perform(
|
||||
iD.actions.ChangeEntityTags(d.id, tags),
|
||||
'changed tags');
|
||||
'Changed tags.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ iD.modes.Select = function(entity, initial) {
|
||||
history.perform(
|
||||
iD.actions.AddNode(node),
|
||||
iD.actions.AddWayNode(datum.id, node.id, choice.index),
|
||||
'added a point to a road');
|
||||
'Added a point to a road.');
|
||||
|
||||
d3.event.preventDefault();
|
||||
d3.event.stopPropagation();
|
||||
|
||||
@@ -10,12 +10,12 @@ iD.operations.Circularize = function(entityId, mode) {
|
||||
if (geometry === 'line') {
|
||||
history.perform(
|
||||
action,
|
||||
'made a line circular');
|
||||
'Made a line circular.');
|
||||
|
||||
} else if (geometry === 'area') {
|
||||
history.perform(
|
||||
action,
|
||||
'made an area circular');
|
||||
'Made an area circular.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,22 +9,22 @@ iD.operations.Delete = function(entityId, mode) {
|
||||
if (geometry === 'vertex') {
|
||||
history.perform(
|
||||
iD.actions.DeleteNode(entityId),
|
||||
'deleted a vertex');
|
||||
'Deleted a vertex.');
|
||||
|
||||
} else if (geometry === 'point') {
|
||||
history.perform(
|
||||
iD.actions.DeleteNode(entityId),
|
||||
'deleted a point');
|
||||
'Deleted a point.');
|
||||
|
||||
} else if (geometry === 'line') {
|
||||
history.perform(
|
||||
iD.actions.DeleteWay(entityId),
|
||||
'deleted a line');
|
||||
'Deleted a line.');
|
||||
|
||||
} else if (geometry === 'area') {
|
||||
history.perform(
|
||||
iD.actions.DeleteWay(entityId),
|
||||
'deleted an area');
|
||||
'Deleted an area.');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -41,7 +41,7 @@ iD.operations.Delete = function(entityId, mode) {
|
||||
operation.id = "delete";
|
||||
operation.key = "⌫";
|
||||
operation.title = "Delete";
|
||||
operation.description = "Remove this from the map";
|
||||
operation.description = "Remove this from the map.";
|
||||
|
||||
return operation;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ iD.operations.Reverse = function(entityId, mode) {
|
||||
var operation = function() {
|
||||
history.perform(
|
||||
iD.actions.ReverseWay(entityId),
|
||||
'reversed a line');
|
||||
'Reversed a line.');
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
@@ -20,7 +20,7 @@ iD.operations.Reverse = function(entityId, mode) {
|
||||
operation.id = "reverse";
|
||||
operation.key = "V";
|
||||
operation.title = "Reverse";
|
||||
operation.description = "Make this way go in the opposite direction";
|
||||
operation.description = "Make this way go in the opposite direction.";
|
||||
|
||||
return operation;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ iD.operations.Split = function(entityId, mode) {
|
||||
action = iD.actions.SplitWay(entityId);
|
||||
|
||||
var operation = function() {
|
||||
history.perform(action, 'split a way');
|
||||
history.perform(action, 'Split a way.');
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
@@ -20,7 +20,7 @@ iD.operations.Split = function(entityId, mode) {
|
||||
operation.id = "split";
|
||||
operation.key = "X";
|
||||
operation.title = "Split";
|
||||
operation.description = "Split this into two ways at this point";
|
||||
operation.description = "Split this into two ways at this point.";
|
||||
|
||||
return operation;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@ iD.operations.Unjoin = function(entityId, mode) {
|
||||
action = iD.actions.UnjoinNode(entityId);
|
||||
|
||||
var operation = function() {
|
||||
history.perform(action, 'unjoined lines');
|
||||
history.perform(action, 'Unjoined lines.');
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
@@ -20,7 +20,7 @@ iD.operations.Unjoin = function(entityId, mode) {
|
||||
operation.id = "unjoin";
|
||||
operation.key = "⇧-J";
|
||||
operation.title = "Unjoin";
|
||||
operation.description = "Disconnect these ways from each other";
|
||||
operation.description = "Disconnect these ways from each other.";
|
||||
|
||||
return operation;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
iD.Background = function() {
|
||||
|
||||
var deviceRatio = (window.devicePixelRatio &&
|
||||
window.devicePixelRatio === 2) ? 0.5 : 1;
|
||||
// tileSize = (deviceRatio === 0.5) ? [128,128] : [256,256];
|
||||
tileSize = [256, 256];
|
||||
|
||||
var tile = d3.geo.tile(),
|
||||
projection,
|
||||
cache = {},
|
||||
@@ -13,7 +19,12 @@ iD.Background = function() {
|
||||
'-o-transform-origin:0 0;' +
|
||||
'-webkit-user-select: none;' +
|
||||
'-webkit-user-drag: none;' +
|
||||
'-moz-user-drag: none;';
|
||||
'-moz-user-drag: none;' +
|
||||
'opacity:0;';
|
||||
|
||||
function tileSizeAtZoom(d, z) {
|
||||
return Math.ceil(tileSize[0] * Math.pow(2, z - d[2])) / tileSize[0];
|
||||
}
|
||||
|
||||
function atZoom(t, distance) {
|
||||
var power = Math.pow(2, distance);
|
||||
@@ -21,108 +32,101 @@ iD.Background = function() {
|
||||
Math.floor(t[0] * power),
|
||||
Math.floor(t[1] * power),
|
||||
t[2] + distance];
|
||||
az.push(source(az));
|
||||
return az;
|
||||
}
|
||||
|
||||
function upZoom(t, distance) {
|
||||
var az = atZoom(t, distance), tiles = [];
|
||||
for (var x = 0; x < 2; x++) {
|
||||
for (var y = 0; y < 2; y++) {
|
||||
var up = [az[0] + x, az[1] + y, az[2]];
|
||||
up.push(source(up));
|
||||
tiles.push(up);
|
||||
}
|
||||
}
|
||||
return tiles;
|
||||
}
|
||||
|
||||
function tileSize(d, z) {
|
||||
return Math.ceil(256 * Math.pow(2, z - d[2])) / 256;
|
||||
}
|
||||
|
||||
function lookUp(d) {
|
||||
for (var up = -1; up > -d[2]; up--) {
|
||||
if (cache[atZoom(d, up)] !== false) return atZoom(d, up);
|
||||
}
|
||||
}
|
||||
|
||||
function uniqueBy(a, n) {
|
||||
var o = [], seen = {};
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
if (seen[a[i][n]] === undefined) {
|
||||
o.push(a[i]);
|
||||
seen[a[i][n]] = true;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
function addSource(d) {
|
||||
d.push(source(d));
|
||||
return d;
|
||||
}
|
||||
|
||||
// derive the tiles onscreen, remove those offscreen and position tiles
|
||||
// correctly for the currentstate of `projection`
|
||||
function background() {
|
||||
var tiles = tile
|
||||
var sel = this,
|
||||
tiles = tile
|
||||
.scale(projection.scale())
|
||||
.scaleExtent(source.scaleExtent || [1, 17])
|
||||
.translate(projection.translate())(),
|
||||
requests = [],
|
||||
scaleExtent = tile.scaleExtent(),
|
||||
z = Math.max(Math.log(projection.scale()) / Math.log(2) - 8, 0),
|
||||
rz = Math.max(scaleExtent[0], Math.min(scaleExtent[1], Math.floor(z))),
|
||||
ts = 256 * Math.pow(2, z - rz),
|
||||
rz = Math.max(scaleExtent[0],
|
||||
Math.min(scaleExtent[1], Math.floor(z))),
|
||||
ts = tileSize[0] * Math.pow(2, z - rz),
|
||||
tile_origin = [
|
||||
projection.scale() / 2 - projection.translate()[0],
|
||||
projection.scale() / 2 - projection.translate()[1]],
|
||||
ups = {};
|
||||
projection.scale() / 2 - projection.translate()[1]];
|
||||
|
||||
tiles.forEach(function(d) {
|
||||
|
||||
if (cache[d] === true) {
|
||||
d.push(source(d));
|
||||
} else if (cache[d] === false &&
|
||||
cache[atZoom(d, -1)] !== false &&
|
||||
!ups[atZoom(d, -1)]) {
|
||||
|
||||
ups[atZoom(d, -1)] = true;
|
||||
tiles.push(atZoom(d, -1));
|
||||
|
||||
} else if (cache[d] === undefined &&
|
||||
lookUp(d)) {
|
||||
|
||||
var upTile = lookUp(d);
|
||||
if (!ups[upTile]) {
|
||||
ups[upTile] = true;
|
||||
tiles.push(upTile);
|
||||
}
|
||||
|
||||
} else if (cache[d] === undefined ||
|
||||
cache[d] === false) {
|
||||
upZoom(d, 1).forEach(function(u) {
|
||||
if (cache[u] && !ups[u]) {
|
||||
ups[u] = true;
|
||||
tiles.push(u);
|
||||
}
|
||||
});
|
||||
addSource(d);
|
||||
requests.push(d);
|
||||
if (!cache[d[3]] && lookUp(d)) {
|
||||
requests.push(addSource(lookUp(d)));
|
||||
}
|
||||
});
|
||||
|
||||
var image = this
|
||||
.selectAll('img')
|
||||
.data(tiles, function(d) { return d; });
|
||||
requests = uniqueBy(requests, 3);
|
||||
|
||||
function load(d) {
|
||||
cache[d.slice(0, 3)] = true;
|
||||
d3.select(this).on('load', null);
|
||||
cache[d[3]] = true;
|
||||
d3.select(this)
|
||||
.on('load', null)
|
||||
.transition()
|
||||
.style('opacity', 1);
|
||||
background.apply(sel);
|
||||
}
|
||||
|
||||
function error(d) {
|
||||
cache[d.slice(0, 3)] = false;
|
||||
cache[d[3]] = false;
|
||||
d3.select(this).on('load', null);
|
||||
d3.select(this).remove();
|
||||
background.apply(sel);
|
||||
}
|
||||
|
||||
function imageTransform(d) {
|
||||
var _ts = tileSize[0] * Math.pow(2, z - d[2]);
|
||||
var scale = tileSizeAtZoom(d, z);
|
||||
return 'translate(' +
|
||||
(Math.round((d[0] * _ts) - tile_origin[0]) + offset[0]) + 'px,' +
|
||||
(Math.round((d[1] * _ts) - tile_origin[1]) + offset[1]) + 'px)' +
|
||||
'scale(' + scale + ',' + scale + ')';
|
||||
}
|
||||
|
||||
var image = this
|
||||
.selectAll('img')
|
||||
.data(requests, function(d) { return d[3]; });
|
||||
|
||||
image.exit()
|
||||
.style(transformProp, imageTransform)
|
||||
.transition()
|
||||
.style('opacity', 0)
|
||||
.remove();
|
||||
|
||||
image.enter().append('img')
|
||||
.attr('style', imgstyle)
|
||||
.attr('src', function(d) { return d[3]; })
|
||||
.on('error', error)
|
||||
.on('load', load);
|
||||
|
||||
image.exit().remove();
|
||||
|
||||
image.style(transformProp, function(d) {
|
||||
var _ts = 256 * Math.pow(2, z - d[2]);
|
||||
var scale = tileSize(d, z);
|
||||
return 'translate(' +
|
||||
(Math.round((d[0] * _ts) - tile_origin[0]) + offset[0]) + 'px,' +
|
||||
(Math.round((d[1] * _ts) - tile_origin[1]) + offset[1]) + 'px) scale(' + scale + ',' + scale + ')';
|
||||
});
|
||||
|
||||
image.style(transformProp, imageTransform);
|
||||
|
||||
if (Object.keys(cache).length > 100) cache = {};
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ iD.Map = function() {
|
||||
for (var i = 0; i < parents.length; i++) {
|
||||
var parent = parents[i];
|
||||
if (only[parent.id] === undefined) {
|
||||
only[parent.id] = graph.entity(parent.id);
|
||||
only[parent.id] = parent;
|
||||
addParents(graph.parentRelations(parent));
|
||||
}
|
||||
}
|
||||
@@ -92,7 +92,7 @@ iD.Map = function() {
|
||||
|
||||
all = _.compact(_.values(only));
|
||||
filter = function(d) {
|
||||
if (d.midpoint) {
|
||||
if (d.type === 'midpoint') {
|
||||
for (var i = 0; i < d.ways.length; i++) {
|
||||
if (d.ways[i].id in only) return true;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ iD.Map = function() {
|
||||
.call(areas, graph, all, filter)
|
||||
.call(multipolygons, graph, all, filter)
|
||||
.call(midpoints, graph, all, filter)
|
||||
.call(labels, graph, all, filter, dimensions);
|
||||
.call(labels, graph, all, filter, dimensions, !difference);
|
||||
}
|
||||
dispatch.drawn(map);
|
||||
}
|
||||
@@ -123,7 +123,7 @@ iD.Map = function() {
|
||||
|
||||
function connectionLoad(err, result) {
|
||||
history.merge(result);
|
||||
redraw(Object.keys(result.entities));
|
||||
redraw(Object.keys(result));
|
||||
}
|
||||
|
||||
function zoomPan() {
|
||||
@@ -165,7 +165,8 @@ iD.Map = function() {
|
||||
}
|
||||
|
||||
function resetTransform() {
|
||||
if (!surface.style(transformProp)) return false;
|
||||
var prop = surface.style(transformProp);
|
||||
if (!prop || prop === 'none') return false;
|
||||
surface.style(transformProp, '');
|
||||
tilegroup.style(transformProp, '');
|
||||
return true;
|
||||
|
||||
+24
-9
@@ -156,7 +156,7 @@ iD.svg.Labels = function(projection) {
|
||||
for (var i = 0; i < nodes.length - 1; i++) {
|
||||
var current = segmentLength(i);
|
||||
var portion;
|
||||
if (!start && sofar + current > from) {
|
||||
if (!start && sofar + current >= from) {
|
||||
portion = (from - sofar) / current;
|
||||
start = [
|
||||
nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
|
||||
@@ -164,7 +164,7 @@ iD.svg.Labels = function(projection) {
|
||||
];
|
||||
i0 = i + 1;
|
||||
}
|
||||
if (!end && sofar + current > to) {
|
||||
if (!end && sofar + current >= to) {
|
||||
portion = (to - sofar) / current;
|
||||
end = [
|
||||
nodes[i][0] + portion * (nodes[i + 1][0] - nodes[i][0]),
|
||||
@@ -183,14 +183,26 @@ iD.svg.Labels = function(projection) {
|
||||
}
|
||||
|
||||
|
||||
return function drawLabels(surface, graph, entities, filter, dimensions) {
|
||||
var rtree = new RTree(),
|
||||
rectangles = {};
|
||||
|
||||
return function drawLabels(surface, graph, entities, filter, dimensions, fullRedraw) {
|
||||
|
||||
|
||||
var rtree = new RTree();
|
||||
var hidePoints = !d3.select('.node.point').node();
|
||||
|
||||
var labelable = [], i, k, entity;
|
||||
for (i = 0; i < label_stack.length; i++) labelable.push([]);
|
||||
|
||||
if (fullRedraw) {
|
||||
rtree = new RTree();
|
||||
rectangles = {};
|
||||
} else {
|
||||
for (i = 0; i < entities.length; i++) {
|
||||
rtree.remove(rectangles[entities[i].id], entities[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
// Split entities into groups specified by label_stack
|
||||
for (i = 0; i < entities.length; i++) {
|
||||
entity = entities[i];
|
||||
@@ -252,7 +264,7 @@ iD.svg.Labels = function(projection) {
|
||||
textAnchor: offset[2]
|
||||
};
|
||||
var rect = new RTree.Rectangle(p.x - m, p.y - m, width + 2*m, height + 2*m);
|
||||
if (tryInsert(rect)) return p;
|
||||
if (tryInsert(rect, entity.id)) return p;
|
||||
}
|
||||
|
||||
|
||||
@@ -275,7 +287,7 @@ iD.svg.Labels = function(projection) {
|
||||
Math.abs(sub[0][1] - sub[sub.length - 1][1]) + 30
|
||||
);
|
||||
if (rev) sub = sub.reverse();
|
||||
if (tryInsert(rect)) return {
|
||||
if (tryInsert(rect, entity.id)) return {
|
||||
'font-size': height + 2,
|
||||
lineString: lineString(sub),
|
||||
startOffset: offset + '%'
|
||||
@@ -298,16 +310,19 @@ iD.svg.Labels = function(projection) {
|
||||
height: height
|
||||
};
|
||||
var rect = new RTree.Rectangle(p.x - width/2, p.y, width, height);
|
||||
if (tryInsert(rect)) return p;
|
||||
if (tryInsert(rect, entity.id)) return p;
|
||||
|
||||
}
|
||||
|
||||
function tryInsert(rect) {
|
||||
function tryInsert(rect, id) {
|
||||
// Check that label is visible
|
||||
if (rect.x1 < 0 || rect.y1 < 0 || rect.x2 > dimensions[0] ||
|
||||
rect.y2 > dimensions[1]) return false;
|
||||
var v = rtree.search(rect, true).length === 0;
|
||||
if (v) rtree.insert(rect);
|
||||
if (v) {
|
||||
rtree.insert(rect, id);
|
||||
rectangles[id] = rect;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
+7
-11
@@ -15,19 +15,15 @@ iD.svg.Midpoints = function(projection) {
|
||||
b = nodes[j + 1],
|
||||
id = [a.id, b.id].sort().join('-');
|
||||
|
||||
if (!midpoints[id] &&
|
||||
iD.geo.dist(projection(a.loc), projection(b.loc)) > 40) {
|
||||
|
||||
var midpoint_loc = iD.geo.interp(a.loc, b.loc, 0.5),
|
||||
parents = _.intersection(graph.parentWays(a),
|
||||
graph.parentWays(b));
|
||||
if (midpoints[id]) {
|
||||
midpoints[id].ways.push({id: entity.id, index: j + 1});
|
||||
|
||||
} else if (iD.geo.dist(projection(a.loc), projection(b.loc)) > 40) {
|
||||
midpoints[id] = {
|
||||
loc: midpoint_loc,
|
||||
ways: parents,
|
||||
nodes: [a.id, b.id],
|
||||
type: 'midpoint',
|
||||
id: id,
|
||||
midpoint: true
|
||||
loc: iD.geo.interp(a.loc, b.loc, 0.5),
|
||||
ways: [{id: entity.id, index: j + 1}]
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -35,7 +31,7 @@ iD.svg.Midpoints = function(projection) {
|
||||
|
||||
var groups = surface.select('.layer-hit').selectAll('g.midpoint')
|
||||
.filter(filter)
|
||||
.data(_.values(midpoints), function (d) { return [d.parents, d.id].join(","); });
|
||||
.data(_.values(midpoints), function (d) { return d.id; });
|
||||
|
||||
var group = groups.enter()
|
||||
.insert('g', ':first-child')
|
||||
|
||||
@@ -36,7 +36,7 @@ iD.svg.Vertices = function(projection) {
|
||||
groups.attr('transform', iD.svg.PointTransform(projection))
|
||||
.call(iD.svg.TagClasses())
|
||||
.call(iD.svg.MemberClasses(graph))
|
||||
.classed('shared', function(entity) { return graph.parentWays(entity).length > 1; });
|
||||
.classed('shared', function(entity) { return graph.isShared(entity); });
|
||||
|
||||
// Selecting the following implicitly
|
||||
// sets the data (vertix entity) on the elements
|
||||
|
||||
@@ -16,7 +16,7 @@ iD.ui.contributors = function(map) {
|
||||
var l = selection
|
||||
.select('.contributor-list')
|
||||
.selectAll('a.user-link')
|
||||
.data(subset);
|
||||
.data(subset, function(d) { return d; });
|
||||
|
||||
|
||||
l.enter().append('a')
|
||||
@@ -43,6 +43,10 @@ iD.ui.contributors = function(map) {
|
||||
ext[1][0], ext[1][1]];
|
||||
})
|
||||
.text(' and ' + (u.length - limit) + ' others');
|
||||
} else {
|
||||
selection
|
||||
.select('.contributor-count')
|
||||
.html('');
|
||||
}
|
||||
|
||||
if (!u.length) {
|
||||
|
||||
+9
-12
@@ -4,20 +4,17 @@ iD.ui.notice = function(selection) {
|
||||
notice = {};
|
||||
|
||||
var div = selection.append('div')
|
||||
.attr('class', 'notice')
|
||||
.append('div')
|
||||
.attr('class', 'notice-inner');
|
||||
.attr('class', 'notice');
|
||||
|
||||
div.append('button')
|
||||
.attr('class', 'zoom-to')
|
||||
.on('click', function() {
|
||||
event.zoom();
|
||||
})
|
||||
.append('span')
|
||||
.attr('class', 'icon invert zoom-in');
|
||||
var button = div.append('button')
|
||||
.attr('class', 'zoom-to notice')
|
||||
.on('click', event.zoom);
|
||||
|
||||
div.append('span')
|
||||
.attr('class', 'notice-text')
|
||||
button.append('span')
|
||||
.attr('class', 'icon zoom-in-invert');
|
||||
|
||||
button.append('span')
|
||||
.attr('class', 'label')
|
||||
.text(t('zoom_in_edit'));
|
||||
|
||||
notice.message = function(_) {
|
||||
|
||||
@@ -94,3 +94,5 @@ iD.util.editDistance = function(a, b) {
|
||||
}
|
||||
return matrix[b.length][a.length];
|
||||
};
|
||||
|
||||
iD.util.getPrototypeOf = Object.getPrototypeOf || function(obj) { return obj.__proto__; };
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
from xml.dom.minidom import parse
|
||||
import json
|
||||
|
||||
dom1 = parse('potlatch.xml')
|
||||
|
||||
inputSets = dom1.getElementsByTagName('inputSet')
|
||||
|
||||
jsonOutput = []
|
||||
|
||||
for inputSet in inputSets:
|
||||
setId = inputSet.getAttribute('id')
|
||||
inputs = inputSet.getElementsByTagName('input')
|
||||
for i in inputs:
|
||||
jsonInput = {}
|
||||
inputType = i.getAttribute('type')
|
||||
if inputType == 'choice':
|
||||
choices = i.getElementsByTagName('choice')
|
||||
jsonInput['type'] = 'choice'
|
||||
jsonInput['description'] = i.getAttribute('description')
|
||||
jsonInput['name'] = i.getAttribute('name')
|
||||
jsonInput['key'] = i.getAttribute('key')
|
||||
jsonInput['choices'] = []
|
||||
for c in choices:
|
||||
jsonInput['choices'].append({
|
||||
"value": c.getAttribute('value'),
|
||||
"text": c.getAttribute('text')
|
||||
})
|
||||
elif inputType == 'freetext':
|
||||
jsonInput['type'] = 'freetext'
|
||||
jsonInput['description'] = i.getAttribute('description')
|
||||
jsonInput['name'] = i.getAttribute('name')
|
||||
jsonInput['key'] = i.getAttribute('key')
|
||||
elif inputType == 'checkbox':
|
||||
jsonInput['type'] = 'checkbox'
|
||||
jsonInput['description'] = i.getAttribute('description')
|
||||
jsonInput['name'] = i.getAttribute('name')
|
||||
jsonInput['key'] = i.getAttribute('key')
|
||||
elif inputType == 'number':
|
||||
jsonInput['type'] = 'number'
|
||||
jsonInput['description'] = i.getAttribute('description')
|
||||
jsonInput['name'] = i.getAttribute('name')
|
||||
jsonInput['minimum'] = i.getAttribute('minimum')
|
||||
jsonInput['maximum'] = i.getAttribute('maximum')
|
||||
jsonInput['key'] = i.getAttribute('key')
|
||||
jsonOutput.append(jsonInput)
|
||||
|
||||
json.dump(jsonOutput, open('presets_potlatch.json', 'w'), indent=4)
|
||||
@@ -0,0 +1,693 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
See http://wiki.openstreetmap.org/wiki/Potlatch_2/Developer_Documentation/Map_Features for documentation.
|
||||
Files can be included like this: <include file="map_features/roads.xml">
|
||||
-->
|
||||
|
||||
<mapFeatures>
|
||||
|
||||
<!-- Categories -->
|
||||
<category name="Roads" id="roads"/>
|
||||
<category name="Paths" id="paths"/>
|
||||
<category name="Shopping" id="shopping"/>
|
||||
<category name="Food and Drink" id="foodanddrink"/>
|
||||
<category name="Amenity" id="amenity"/>
|
||||
<category name="Tourism" id="tourism"/>
|
||||
<category name="Accommodation" id="accommodation"/>
|
||||
<category name="Transport" id="transport"/>
|
||||
<category name="Water" id="water"/>
|
||||
<category name="Natural" id="natural"/>
|
||||
<category name="Barrier" id="barrier"/>
|
||||
<category name="Power" id="power"/>
|
||||
<category name="Admin" id="admin"/>
|
||||
<category name="Buildings" id="buildings"/>
|
||||
<category name="Landuse" id="landuse"/>
|
||||
<category name="Agriculture" id="agriculture"/>
|
||||
<category name="Advanced" id="advanced"/>
|
||||
<category name="Places" id="places"/>
|
||||
<category name="Sport and Leisure" id="sport"/>
|
||||
<category name="Man-made" id="manmade"/>
|
||||
|
||||
<!-- Common input sets -->
|
||||
|
||||
<inputSet id="simpleName">
|
||||
<input type="freetext" presence="always" name="Name" key="name" description="The name" priority="highest"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="nameAndRef">
|
||||
<inputSet ref="simpleName" />
|
||||
<input type="freetext" presence="always" name="Reference number" key="ref" description="A reference number or code used to identify this thing." />
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="simpleBrand">
|
||||
<input type="freetext" category="Naming" presence="always" name="Brand" key="brand" description="Brand, i.e. Acme"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="simpleOperator">
|
||||
<input type="freetext" category="Naming" presence="always" name="Operator" key="operator" description="Operator, i.e. Acme Springfield Ltd"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="source">
|
||||
<input type="freetext" category="Details" presence="onTagMatch" name="Source" key="source" description="The primary source of information for this object (GPS, survey, Bing, ...)" priority="lowest"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="designation">
|
||||
<input type="freetext" category="Details" presence="onTagMatch" description="The official designation or classification (if any). Only use this if the organisation that runs it has its own classification system." name="Official classification" key="designation" priority="lowest" />
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="common">
|
||||
<inputSet ref="source"/>
|
||||
<inputSet ref="designation"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="names">
|
||||
<input type="freetext" presence="always"
|
||||
name="Name" category="Details" priority="highest"
|
||||
key="name" description="The most common name"/>
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="International Name" category="Details" subcategory="Additional names"
|
||||
key="int_name" description="The internationally recognised name"/>
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="Historical Name" category="Details" subcategory="Additional names" priority="low"
|
||||
key="old_name" description="The historic or previous name"/>
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="Alternative Name" category="Details" subcategory="Additional names" priority="low"
|
||||
key="alt_name" description="An alternative, currently used, name"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="wifi">
|
||||
<input type="choice" presence="onTagMatch" category="Details" name="Wifi" key="wifi">
|
||||
<choice value="free" text="Free"/>
|
||||
<choice value="yes" text="Yes"/>
|
||||
<choice value="no" text="No"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="buildingAddress">
|
||||
<input type="freetext" presence="onTagMatch" category="Address" description="The number of the house, e.g. 156 or 10-12" name="House Number" key="addr:housenumber"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Address" description="The name of the house, e.g. Riverbank Cottage" name="Building Name" key="addr:housename"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Address" description="The Street Name (optional)" name="Street Name" key="addr:street"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Address" description="The postcode" name="Postcode" key="addr:postcode"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="web">
|
||||
<input type="freetext" presence="onTagMatch" category="Address" description="The URL of the website" name="Website" key="website"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="cuisine">
|
||||
<input type="choice" presence="always" name="Cuisine" category="Details" description="The type of food that they serve" key="cuisine">
|
||||
<!-- The 30 most popular values according to taginfo 23/12/2010 -->
|
||||
<choice value="burger" text="Burger"/>
|
||||
<choice value="chicken" text="Chicken"/>
|
||||
<choice value="chinese" text="Chinese"/>
|
||||
<choice value="coffee_shop" text="Coffee Shop"/>
|
||||
<choice value="greek" text="Greek"/>
|
||||
<choice value="pizza" text="Pizza"/>
|
||||
<choice value="sandwich" text="Sandwich"/>
|
||||
<choice value="seafood" text="Sea Food"/>
|
||||
<choice value="regional" text="Regional"/>
|
||||
<choice value="italian" text="Italian"/>
|
||||
<choice value="german" text="German"/>
|
||||
<choice value="kebab" text="Kebab/souvlaki/gyro"/>
|
||||
<choice value="indian" text="Indian"/>
|
||||
<choice value="asian" text="Asian"/>
|
||||
<choice value="mexican" text="Mexican"/>
|
||||
<choice value="thai" text="Thai"/>
|
||||
<choice value="japanese" text="Japanese"/>
|
||||
<choice value="ice_cream" text="Ice-cream"/>
|
||||
<choice value="fish_and_chips" text="Fish & Chips"/>
|
||||
<choice value="turkish" text="Turkish"/>
|
||||
<choice value="french" text="French"/>
|
||||
<choice value="sushi" text="Sushi"/>
|
||||
<choice value="american" text="American"/>
|
||||
<choice value="steak_house" text="Steak House"/>
|
||||
<choice value="international" text="International"/>
|
||||
<choice value="spanish" text="Spanish"/>
|
||||
<choice value="vietnamese" text="Vietnamese"/>
|
||||
<choice value="fish" text="Fish"/>
|
||||
<choice value="bavarian" text="Bavarian"/>
|
||||
<choice value="vegetarian" text="Vegetarian"/>
|
||||
|
||||
<help>http://wiki.openstreetmap.org/wiki/Key:cuisine</help>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<!-- Roads -->
|
||||
|
||||
<inputSet id="majorRoad">
|
||||
<inputSet ref="names"/>
|
||||
<inputSet ref="roadRefs"/>
|
||||
<inputSet ref="roadRestrictions"/>
|
||||
<inputSet ref="roadPhysical"/>
|
||||
<inputSet ref="cycle"/>
|
||||
<inputSet ref="bicycle-lane"/>
|
||||
<inputSet ref="bus-route"/>
|
||||
<inputSet ref="tram-route"/>
|
||||
<inputSet ref="pedestrians"/>
|
||||
<inputSet ref="roadLanes"/>
|
||||
<inputSet ref="roadRoundabout"/>
|
||||
<inputSet ref="permissions"/>
|
||||
</inputSet>
|
||||
|
||||
|
||||
<inputSet id="minorRoad">
|
||||
<inputSet ref="names"/>
|
||||
<inputSet ref="roadRestrictions"/>
|
||||
<inputSet ref="roadPhysical"/>
|
||||
<inputSet ref="cycle"/>
|
||||
<inputSet ref="bicycle-lane"/>
|
||||
<inputSet ref="bus-route"/>
|
||||
<inputSet ref="tram-route"/>
|
||||
<inputSet ref="pedestrians"/>
|
||||
<inputSet ref="roadLanes"/>
|
||||
<inputSet ref="roadRoundabout"/>
|
||||
<inputSet ref="permissions"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="path">
|
||||
<inputSet ref="simpleName"/>
|
||||
<inputSet ref="roadPhysical"/>
|
||||
<inputSet ref="cycle"/>
|
||||
<inputSet ref="pedestrians"/>
|
||||
<inputSet ref="permissions"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="junctionNode">
|
||||
<inputSet ref="turnRestrictions"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="roadRefs">
|
||||
<input type="freetext" presence="always"
|
||||
name="Reference" category="Details" priority="high"
|
||||
key="ref" description="The official reference number"/>
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="International Reference" category="Details" subcategory="Additional names"
|
||||
key="int_ref" description="The official international reference number"/>
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="Old Reference" category="Details" subcategory="Additional names" priority="low"
|
||||
key="old_ref" description="The historic or previous reference number"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="roadPhysical">
|
||||
<input type="freetext" presence="onTagMatch"
|
||||
name="Width" category="Details" subcategory="Physical"
|
||||
key="width" description="Width of the road" layout="horizontal"/>
|
||||
<input type="choice" presence="onTagMatch"
|
||||
name="Surface" category="Details" description="Type of road surface"
|
||||
key="surface" layout="horizontal">
|
||||
<choice value="unpaved" text="Unpaved" description="Road surface is unsealed"/>
|
||||
<choice value="paved" text="Paved" description="Road surface is sealed"/>
|
||||
<choice value="asphalt" text="Asphalt"/>
|
||||
<choice value="concrete" text="Concrete"/>
|
||||
<choice value="paving_stones" text="Paving stones"/>
|
||||
<choice value="cobblestone" text="Cobblestone"/>
|
||||
<choice value="sand" text="Sand"/>
|
||||
<choice value="gravel" text="Gravel"/>
|
||||
<choice value="dirt" text="Dirt"/>
|
||||
<choice value="grass" text="Grass"/>
|
||||
</input>
|
||||
<inputSet ref="bridge"/>
|
||||
<inputSet ref="tunnel"/>
|
||||
<inputSet ref="embankment-cutting"/>
|
||||
<!-- not sure which category best suits put area=yes -->
|
||||
<input type="checkbox" presence="onTagMatch" category="Details" subcategory="Physical" key="area" name="Open area" description="The way is a large open space, like at a dock, where vehicles can move anywhere within the space, rather than just along the edge." />
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="roadLanes">
|
||||
<input presence="onTagMatch" type="number" name="Lanes" category="Details" description="Total number of lanes, counting both directions"
|
||||
key="lanes" minimum="1" maximum="10" layout="horizontal"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="bridge">
|
||||
<input type="choice" presence="onTagMatch"
|
||||
name="Bridge" category="Details" description="Road goes over a bridge"
|
||||
key="bridge" layout="horizontal">
|
||||
<choice value="yes" text="Generic Bridge" description="Generic bridge -- type unknown"/>
|
||||
<choice value="viaduct" text="Viaduct" description="Viaduct"/>
|
||||
<choice value="suspension" text="Suspension bridge"/>
|
||||
</input>
|
||||
<input type="slider" presence="onTagMatch"
|
||||
name="Layer" category="Details" description="Relative vertical positions (-5 lowest, +5 highest)"
|
||||
key="layer" minimum="-5" maximum="5" default="0" snapInterval="1" labels="Lowest,Ground,Highest"
|
||||
defaultName="Ground"/>
|
||||
</inputSet>
|
||||
<inputSet id="tunnel">
|
||||
<!-- Not ideal, used for non-roads too. -->
|
||||
<input type="choice" presence="onTagMatch"
|
||||
name="Tunnel" category="Details" subcategory="Physical" description="Road goes into a tunnel"
|
||||
key="tunnel" layout="horizontal">
|
||||
<choice value="yes" text="Tunnel" description="Generic tunnel"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="embankment-cutting">
|
||||
<input type="choice"
|
||||
name="Embankment" category="Details" subcategory="Physical" description="Road supported on a raised bed of earth and rock."
|
||||
key="embankment" layout="horizontal">
|
||||
<choice value="yes" text="Embankment"/>
|
||||
</input>
|
||||
<input type="choice"
|
||||
name="Cutting" category="Details" subcategory="Physical" description="Road carved out of hill on one or both sides."
|
||||
key="cutting" layout="horizontal">
|
||||
<choice value="yes" text="Cutting"/>
|
||||
</input>
|
||||
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="rail-electrification">
|
||||
<input type="choice" name="Electrified" category="Details" subcategory="Electrification" description="Is the track electrified (whether by 3rd rail, overhead wires, etc)?"
|
||||
key="electrified">
|
||||
<choice value="yes" text="Yes"/>
|
||||
<choice value="contact_line" text="Overhead line"/>
|
||||
<choice value="rail" text="Third rail"/>
|
||||
<choice value="no" text="No"/>
|
||||
</input>
|
||||
<input type="choice" name="Voltage" category="Details" subcategory="Electrification" description="Nominal voltage of electric wires"
|
||||
key="voltage" presence="withCategory">
|
||||
<choice value="600" text="600V"/>
|
||||
<choice value="750" text="750V"/>
|
||||
<choice value="1500" text="1500V"/>
|
||||
<choice value="3000" text="3000V"/>
|
||||
<choice value="12000" text="12kV"/>
|
||||
<choice value="15000" text="15kV"/>
|
||||
<choice value="25000" text="25kV"/>
|
||||
</input>
|
||||
<input type="choice" name="Frequency" category="Details" subcategory="Electrification" description="Frequency in Hertz of alternating current power supply"
|
||||
key="frequency" presence="withCategory">
|
||||
<choice value="0" text="DC"/>
|
||||
<choice value="16.67" text="16.67 Hz"/>
|
||||
<choice value="16.7" text="16.7 Hz"/>
|
||||
<choice value="25" text="25 Hz"/>
|
||||
<choice value="50" text="50 Hz"/>
|
||||
<choice value="60" text="60 Hz"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="fee">
|
||||
<input type="freetext" presence="onTagMatch" category="Restrictions" description="The charge/cost of using this amenity" name="Fee" key="fee"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="roadRestrictions">
|
||||
<input type="choice" presence="always"
|
||||
name="Oneway" category="Restrictions" description="Oneway roads"
|
||||
key="oneway">
|
||||
<choice value="yes" match="yes|true|1" text="One way"
|
||||
description="Road can only be travelled in direction of way" icon="features/oneway__yes.png"/>
|
||||
<choice value="no" match="no|false|0" text="Two way"
|
||||
description="Road can be travelled in both directions" icon="features/oneway__no.png"/>
|
||||
<choice value="-1" match="-1|reverse" text="One way reverse"
|
||||
description="Road can be travelled in opposite direction to way" icon="features/oneway__-1.png"/>
|
||||
</input>
|
||||
<input type="speed" presence="onTagMatch"
|
||||
name="Speed Limit" category="Restrictions" description="Maximum permitted speed on this road"
|
||||
key="maxspeed"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="roadRoundabout">
|
||||
<!-- review the choice of category -->
|
||||
<input type="choice" presence="onTagMatch" name="Roundabout" category="Restrictions" description="Whether this road is a roundabout. Make the way face the direction appropriate for the country."
|
||||
key="junction">
|
||||
<choice value="roundabout" text="Yes"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="turnRestrictions">
|
||||
<input type="turn" name="Turn restriction" description="Turn restriction" category="Restrictions" priority="normal" presence="onTagMatch">
|
||||
<match k="type" v="restriction"/>
|
||||
<role role="via"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="trafficSignals">
|
||||
<!-- can't add as standalone feature due to current limitations, would conflict with junction node feature -->
|
||||
<input type="choice" key="highway" name="Traffic signals" description="Intersection controlled by traffic lights" category="Restrictions" presence="always">
|
||||
<choice value="traffic_signals" text="Yes"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="pedestrians">
|
||||
<input type="choice" name="Pedestrians permitted" description="Can pedestrians use this road, including footpaths if any?" category="Walk" key="foot">
|
||||
<choice value="yes" text="Allowed"/>
|
||||
<choice value="no" text="Prohibited"/>
|
||||
<choice value="designated" text="Designated"/>
|
||||
</input>
|
||||
<input type="choice" presence="onTagMatch"
|
||||
name="Sidewalks" category="Walk" description="Whether there is a sidewalk at the side of the street"
|
||||
key="sidewalk" layout="horizontal">
|
||||
<choice value="both" text="Both" description="There is a sidewalk on both sides of the road." match="yes"/>
|
||||
<choice value="left" text="Left" description="The sidewalk is on the left of the direction of the road."/>
|
||||
<choice value="right" text="Right" description="The sidewalk is on the right of the direction of the road."/>
|
||||
<choice value="separate" text="Separate" description="The sidewalk has been mapped as a separate line parallel."/>
|
||||
<choice value="none" text="None" description="There is no sidewalk on this section of road." match="no"/>
|
||||
</input>
|
||||
<input type="route" name="National Walking Route" description="National walking route" category="Walk" priority="low">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="hiking|foot"/>
|
||||
<match k="network" v="nwn"/>
|
||||
<icon image="features/route__nwn.png" background="red" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
<input type="route" name="Regional Walking Route" description="Regional walking route" category="Walk" priority="low">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="hiking|foot"/>
|
||||
<match k="network" v="rwn"/>
|
||||
<icon image="features/route__rwn.png" background="cyan" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
<input type="route" name="Local Walking Route" description="Local walking route" category="Walk" priority="lowest">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="hiking|foot"/>
|
||||
<match k="network" v="lwn"/>
|
||||
<icon image="features/route__lwn.png" background="blue" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="naptan">
|
||||
<input type="freetext" presence="onTagMatch" category="Transport" description="12 character internal Naptan ID" name="Atco Code" key="naptan:AtcoCode"/>
|
||||
<input type="choice" presence="onTagMatch" category="Transport" description="The eight-point compass bearning" name="Naptan Bearing" key="naptan:Bearing" >
|
||||
<choice value="N" match="N" text="N" description=""/>
|
||||
<choice value="NE" match="NE" text="NE" description=""/>
|
||||
<choice value="E" match="E" text="E" description=""/>
|
||||
<choice value="SE" match="SE" text="SE" description=""/>
|
||||
<choice value="S" match="S" text="S" description=""/>
|
||||
<choice value="SW" match="SW" text="SW" description=""/>
|
||||
<choice value="W" match="W" text="W" description=""/>
|
||||
<choice value="NW" match="NW" text="NW" description=""/>
|
||||
</input>
|
||||
<input type="freetext" presence="onTagMatch" category="Transport" description="The naptan common name" name="Naptan Common Name (read-only)" key="naptan:CommonName"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Transport" description="" name="Naptan Indicator (read-only)" key="naptan:Indicator"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Transport" description="" name="Naptan Street (read-only)" key="naptan:Street"/>
|
||||
<input type="freetext" presence="onTagMatch" category="Transport" description="Delete this when the details have been verified on-the-ground" name="Naptan Verified?" key="naptan:verified"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="buses">
|
||||
<input type="freetext" presence="always" category="Transport" name="Stop Name" key="name" description="The name of the bus stop"/>
|
||||
<input type="freetext" presence="always" category="Transport" name="Local Ref" key="local_ref" description="The local reference of the stop, usually one or two letters above the main flag, used at bus interchanges, e.g. L, BX"/>
|
||||
<inputSet ref="naptan"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="bus-route">
|
||||
<input type="route" name="Bus Route" description="Bus route" category="Transport" priority="low" presence="onTagMatch">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="bus"/>
|
||||
<icon image="features/route__bus.png">
|
||||
<font size="12pt">${operator} <b>${ref}</b></font>
|
||||
</icon>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="tram-route">
|
||||
<input type="route" name="Tram Route" description="Tram route" category="Transport" priority="low" presence="onTagMatch">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="tram"/>
|
||||
<icon image="features/transport__tram.png">
|
||||
<font size="12pt">${operator} <b>${ref}</b></font>
|
||||
</icon>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="train-route">
|
||||
<input type="route" name="Train Route" description="Train route" category="Transport" priority="low" presence="onTagMatch">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="train"/>
|
||||
<icon image="features/transport__railway.png">
|
||||
<font size="12pt">${name|operator} <b>(${ref})</b></font>
|
||||
</icon>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="cycle">
|
||||
<inputSet ref="bicycle-permission"/>
|
||||
<input type="route" name="National Cycle Routes" description="A signposted route in a National Cycle Network, or nearest equivalent." category="Cycle" priority="low">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="bicycle"/>
|
||||
<match k="network" v="ncn"/>
|
||||
<icon image="features/route__ncn.png" background="#ff6f7a" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
<input type="route" name="Regional Cycle Routes" description="A signposted route in a Regional Cycle Network, or nearest equivalent." category="Cycle" priority="low">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="bicycle"/>
|
||||
<match k="network" v="rcn"/>
|
||||
<icon image="features/route__rcn.png" background="#6ff7ff" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
<input type="route" name="Local Cycle Routes" description="A signposted route in a Local Cycle Network, or nearest equivalent." category="Cycle" priority="lowest">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="bicycle"/>
|
||||
<match k="network" v="lcn"/>
|
||||
<icon image="features/route__lcn.png" background="#7d6fff" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
<input type="route" name="Mountain bike route" description="A signposted route for mountain biking." category="Cycle" priority="lowest">
|
||||
<match k="type" v="route"/>
|
||||
<match k="route" v="mtb"/>
|
||||
<icon image="features/route__mtb.png" background="#9f4404" foreground="white">
|
||||
<font size="14pt"><b>${ref}</b></font><br/>
|
||||
<font size="12pt">${name}</font>
|
||||
</icon>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="bicycle-permission">
|
||||
<input type="choice"
|
||||
name="Bicycles permitted" category="Cycle" description="Are bicyles allowed to use this road (regardless of physical suitability)?"
|
||||
key="bicycle">
|
||||
<choice value="yes" text="Allowed"/>
|
||||
<choice value="no" text="Prohibited" desciption="Cycling is not allowed. Pushing your bicycle is not allowed either."/>
|
||||
<choice value="private" text="Private"/>
|
||||
<choice value="dismount" text="Cyclists dismount" description="Signage states that cyclists should dismount and push their bike for the selected path or road."/>
|
||||
<choice value="designated" text="Designated"/>
|
||||
<help>http://wiki.openstreetmap.org/wiki/Key:access</help>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="bicycle-lane">
|
||||
<input type="choice"
|
||||
name="Bike lanes" category="Cycle" description="Road has bike lanes within the road surface"
|
||||
key="cycleway" layout="horizontal">
|
||||
<choice value="no" text="No bike lanes"/>
|
||||
<choice value="lane" text="On-road bike lane" description="Separated by painted line on the road from cars"/>
|
||||
<choice value="track" text="Parallel track" description="Separated by kerb or parked cars"/>
|
||||
<choice value="opposite_lane" text="Contraflow lane" description="Separated by painted line, and allowing bicycles in both directions in an otherwise one-way street."/>
|
||||
<choice value="opposite_track" text="Contraflow track" description="Separated by kerb or parked cars, and allowing bicycles in both directions in an otherwise one-way street."/>
|
||||
<choice value="opposite" text="Contraflow unmarked" description="The route may be cycled in the direction opposite of other traffic, but does not have a dedicated lane."/>
|
||||
<help>http://wiki.openstreetmap.org/wiki/Key:cycleway</help>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="route">
|
||||
<inputSet ref="names"/>
|
||||
<inputSet ref="roadRefs"/>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="waterways">
|
||||
<inputSet ref="simpleName"/>
|
||||
<input type="freetext" presence="always" name="Width" category="Details" key="width" description="The width in metres"/>
|
||||
<inputSet ref="boatPermissions"/>
|
||||
</inputSet>
|
||||
<!-- Would be good to have a dedicated 'access' type of input -->
|
||||
<!-- all the description fields are identical at the moment for ease of maintenance, should be tailored in future. -->
|
||||
<inputSet id="boatPermissions">
|
||||
<input type="choice" name="Boat permission" category="Restrictions" key="boat" description="Are boats allowed to use this waterway?">
|
||||
<choice value="yes" text="Allowed" description="General right of way."/>
|
||||
<choice value="no" text="Prohibited" description="No access to the public."/>
|
||||
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
|
||||
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
|
||||
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="permissions">
|
||||
<input type="choice" name="General access" category="Restrictions" key="access" description="Is there a general right of access, regardless of mode of transport?">
|
||||
<choice value="yes" text="Allowed" description="General right of way."/>
|
||||
<choice value="no" text="Prohibited" description="No access to the public."/>
|
||||
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
|
||||
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
|
||||
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
|
||||
</input>
|
||||
|
||||
<input type="choice" name="Motor vehicles" category="Restrictions" key="motor_vehicle" description="Are cars and other private vehicles allowed?">
|
||||
<choice value="yes" text="Allowed" description="General right of way."/>
|
||||
<choice value="no" text="Prohibited" description="No access to the public."/>
|
||||
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
|
||||
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
|
||||
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
|
||||
</input>
|
||||
|
||||
<inputSet ref="hores-permission" />
|
||||
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="horse-permission">
|
||||
<input type="choice" name="Horses" category="Restrictions" key="horse" description="Are horses allowed?">
|
||||
<choice value="yes" text="Allowed" description="General right of way."/>
|
||||
<choice value="no" text="Prohibited" description="No access to the public."/>
|
||||
<choice value="permissive" text="Permissive" description="Access permitted through private land."/>
|
||||
<choice value="private" text="Private" description="No access to the public, except individual exceptions."/>
|
||||
<choice value="designated" text="Designated" description="Permitted, according to signs or specific local laws."/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="places">
|
||||
<input type="choice" name="Type of Place" presence="always" key="place">
|
||||
<choice value="locality" text="Locality"/>
|
||||
<choice value="hamlet" text="Hamlet"/>
|
||||
<choice value="village" text="Village"/>
|
||||
<choice value="suburb" text="Suburb"/>
|
||||
<choice value="town" text="Town"/>
|
||||
<choice value="city" text="City"/>
|
||||
<choice value="county" text="County"/>
|
||||
<choice value="region" text="Region"/>
|
||||
<choice value="state" text="State"/>
|
||||
<choice value="country" text="Country"/>
|
||||
<choice value="continent" text="Continent"/>
|
||||
<choice value="island" text="Island"/>
|
||||
<choice value="islet" text="Islet"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="isBuilding">
|
||||
<input type="choice" presence="withCategory" category="Details" name="Building type, if it is one" key="building">
|
||||
<choice value="yes" text="Generic building"/>
|
||||
<choice value="residential" text="Generic residential"/>
|
||||
<choice value="apartments" text="Big apartments house"/>
|
||||
<choice value="terrace" text="Terraced house"/>
|
||||
<choice value="house" text="Family house"/>
|
||||
<choice value="hut" text="Small hut"/>
|
||||
<choice value="garage" text="A garage"/>
|
||||
<choice value="garages" text="Block of garages"/>
|
||||
<choice value="office" text="Office building"/>
|
||||
<choice value="public" text="Public building"/>
|
||||
<choice value="industrial" text="Generic industrial"/>
|
||||
<choice value="manufacture" text="Manufacture"/>
|
||||
<choice value="warehouse" text="Warehouse"/>
|
||||
<choice value="hangar" text="Hangar"/>
|
||||
<choice value="storage_tank" text="Fluids storage tank"/>
|
||||
<choice value="retail" text="Retail"/>
|
||||
<choice value="supermarket" text="Supermarket"/>
|
||||
<choice value="train_station" text="Train station"/>
|
||||
<choice value="church" text="Church"/>
|
||||
<choice value="school" text="School"/>
|
||||
<choice value="bunker" text="Military bunker"/>
|
||||
<choice value="collapsed" text="Collapsed building"/>
|
||||
<choice value="roof" text="Just a roof"/>
|
||||
<help>http://wiki.openstreetmap.org/wiki/Key:building</help>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="powerCables">
|
||||
<input type="choice" name="Cables" key="cables" presence="always" category="Details">
|
||||
<choice value="2" text="2"/>
|
||||
<choice value="3" text="3"/>
|
||||
<choice value="4" text="4"/>
|
||||
<choice value="6" text="6"/>
|
||||
<choice value="8" text="8"/>
|
||||
<choice value="10" text="10"/>
|
||||
<choice value="12" text="12"/>
|
||||
</input>
|
||||
<input type="choice" name="Voltage" key="voltage" presence="always" category="Details">
|
||||
<!-- choices based on http://osmdoc.com/en/tag/voltage/#values-->
|
||||
<choice value="400" text="400 V"/>
|
||||
<choice value="600" text="600 V"/>
|
||||
<choice value="750" text="750 V"/>
|
||||
<choice value="1500" text="1500 V"/>
|
||||
<choice value="3000" text="3000 V"/>
|
||||
<choice value="15000" text="15 kV"/>
|
||||
<choice value="20000" text="20 kV"/>
|
||||
<choice value="35000" text="35 kV"/>
|
||||
<choice value="110000" text="110 kV"/>
|
||||
<choice value="132000" text="132 kV"/>
|
||||
<choice value="138000" text="138 kV"/>
|
||||
<choice value="220000" text="220 kV"/>
|
||||
<choice value="380000" text="380 kV"/>
|
||||
<choice value="500000" text="500 kV"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="pitchSport">
|
||||
<input name="Sport" presence="always" category="Details" key="sport" type="choice" description="The sport that is predominantly played here.">
|
||||
<choice value="9pin" text="9 pin bowling"/>
|
||||
<choice value="10pin" text="10 pin bowling"/>
|
||||
<choice value="american_football" text="American football"/>
|
||||
<choice value="archery" text="Archery"/>
|
||||
<choice value="athletics" text="Athletics"/>
|
||||
<choice value="australian_football" text="Australian Rules Football"/>
|
||||
<choice value="baseball" text="Baseball"/>
|
||||
<choice value="basketball" text="Basketball"/>
|
||||
<choice value="beachvolleyball" text="Beach volleyball"/>
|
||||
<choice value="boules" text="Boules/petanque/bocci"/>
|
||||
<choice value="bowls" text="Lawn bowls"/>
|
||||
<choice value="canadian_football" text="Canadian football"/>
|
||||
<choice value="chess" text="Chess"/>
|
||||
<choice value="cricket" text="Cricket"/>
|
||||
<choice value="cricket_nets" text="Cricket nets"/>
|
||||
<choice value="croquet" text="Croquet"/>
|
||||
<choice value="equestrian" text="Equestrian"/>
|
||||
<choice value="gaelic_football" text="Gaelic football"/>
|
||||
<choice value="gymnastics" text="Gymnastics"/>
|
||||
<choice value="team_handball" text="(Team) handball"/>
|
||||
<choice value="hockey" text="(Field) hockey"/>
|
||||
<choice value="korfball" text="Korball"/>
|
||||
<choice value="pelota" text="Pelota"/>
|
||||
<choice value="rugby_league" text="Rugby league"/>
|
||||
<choice value="rugby_union" text="Rugby union"/>
|
||||
<choice value="shooting" text="Shooting"/>
|
||||
<choice value="skating" text="Ice skating"/>
|
||||
<choice value="skateboard" text="Skateboarding"/>
|
||||
<choice value="soccer" text="Soccer/football"/>
|
||||
<choice value="swimming" text="Swimming"/>
|
||||
<choice value="table_tennis" text="Table tennis"/>
|
||||
<choice value="tennis" text="Tennis"/>
|
||||
<choice value="volleyball" text="Volleyball"/>
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<inputSet id="wheelchair-basic">
|
||||
<input type="choice" name="Wheelchair" key="wheelchair" presence="onTagMatch" category="Details">
|
||||
<choice value="yes" text="Yes: ramps/elevators/etc" />
|
||||
<choice value="no" text="No: inaccessible to wheelchairs" />
|
||||
<choice value="limited" text="Limited accessibility" />
|
||||
</input>
|
||||
</inputSet>
|
||||
|
||||
<!-- Features -->
|
||||
|
||||
<include file="map_features/roads.xml" /> <!-- includes traffic calming, roundabouts, race track -->
|
||||
<include file="map_features/paths.xml" /> <!-- includes steps, highway=pedestrian, bike/hike routes -->
|
||||
<include file="map_features/water.xml" /> <!-- includes coastline -->
|
||||
<include file="map_features/transport.xml" /> <!-- includes rail, trams, bus, airports, car rental, bike parking... -->
|
||||
<include file="map_features/power.xml" /> <!-- includes power lines, pylons, stations -->
|
||||
<include file="map_features/places.xml" /> <!-- includes boundaries (inc. NP) -->
|
||||
<include file="map_features/tourism.xml" /> <!-- includes accommodation -->
|
||||
<include file="map_features/barriers.xml" />
|
||||
<include file="map_features/shopping.xml" /> <!-- no clear distinction between this, amenities and buildings -->
|
||||
<include file="map_features/amenities.xml" /> <!-- includes cafe, bar... -->
|
||||
<include file="map_features/landuse.xml" /> <!-- includes outdoor leisure/sporting stuff, agriculture, natural. -->
|
||||
<include file="map_features/man_made.xml" />
|
||||
<include file="map_features/buildings.xml" /> <!-- a bit of a dumping ground, includes sports centre, shopping centre, hospital... -->
|
||||
|
||||
|
||||
|
||||
<!-- Relations -->
|
||||
<feature name="Multipolygon">
|
||||
<category>advanced</category>
|
||||
<relation/>
|
||||
<tag k="type" v="multipolygon"/>
|
||||
<inputSet ref="common"/>
|
||||
</feature>
|
||||
|
||||
</mapFeatures>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -68,6 +68,7 @@
|
||||
<script src='../js/id/ui/confirm.js'></script>
|
||||
|
||||
<script src='../js/id/actions.js'></script>
|
||||
<script src="../js/id/actions/add_midpoint.js"></script>
|
||||
<script src='../js/id/actions/add_node.js'></script>
|
||||
<script src='../js/id/actions/add_way.js'></script>
|
||||
<script src='../js/id/actions/add_way_node.js'></script>
|
||||
@@ -137,6 +138,7 @@
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/lib/d3.keybinding.js"></script>
|
||||
|
||||
<script src="spec/actions/add_midpoint.js"></script>
|
||||
<script src="spec/actions/add_node.js"></script>
|
||||
<script src="spec/actions/add_way.js"></script>
|
||||
<script src="spec/actions/change_entity_tags.js"></script>
|
||||
@@ -170,6 +172,7 @@
|
||||
<script src="spec/svg/areas.js"></script>
|
||||
<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>
|
||||
@@ -182,6 +185,7 @@
|
||||
<script src="spec/ui/confirm.js"></script>
|
||||
|
||||
<script src="spec/connection.js"></script>
|
||||
<script src="spec/geo.js"></script>
|
||||
<script src="spec/oauth.js"></script>
|
||||
<script src="spec/taginfo.js"></script>
|
||||
<script src="spec/util.js"></script>
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<!-- include spec files here... -->
|
||||
<script src="spec/lib/d3.keybinding.js"></script>
|
||||
|
||||
<script src="spec/actions/add_midpoint.js"></script>
|
||||
<script src="spec/actions/add_node.js"></script>
|
||||
<script src="spec/actions/add_way.js"></script>
|
||||
<script src="spec/actions/change_entity_tags.js"></script>
|
||||
@@ -65,6 +66,7 @@
|
||||
<script src="spec/svg/areas.js"></script>
|
||||
<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>
|
||||
@@ -77,6 +79,7 @@
|
||||
<script src="spec/ui/confirm.js"></script>
|
||||
|
||||
<script src="spec/connection.js"></script>
|
||||
<script src="spec/geo.js"></script>
|
||||
<script src="spec/oauth.js"></script>
|
||||
<script src="spec/taginfo.js"></script>
|
||||
<script src="spec/util.js"></script>
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
describe("iD.actions.AddMidpoint", function () {
|
||||
it("adds the node at the midpoint location", function () {
|
||||
var node = iD.Node(),
|
||||
midpoint = {loc: [1, 2], ways: []},
|
||||
graph = iD.actions.AddMidpoint(midpoint, node)(iD.Graph());
|
||||
|
||||
expect(graph.entity(node.id).loc).to.eql([1, 2]);
|
||||
});
|
||||
|
||||
it("adds the node to all ways at the respective indexes", function () {
|
||||
var node = iD.Node(),
|
||||
a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
w1 = iD.Way(),
|
||||
w2 = iD.Way({nodes: [a.id, b.id]}),
|
||||
midpoint = {loc: [1, 2], ways: [{id: w1.id, index: 0}, {id: w2.id, index: 1}]},
|
||||
graph = iD.actions.AddMidpoint(midpoint, node)(iD.Graph([a, b, w1, w2]));
|
||||
|
||||
expect(graph.entity(w1.id).nodes).to.eql([node.id]);
|
||||
expect(graph.entity(w2.id).nodes).to.eql([a.id, node.id, b.id]);
|
||||
});
|
||||
});
|
||||
@@ -31,8 +31,7 @@ describe("iD.actions.DeleteWay", function () {
|
||||
expect(graph.entity(node.id)).not.to.be.undefined;
|
||||
});
|
||||
|
||||
// See #508
|
||||
xit("deletes multiple member nodes", function () {
|
||||
it("deletes multiple member nodes", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
way = iD.Way({nodes: [a.id, b.id]}),
|
||||
@@ -42,7 +41,7 @@ describe("iD.actions.DeleteWay", function () {
|
||||
expect(graph.entity(b.id)).to.be.undefined;
|
||||
});
|
||||
|
||||
xit("deletes a circular way's start/end node", function () {
|
||||
it("deletes a circular way's start/end node", function () {
|
||||
var a = iD.Node(),
|
||||
b = iD.Node(),
|
||||
c = iD.Node(),
|
||||
|
||||
@@ -26,24 +26,24 @@ describe('iD.Connection', function () {
|
||||
c.loadFromURL('data/node.xml', done);
|
||||
});
|
||||
|
||||
it('returns a graph', function (done) {
|
||||
it('returns an object', function (done) {
|
||||
c.loadFromURL('data/node.xml', function (err, graph) {
|
||||
expect(err).to.not.be.ok;
|
||||
expect(graph).to.be.instanceOf(iD.Graph);
|
||||
expect(typeof graph).to.eql('object');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('parses a node', function (done) {
|
||||
c.loadFromURL('data/node.xml', function (err, graph) {
|
||||
expect(graph.entity('n356552551')).to.be.instanceOf(iD.Entity);
|
||||
expect(graph.n356552551).to.be.instanceOf(iD.Entity);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('parses a way', function (done) {
|
||||
c.loadFromURL('data/way.xml', function (err, graph) {
|
||||
expect(graph.entity('w19698713')).to.be.instanceOf(iD.Entity);
|
||||
expect(graph.w19698713).to.be.instanceOf(iD.Entity);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
describe('iD.geo', function() {
|
||||
describe('.roundCoords', function() {
|
||||
expect(iD.geo.roundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1.1])).to.eql([0, 1]);
|
||||
});
|
||||
|
||||
describe('.interp', function() {
|
||||
it('interpolates halfway', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.geo.interp(a, b, 0.5)).to.eql([5, 5]);
|
||||
});
|
||||
it('interpolates to one side', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.geo.interp(a, b, 0)).to.eql([0, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.dist', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 10 unit line is 10', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(10);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.geo.dist(a, b)).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.pointInPolygon', function() {
|
||||
it('says a point in a polygon is on a polygon', function() {
|
||||
var poly = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
|
||||
var point = [0.5, 0.5];
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.true;
|
||||
});
|
||||
it('says a point outside of a polygon is outside', function() {
|
||||
var poly = [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
[1, 0],
|
||||
[0, 0]];
|
||||
var point = [0.5, 1.5];
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.polygonContainsPolygon', function() {
|
||||
it('says a polygon in a polygon is in', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonContainsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
it('says a polygon outside of a polygon is out', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 9], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonContainsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.polygonIntersectsPolygon', function() {
|
||||
it('says a polygon in a polygon intersects it', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says a polygon that partially intersects does', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[-1, -1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says totally disjoint polygons do not intersect', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[-1, -1], [-1, -2], [-2, -2], [-2, -1], [-1, -1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('.pathLength', function() {
|
||||
it('calculates a simple path length', function() {
|
||||
var path = [[0, 0], [0, 1], [3, 5]];
|
||||
expect(iD.geo.pathLength(path)).to.eql(6);
|
||||
});
|
||||
});
|
||||
});
|
||||
+221
-25
@@ -1,25 +1,164 @@
|
||||
describe('iD.Graph', function() {
|
||||
it("can be constructed with an entities Object", function () {
|
||||
var entity = iD.Entity(),
|
||||
graph = iD.Graph({'n-1': entity});
|
||||
expect(graph.entity('n-1')).to.equal(entity);
|
||||
});
|
||||
|
||||
it("can be constructed with an entities Array", function () {
|
||||
var entity = iD.Entity(),
|
||||
graph = iD.Graph([entity]);
|
||||
expect(graph.entity(entity.id)).to.equal(entity);
|
||||
});
|
||||
|
||||
if (iD.debug) {
|
||||
it("is frozen", function () {
|
||||
expect(Object.isFrozen(iD.Graph())).to.be.true;
|
||||
describe("constructor", function () {
|
||||
it("accepts an entities Object", function () {
|
||||
var entity = iD.Entity(),
|
||||
graph = iD.Graph({'n-1': entity});
|
||||
expect(graph.entity('n-1')).to.equal(entity);
|
||||
});
|
||||
|
||||
it("freezes entities", function () {
|
||||
expect(Object.isFrozen(iD.Graph().entities)).to.be.true;
|
||||
it("accepts an entities Array", function () {
|
||||
var entity = iD.Entity(),
|
||||
graph = iD.Graph([entity]);
|
||||
expect(graph.entity(entity.id)).to.equal(entity);
|
||||
});
|
||||
}
|
||||
|
||||
it("accepts a Graph", function () {
|
||||
var entity = iD.Entity(),
|
||||
graph = iD.Graph(iD.Graph([entity]));
|
||||
expect(graph.entity(entity.id)).to.equal(entity);
|
||||
});
|
||||
|
||||
it("copies other's entities", function () {
|
||||
var entity = iD.Entity(),
|
||||
base = iD.Graph([entity]),
|
||||
graph = iD.Graph(base);
|
||||
expect(graph.entities).not.to.equal(base.entities);
|
||||
});
|
||||
|
||||
it("rebases on other's base", function () {
|
||||
var base = iD.Graph(),
|
||||
graph = iD.Graph(base);
|
||||
expect(graph.base().entities).to.equal(base.base().entities);
|
||||
});
|
||||
|
||||
it("freezes by default", function () {
|
||||
expect(iD.Graph().frozen).to.be.true;
|
||||
});
|
||||
|
||||
it("remains mutable if passed true as second argument", function () {
|
||||
expect(iD.Graph([], true).frozen).not.to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("#freeze", function () {
|
||||
it("sets the frozen flag", function () {
|
||||
expect(iD.Graph([], true).freeze().frozen).to.be.true;
|
||||
});
|
||||
|
||||
if (iD.debug) {
|
||||
it("freezes entities", function () {
|
||||
expect(Object.isFrozen(iD.Graph().entities)).to.be.true;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("#rebase", function () {
|
||||
it("preserves existing entities", function () {
|
||||
var node = iD.Node({id: 'n'}),
|
||||
graph = iD.Graph([node]);
|
||||
graph.rebase({});
|
||||
expect(graph.entity('n')).to.equal(node);
|
||||
});
|
||||
|
||||
it("includes new entities", function () {
|
||||
var node = iD.Node({id: 'n'}),
|
||||
graph = iD.Graph();
|
||||
graph.rebase({'n': node});
|
||||
expect(graph.entity('n')).to.equal(node);
|
||||
});
|
||||
|
||||
it("gives precedence to existing entities", function () {
|
||||
var a = iD.Node({id: 'n'}),
|
||||
b = iD.Node({id: 'n'}),
|
||||
graph = iD.Graph([a]);
|
||||
graph.rebase({'n': b});
|
||||
expect(graph.entity('n')).to.equal(a);
|
||||
});
|
||||
|
||||
it("inherits entities from base prototypally", function () {
|
||||
var graph = iD.Graph();
|
||||
graph.rebase({'n': iD.Node()});
|
||||
expect(graph.entities).not.to.have.ownProperty('n');
|
||||
});
|
||||
|
||||
it("updates parentWays", function () {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
w1 = iD.Way({id: 'w1', nodes: ['n']}),
|
||||
w2 = iD.Way({id: 'w2', nodes: ['n']}),
|
||||
graph = iD.Graph([n, w1]);
|
||||
|
||||
graph.rebase({ 'w2': w2 });
|
||||
expect(graph.parentWays(n)).to.eql([w1, w2]);
|
||||
expect(graph._parentWays.hasOwnProperty('n')).to.be.false;
|
||||
});
|
||||
|
||||
it("avoids adding duplicate parentWays", function () {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
w1 = iD.Way({id: 'w1', nodes: ['n']}),
|
||||
graph = iD.Graph([n, w1]);
|
||||
graph.rebase({ 'w1': w1 });
|
||||
expect(graph.parentWays(n)).to.eql([w1]);
|
||||
});
|
||||
|
||||
it("updates parentWays for nodes with modified parentWays", function () {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
w1 = iD.Way({id: 'w1', nodes: ['n']}),
|
||||
w2 = iD.Way({id: 'w2', nodes: ['n']}),
|
||||
w3 = iD.Way({id: 'w3', nodes: ['n']}),
|
||||
graph = iD.Graph([n, w1]),
|
||||
graph2 = graph.replace(w2);
|
||||
graph.rebase({ 'w3': w3 });
|
||||
graph2.rebase({ 'w3': w3 });
|
||||
|
||||
expect(graph2.parentWays(n)).to.eql([w1, w2, w3]);
|
||||
});
|
||||
|
||||
it("avoids re-adding removed parentWays", function() {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
w1 = iD.Way({id: 'w1', nodes: ['n']}),
|
||||
graph = iD.Graph([n, w1]),
|
||||
graph2 = graph.remove(w1);
|
||||
graph.rebase({ 'w1': w1 });
|
||||
graph2.rebase({ 'w1': w1 });
|
||||
expect(graph2.parentWays(n)).to.eql([]);
|
||||
});
|
||||
|
||||
it("updates parentRelations", function () {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
r1 = iD.Relation({id: 'r1', members: [{id: 'n'}]}),
|
||||
r2 = iD.Relation({id: 'r2', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([n, r1]);
|
||||
|
||||
graph.rebase({'r2': r2});
|
||||
|
||||
expect(graph.parentRelations(n)).to.eql([r1, r2]);
|
||||
expect(graph._parentRels.hasOwnProperty('n')).to.be.false;
|
||||
});
|
||||
|
||||
it("avoids re-adding removed parentRels", function() {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
r1 = iD.Relation({id: 'r1', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([n, r1]),
|
||||
graph2 = graph.remove(r1);
|
||||
graph.rebase({ 'w1': r1 });
|
||||
graph2.rebase({ 'w1': r1 });
|
||||
expect(graph2.parentWays(n)).to.eql([]);
|
||||
});
|
||||
|
||||
it("updates parentRels for nodes with modified parentWays", function () {
|
||||
var n = iD.Node({id: 'n'}),
|
||||
r1 = iD.Relation({id: 'r1', members: [{id: 'n'}]}),
|
||||
r2 = iD.Relation({id: 'r2', members: [{id: 'n'}]}),
|
||||
r3 = iD.Relation({id: 'r3', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([n, r1]),
|
||||
graph2 = graph.replace(r2);
|
||||
|
||||
graph.rebase({'r3': r3});
|
||||
graph2.rebase({'r3': r3});
|
||||
expect(graph2.parentRelations(n)).to.eql([r1, r2, r3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#remove", function () {
|
||||
it("returns a new graph", function () {
|
||||
@@ -40,6 +179,20 @@ describe('iD.Graph', function() {
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.remove(node).entity(node.id)).to.be.undefined;
|
||||
});
|
||||
|
||||
it("removes the entity as a parentWay", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
w1 = iD.Way({id: 'w', nodes: ['n']}),
|
||||
graph = iD.Graph([node, w1]);
|
||||
expect(graph.remove(w1).parentWays(node)).to.eql([]);
|
||||
});
|
||||
|
||||
it("removes the entity as a parentRelation", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
r1 = iD.Relation({id: 'w', members: [{id: 'n' }]}),
|
||||
graph = iD.Graph([node, r1]);
|
||||
expect(graph.remove(r1).parentRelations(node)).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#replace", function () {
|
||||
@@ -62,6 +215,49 @@ describe('iD.Graph', function() {
|
||||
graph = iD.Graph([node1]);
|
||||
expect(graph.replace(node2).entity(node2.id)).to.equal(node2);
|
||||
});
|
||||
|
||||
it("adds parentWays", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
w1 = iD.Way({id: 'w', nodes: ['n']}),
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.replace(w1).parentWays(node)).to.eql([w1]);
|
||||
});
|
||||
|
||||
it("removes parentWays", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
w1 = iD.Way({id: 'w', nodes: ['n']}),
|
||||
graph = iD.Graph([node, w1]);
|
||||
expect(graph.remove(w1).parentWays(node)).to.eql([]);
|
||||
});
|
||||
|
||||
it("doesn't add duplicate parentWays", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
w1 = iD.Way({id: 'w', nodes: ['n']}),
|
||||
graph = iD.Graph([node, w1]);
|
||||
expect(graph.replace(w1).parentWays(node)).to.eql([w1]);
|
||||
});
|
||||
|
||||
it("adds parentRels", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
r1 = iD.Relation({id: 'w', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([node]);
|
||||
expect(graph.replace(r1).parentRelations(node)).to.eql([r1]);
|
||||
});
|
||||
|
||||
it("removes parentRelations", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
r1 = iD.Relation({id: 'w', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([node, r1]);
|
||||
expect(graph.remove(r1).parentRelations(node)).to.eql([]);
|
||||
});
|
||||
|
||||
it("doesn't add duplicate parentRelations", function () {
|
||||
var node = iD.Node({id: 'n' }),
|
||||
r1 = iD.Relation({id: 'w', members: [{id: 'n'}]}),
|
||||
graph = iD.Graph([node, r1]);
|
||||
expect(graph.replace(r1).parentRelations(node)).to.eql([r1]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("#update", function () {
|
||||
@@ -159,18 +355,18 @@ describe('iD.Graph', function() {
|
||||
|
||||
describe("#modified", function () {
|
||||
it("returns an Array of ids of modified entities", function () {
|
||||
var node1 = iD.Node({id: 'n1', _updated: true}),
|
||||
node2 = iD.Node({id: 'n2'}),
|
||||
graph = iD.Graph([node1, node2]);
|
||||
expect(graph.modified()).to.eql([node1.id]);
|
||||
var node = iD.Node({id: 'n1'}),
|
||||
node_ = iD.Node({id: 'n1'}),
|
||||
graph = iD.Graph([node]).replace(node_);
|
||||
expect(graph.modified()).to.eql([node.id]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#created", function () {
|
||||
it("returns an Array of ids of created entities", function () {
|
||||
var node1 = iD.Node({id: 'n-1', _updated: true}),
|
||||
var node1 = iD.Node({id: 'n-1'}),
|
||||
node2 = iD.Node({id: 'n2'}),
|
||||
graph = iD.Graph([node1, node2]);
|
||||
graph = iD.Graph([node2]).replace(node1);
|
||||
expect(graph.created()).to.eql([node1.id]);
|
||||
});
|
||||
});
|
||||
@@ -185,7 +381,7 @@ describe('iD.Graph', function() {
|
||||
|
||||
it("doesn't include created entities that were subsequently deleted", function () {
|
||||
var node = iD.Node(),
|
||||
graph = iD.Graph([node]).remove(node);
|
||||
graph = iD.Graph().replace(node).remove(node);
|
||||
expect(graph.deleted()).to.eql([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -153,17 +153,15 @@ describe("iD.History", function () {
|
||||
|
||||
it("includes modified entities", function () {
|
||||
var node1 = iD.Node({id: "n1"}),
|
||||
node2 = node1.update({}),
|
||||
graph = iD.Graph([node1]);
|
||||
history.merge(graph);
|
||||
node2 = node1.update({});
|
||||
history.merge({ n1: node1});
|
||||
history.perform(function (graph) { return graph.replace(node2); });
|
||||
expect(history.changes().modified).to.eql([node2]);
|
||||
});
|
||||
|
||||
it("includes deleted entities", function () {
|
||||
var node = iD.Node({id: "n1"}),
|
||||
graph = iD.Graph([node]);
|
||||
history.merge(graph);
|
||||
var node = iD.Node({id: "n1"});
|
||||
history.merge({ n1: node });
|
||||
history.perform(function (graph) { return graph.remove(node); });
|
||||
expect(history.changes().deleted).to.eql([node]);
|
||||
});
|
||||
@@ -189,7 +187,7 @@ describe("iD.History", function () {
|
||||
it("is the sum of all types of changes", function() {
|
||||
var node1 = iD.Node({id: "n1"}),
|
||||
node2 = iD.Node();
|
||||
history.merge(iD.Graph([node1]));
|
||||
history.merge({ n1: node1 });
|
||||
history.perform(function (graph) { return graph.remove(node1); });
|
||||
expect(history.numChanges()).to.eql(1);
|
||||
history.perform(function (graph) { return graph.replace(node2); });
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
describe("iD.svg.Midpoints", 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("finds the location of the midpoints", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [50, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
graph = iD.Graph([a, b, line]);
|
||||
|
||||
surface.call(iD.svg.Midpoints(projection), graph, [line], filter);
|
||||
|
||||
expect(surface.select('.midpoint').datum().loc).to.eql([25, 0]);
|
||||
});
|
||||
|
||||
it("doesn't create midpoints on segments with pixel length less than 40", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [39, 0]}),
|
||||
line = iD.Way({nodes: [a.id, b.id]}),
|
||||
graph = iD.Graph([a, b, line]);
|
||||
|
||||
surface.call(iD.svg.Midpoints(projection), graph, [line], filter);
|
||||
|
||||
expect(surface.selectAll('.midpoint')[0]).to.have.length(0);
|
||||
});
|
||||
|
||||
it("binds a datum whose 'ways' property lists ways which include the segement", function () {
|
||||
var a = iD.Node({loc: [0, 0]}),
|
||||
b = iD.Node({loc: [50, 0]}),
|
||||
c = iD.Node({loc: [1, 1]}),
|
||||
d = iD.Node({loc: [2, 2]}),
|
||||
l1 = iD.Way({nodes: [a.id, b.id]}),
|
||||
l2 = iD.Way({nodes: [b.id, a.id]}),
|
||||
l3 = iD.Way({nodes: [c.id, a.id, b.id, d.id]}),
|
||||
l4 = iD.Way({nodes: [a.id, d.id, b.id]}),
|
||||
graph = iD.Graph([a, b, c, d, l1, l2, l3, l4]),
|
||||
ab = function (d) { return d.id === [a.id, b.id].sort().join("-"); };
|
||||
|
||||
surface.call(iD.svg.Midpoints(projection), graph, [l1, l2, l3, l4], filter);
|
||||
|
||||
expect(surface.selectAll('.midpoint').filter(ab).datum().ways).to.eql([
|
||||
{id: l1.id, index: 1},
|
||||
{id: l2.id, index: 1},
|
||||
{id: l3.id, index: 2}]);
|
||||
});
|
||||
});
|
||||
+4
-103
@@ -1,123 +1,24 @@
|
||||
describe('iD.Util', function() {
|
||||
var util;
|
||||
|
||||
it('#trueObj', function() {
|
||||
it('.trueObj', function() {
|
||||
expect(iD.util.trueObj(['a', 'b', 'c'])).to.eql({ a: true, b: true, c: true });
|
||||
expect(iD.util.trueObj([])).to.eql({});
|
||||
});
|
||||
|
||||
it('#tagText', function() {
|
||||
it('.tagText', function() {
|
||||
expect(iD.util.tagText({})).to.eql('');
|
||||
expect(iD.util.tagText({tags:{foo:'bar'}})).to.eql('foo: bar');
|
||||
expect(iD.util.tagText({tags:{foo:'bar',two:'three'}})).to.eql('foo: bar\ntwo: three');
|
||||
});
|
||||
|
||||
it('#stringQs', function() {
|
||||
it('.stringQs', function() {
|
||||
expect(iD.util.stringQs('foo=bar')).to.eql({foo: 'bar'});
|
||||
expect(iD.util.stringQs('foo=bar&one=2')).to.eql({foo: 'bar', one: '2' });
|
||||
expect(iD.util.stringQs('')).to.eql({});
|
||||
});
|
||||
|
||||
it('#qsString', function() {
|
||||
it('.qsString', function() {
|
||||
expect(iD.util.qsString({ foo: 'bar' })).to.eql('foo=bar');
|
||||
expect(iD.util.qsString({ foo: 'bar', one: 2 })).to.eql('foo=bar&one=2');
|
||||
expect(iD.util.qsString({})).to.eql('');
|
||||
});
|
||||
|
||||
describe('geo', function() {
|
||||
describe('#roundCoords', function() {
|
||||
expect(iD.geo.roundCoords([0.1, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1])).to.eql([0, 1]);
|
||||
expect(iD.geo.roundCoords([0, 1.1])).to.eql([0, 1]);
|
||||
});
|
||||
|
||||
describe('#interp', function() {
|
||||
it('interpolates halfway', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.geo.interp(a, b, 0.5)).to.eql([5, 5]);
|
||||
});
|
||||
it('interpolates to one side', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 10];
|
||||
expect(iD.geo.interp(a, b, 0)).to.eql([0, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#dist', function() {
|
||||
it('distance between two same points is zero', function() {
|
||||
var a = [0, 0],
|
||||
b = [0, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(0);
|
||||
});
|
||||
it('a straight 10 unit line is 10', function() {
|
||||
var a = [0, 0],
|
||||
b = [10, 0];
|
||||
expect(iD.geo.dist(a, b)).to.eql(10);
|
||||
});
|
||||
it('a pythagorean triangle is right', function() {
|
||||
var a = [0, 0],
|
||||
b = [4, 3];
|
||||
expect(iD.geo.dist(a, b)).to.eql(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#pointInPolygon', function() {
|
||||
it('says a point in a polygon is on a polygon', function() {
|
||||
var poly = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]];
|
||||
var point = [0.5, 0.5];
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.true;
|
||||
});
|
||||
it('says a point outside of a polygon is outside', function() {
|
||||
var poly = [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
[1, 0],
|
||||
[0, 0]];
|
||||
var point = [0.5, 1.5];
|
||||
expect(iD.geo.pointInPolygon(point, poly)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#polygonContainsPolygon', function() {
|
||||
it('says a polygon in a polygon is in', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonContainsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
it('says a polygon outside of a polygon is out', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 9], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonContainsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#polygonIntersectsPolygon', function() {
|
||||
it('says a polygon in a polygon intersects it', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[1, 1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says a polygon that partially intersects does', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[-1, -1], [1, 2], [2, 2], [2, 1], [1, 1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.true;
|
||||
});
|
||||
|
||||
it('says totally disjoint polygons do not intersect', function() {
|
||||
var outer = [[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]];
|
||||
var inner = [[-1, -1], [-1, -2], [-2, -2], [-2, -1], [-1, -1]];
|
||||
expect(iD.geo.polygonIntersectsPolygon(outer, inner)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#pathLength', function() {
|
||||
it('calculates a simple path length', function() {
|
||||
var path = [[0, 0], [0, 1], [3, 5]];
|
||||
expect(iD.geo.pathLength(path)).to.eql(6);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user