Merge branch 'turn-restrictions'

This commit is contained in:
John Firebaugh
2014-05-21 14:22:22 -07:00
58 changed files with 2252 additions and 792 deletions
+3
View File
@@ -124,3 +124,6 @@ D3_FILES = \
js/lib/d3.v3.js: $(D3_FILES)
node_modules/.bin/smash $(D3_FILES) > $@
@echo 'd3 rebuilt. Please reapply 7e2485d, 4da529f, and 223974d'
js/lib/lodash.js:
node_modules/.bin/lodash --debug --output $@ include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick" exports="global,node"
+27 -4
View File
@@ -39,6 +39,13 @@ body {
height: 100%;
}
#defs {
/* Can't be display: none or the clippaths are ignored. */
position: absolute;
width: 0;
height: 0;
}
.spacer {
height: 40px;
margin-right: 10px;
@@ -1527,6 +1534,26 @@ input[type=number] {
border-bottom: 0;
border-radius: 0 0 4px 0;
}
/* Restrictions editor */
.form-field-restrictions .preset-input-wrap {
position: relative;
height: 300px;
}
.form-field-restrictions .restriction-help {
z-index: 1;
position: absolute;
top: 0;
left: 0;
right: 0;
padding: 2px 6px;
background-color: rgba(255, 255, 255, .8);
color: #999;
text-align: center;
}
/* combobox dropdown */
div.combobox {
@@ -2749,10 +2776,6 @@ img.wiki-image {
fill: rgba(255,255,255,.5);
}
.radial-menu .icon {
pointer-events: none;
}
.lasso-box {
fill-opacity:0.1;
stroke: #fff;
+7 -4
View File
@@ -113,10 +113,6 @@
.feature-laundry{background-position:-108px -504px;}
.feature-car{background-position:-162px -504px;}
.feature-suitcase{background-position:-216px -504px;}
.feature-hairdresser{background-position:-0px -528px;}
.feature-chemist{background-position:-54px -528px;}
.feature-mobilephone{background-position:-108px -528px;}
.feature-scooter{background-position:-162px -528px;}
.preset-icon-line.feature-highway-motorway{background-position:-20px -25px;}
.preset-icon-line.feature-highway-trunk{background-position:-80px -25px;}
.preset-icon-line.feature-highway-primary{background-position:-140px -25px;}
@@ -172,3 +168,10 @@
.preset-icon-relation.feature-route-power{background-position:-800px -25px;}
.preset-icon-relation.feature-route-pipeline{background-position:-860px -25px;}
.preset-icon-relation.feature-route-master{background-position:-920px -25px;}
.preset-icon-relation.feature-restriction-no-straight-on{background-position:-980px -25px;}
.preset-icon-relation.feature-restriction-no-u-turn{background-position:-1040px -25px;}
.preset-icon-relation.feature-restriction-no-left-turn{background-position:-1100px -25px;}
.preset-icon-relation.feature-restriction-no-right-turn{background-position:-1160px -25px;}
.preset-icon-relation.feature-restriction-only-straight-ahead{background-position:-1220px -25px;}
.preset-icon-relation.feature-restriction-only-left-turn{background-position:-1280px -25px;}
.preset-icon-relation.feature-restriction-only-right-turn{background-position:-1340px -25px;}
+23
View File
@@ -945,6 +945,19 @@ text.point {
font-size: 10px;
}
/* Turns */
g.turn rect,
g.turn circle {
fill: none;
pointer-events: all;
}
.form-field-restrictions .vertex {
pointer-events: none;
cursor: auto !important;
}
/* Cursors */
#map {
@@ -1092,6 +1105,16 @@ text.point {
) 9 9, crosshair;
}
.turn rect,
.turn circle {
cursor: pointer; /* Opera */
cursor: url(img/cursor-pointer.png) 6 1, pointer; /* FF */
cursor: -webkit-image-set(
url(img/cursor-pointer.png) 1x,
url(img/cursor-pointer2x.png) 2x
) 6 1, pointer;
}
.lasso #map {
pointer-events: visibleStroke;
}
+9
View File
@@ -145,6 +145,15 @@ en:
multiple: "Split {n} lines/area boundaries."
not_eligible: Lines can't be split at their beginning or end.
multiple_ways: There are too many lines here to split.
restriction:
help:
select: Click to select a road segment.
toggle: Click to toggle turn restrictions.
toggle_on: 'Click to add a "{restriction}" restriction.'
toggle_off: 'Click to remove the "{restriction}" restriction.'
annotation:
create: Added a turn restriction
delete: Deleted a turn restriction
undo:
tooltip: "Undo: {action}"
nothing: Nothing to undo.
File diff suppressed because one or more lines are too long
+8 -1
View File
@@ -23,5 +23,12 @@
"icon-operation-disabled-orthogonalize": [160, 160],
"icon-operation-disabled-rotate": [180, 160],
"icon-operation-disabled-simplify": [200, 160],
"icon-operation-disabled-continue": [220, 160]
"icon-operation-disabled-continue": [220, 160],
"icon-restriction-yes": [50, 80],
"icon-restriction-no": [95, 80],
"icon-restriction-only": [140, 80],
"icon-restriction-yes-u": [185, 80],
"icon-restriction-no-u": [230, 80],
"icon-restriction-only-u": [275, 80]
}
+27
View File
@@ -11,6 +11,8 @@ en:
name: Path
"category-rail":
name: Rail
"category-restriction":
name: Restriction
"category-road":
name: Road
"category-route":
@@ -138,6 +140,8 @@ en:
label: Emergency
entrance:
label: Type
except:
label: Exceptions
fax:
label: Fax
placeholder: +31 42 123 4567
@@ -294,6 +298,8 @@ en:
taoist: Taoist
restriction:
label: Type
restrictions:
label: Turn Restrictions
route:
label: Type
route_master:
@@ -1795,6 +1801,27 @@ en:
type/restriction:
name: Restriction
terms: "<translate with synonyms or related terms for 'Restriction', separated by commas>"
type/restriction/no_left_turn:
name: No Left Turn
terms: "<translate with synonyms or related terms for 'No Left Turn', separated by commas>"
type/restriction/no_right_turn:
name: No Right Turn
terms: "<translate with synonyms or related terms for 'No Right Turn', separated by commas>"
type/restriction/no_straight_on:
name: No Straight On
terms: "<translate with synonyms or related terms for 'No Straight On', separated by commas>"
type/restriction/no_u_turn:
name: "No U-turn"
terms: "<translate with synonyms or related terms for 'No U-turn', separated by commas>"
type/restriction/only_left_turn:
name: Left Turn Only
terms: "<translate with synonyms or related terms for 'Left Turn Only', separated by commas>"
type/restriction/only_right_turn:
name: Right Turn Only
terms: "<translate with synonyms or related terms for 'Right Turn Only', separated by commas>"
type/restriction/only_straight_ahead:
name: No Turns
terms: "<translate with synonyms or related terms for 'No Turns', separated by commas>"
type/route:
name: Route
terms: "<translate with synonyms or related terms for 'Route', separated by commas>"
+15
View File
@@ -68,6 +68,21 @@
"railway/abandoned"
]
},
"category-restriction": {
"geometry": "relation",
"name": "Restriction",
"icon": "restriction",
"members": [
"type/restriction/no_left_turn",
"type/restriction/no_right_turn",
"type/restriction/no_straight_on",
"type/restriction/no_u_turn",
"type/restriction/only_left_turn",
"type/restriction/only_right_turn",
"type/restriction/only_straight_ahead",
"type/restriction"
]
},
"category-road": {
"geometry": "line",
"name": "Road",
+15
View File
@@ -0,0 +1,15 @@
{
"geometry": "relation",
"name": "Restriction",
"icon": "restriction",
"members": [
"type/restriction/no_left_turn",
"type/restriction/no_right_turn",
"type/restriction/no_straight_on",
"type/restriction/no_u_turn",
"type/restriction/only_left_turn",
"type/restriction/only_right_turn",
"type/restriction/only_straight_ahead",
"type/restriction"
]
}
+1 -1
View File
@@ -40,8 +40,8 @@
],
"relation": [
"category-route",
"category-restriction",
"type/boundary",
"type/restriction",
"type/multipolygon",
"relation"
]
+14
View File
@@ -320,6 +320,11 @@
"type": "typeCombo",
"label": "Type"
},
"except": {
"key": "except",
"type": "combo",
"label": "Exceptions"
},
"fax": {
"key": "fax",
"type": "tel",
@@ -716,6 +721,15 @@
"type": "combo",
"label": "Type"
},
"restrictions": {
"type": "restrictions",
"geometry": "vertex",
"icon": "restrictions",
"reference": {
"rtype": "restriction"
},
"label": "Turn Restrictions"
},
"route": {
"key": "route",
"type": "combo",
+5
View File
@@ -0,0 +1,5 @@
{
"key": "except",
"type": "combo",
"label": "Exceptions"
}
+9
View File
@@ -0,0 +1,9 @@
{
"type": "restrictions",
"geometry": "vertex",
"icon": "restrictions",
"reference": {
"rtype": "restriction"
},
"label": "Turn Restrictions"
}
+100 -1
View File
@@ -7987,9 +7987,108 @@
"name": "Restriction",
"icon": "restriction",
"fields": [
"restriction"
"restriction",
"except"
]
},
"type/restriction/no_left_turn": {
"name": "No Left Turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_left_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-left-turn"
},
"type/restriction/no_right_turn": {
"name": "No Right Turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_right_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-right-turn"
},
"type/restriction/no_straight_on": {
"name": "No Straight On",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_straight_on"
},
"fields": [
"except"
],
"icon": "restriction-no-straight-on"
},
"type/restriction/no_u_turn": {
"name": "No U-turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_u_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-u-turn"
},
"type/restriction/only_left_turn": {
"name": "Left Turn Only",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_left_turn"
},
"fields": [
"except"
],
"icon": "restriction-only-left-turn"
},
"type/restriction/only_right_turn": {
"name": "Right Turn Only",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_right_turn"
},
"fields": [
"except"
],
"icon": "restriction-only-right-turn"
},
"type/restriction/only_straight_ahead": {
"name": "No Turns",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_straight_ahead"
},
"fields": [
"except"
],
"icon": "restriction-only-straight-ahead"
},
"type/route": {
"geometry": [
"relation"
+2 -1
View File
@@ -8,6 +8,7 @@
"name": "Restriction",
"icon": "restriction",
"fields": [
"restriction"
"restriction",
"except"
]
}
@@ -0,0 +1,14 @@
{
"name": "No Left Turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_left_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-left-turn"
}
@@ -0,0 +1,14 @@
{
"name": "No Right Turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_right_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-right-turn"
}
@@ -0,0 +1,14 @@
{
"name": "No Straight On",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_straight_on"
},
"fields": [
"except"
],
"icon": "restriction-no-straight-on"
}
@@ -0,0 +1,14 @@
{
"name": "No U-turn",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "no_u_turn"
},
"fields": [
"except"
],
"icon": "restriction-no-u-turn"
}
@@ -0,0 +1,14 @@
{
"name": "Left Turn Only",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_left_turn"
},
"fields": [
"except"
],
"icon": "restriction-only-left-turn"
}
@@ -0,0 +1,14 @@
{
"name": "Right Turn Only",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_right_turn"
},
"fields": [
"except"
],
"icon": "restriction-only-right-turn"
}
@@ -0,0 +1,14 @@
{
"name": "No Turns",
"geometry": [
"relation"
],
"tags": {
"type": "restriction",
"restriction": "only_straight_ahead"
},
"fields": [
"except"
],
"icon": "restriction-only-straight-ahead"
}
+2 -1
View File
@@ -63,7 +63,8 @@
"textarea",
"localized",
"wikipedia",
"typeCombo"
"typeCombo",
"restrictions"
],
"required": true
},
+8 -1
View File
@@ -14,5 +14,12 @@
"route-ferry": [740, 25],
"route-power": [800, 25],
"route-pipeline": [860, 25],
"route-master": [920, 25]
"route-master": [920, 25],
"restriction-no-straight-on": [980, 25],
"restriction-no-u-turn": [1040, 25],
"restriction-no-left-turn": [1100, 25],
"restriction-no-right-turn": [1160, 25],
"restriction-only-straight-ahead": [1220, 25],
"restriction-only-left-turn": [1280, 25],
"restriction-only-right-turn": [1340, 25]
}
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 107 KiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 22 KiB

+160 -8
View File
@@ -27,20 +27,20 @@
inkscape:window-width="1440"
inkscape:window-height="856"
id="namedview392"
showgrid="false"
inkscape:zoom="11.313708"
inkscape:cx="19.712517"
inkscape:cy="454.54715"
inkscape:window-x="298"
inkscape:window-y="6"
showgrid="true"
inkscape:zoom="2.8284271"
inkscape:cx="186.53252"
inkscape:cy="447.46176"
inkscape:window-x="276"
inkscape:window-y="71"
inkscape:window-maximized="0"
inkscape:current-layer="svg12393"
showguides="false"
showguides="true"
inkscape:guide-bbox="true"
inkscape:snap-global="true"
inkscape:snap-bbox="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-nodes="false">
inkscape:snap-nodes="true">
<inkscape:grid
type="xygrid"
id="grid6326"
@@ -48,6 +48,22 @@
visible="true"
enabled="true"
snapvisiblegridlinesonly="true" />
<sodipodi:guide
position="0,0"
orientation="0,800"
id="guide4211" />
<sodipodi:guide
position="800,0"
orientation="-560,0"
id="guide4213" />
<sodipodi:guide
position="800,560"
orientation="0,-800"
id="guide4215" />
<sodipodi:guide
position="0,560"
orientation="560,0"
id="guide4217" />
</sodipodi:namedview>
<defs
id="defs12395">
@@ -1881,4 +1897,140 @@
transform="matrix(0,1,-1,0,0,0)"
rx="0.5"
ry="0.5" />
<g
id="g3360"
transform="matrix(0,1,-1,0,45,203)">
<path
sodipodi:nodetypes="ssccccccsscccccss"
inkscape:connector-curvature="0"
id="path12182"
d="m -111,-49 c -2,0 -3.2899,1.14974 -4,2 l -7.03125,8.41907 c -1.27939,1.51205 -1.17862,4.00251 0.21875,5.40625 l 1,1 c 0.73414,0.74243 1.76843,1.17913 2.8125,1.1875 l 0,4.375 c -2.98748,2.1875 -4.99995,5.67161 -5,9.625 6e-5,6.58002 5.41995,11.98722 12,11.98718 6.58003,10e-6 12,-5.40714 12,-11.98718 -1e-5,-3.96861 -1.99295,-7.47078 -5,-9.65625 l 0,-4.34375 c 1.05451,-1.8e-4 2.10227,-0.43771 2.84375,-1.1875 l 1,-1 c 1.39737,-1.40374 1.49814,-3.8942 0.21875,-5.40625 L -107,-47 c -0.77221,-0.92054 -2,-2 -4,-2 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
sodipodi:nodetypes="ccccccssccccc"
inkscape:connector-curvature="0"
id="path12188"
d="m -111,-45 -8,9.01282 1,1 4,0 0,10.59375 c -2.92023,1.19221 -4.99998,4.05725 -5,7.40625 4e-5,4.41829 3.58172,7.98721 8,7.98718 4.41827,10e-6 8,-3.5689 8,-7.98718 2e-5,-3.36351 -2.05966,-6.25472 -5,-7.4375 l 0,-10.5625 4,0 1,-1 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
inkscape:connector-curvature="0"
id="path12196"
d="m -110.98512,-19.99704 c 1.65685,0 3,1.34313 2.99999,3.00001 -1e-5,1.65687 -1.34312,3 -2.99999,3.00002 -1.65687,-3e-5 -2.99999,-1.34314 -2.99999,-3.00001 -10e-6,-1.6569 1.34312,-3 2.99999,-3.00002 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
</g>
<g
id="g4219">
<path
sodipodi:nodetypes="ssccccccsscccccss"
inkscape:connector-curvature="0"
id="path12182-9"
d="m 139,92 c 0,-2 -1.14974,-3.2899 -2,-4 l -8.41907,-7.03125 c -1.51205,-1.27939 -4.00251,-1.17862 -5.40625,0.21875 l -1,1 c -0.74243,0.73414 -1.17913,1.76843 -1.1875,2.8125 l -4.375,0 c -2.1875,-2.98748 -5.67161,-4.99995 -9.625,-5 -6.58002,6e-5 -11.98722,5.41995 -11.98718,12 -1e-5,6.58003 5.40714,12 11.98718,12 3.96861,-1e-5 7.47078,-1.99295 9.65625,-5 l 4.34375,0 c 1.8e-4,1.05451 0.43771,2.10227 1.1875,2.84375 l 1,1 c 1.40374,1.39737 3.8942,1.49814 5.40625,0.21875 L 137,96 c 0.92054,-0.77221 2,-2 2,-4 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
sodipodi:nodetypes="ccccccssccccc"
inkscape:connector-curvature="0"
id="path12188-3"
d="m 135,92 -9.01282,-8 -1,1 0,4 -10.59375,0 c -1.19221,-2.92023 -4.05725,-4.99998 -7.40625,-5 -4.41829,4e-5 -7.98721,3.58172 -7.98718,8 -1e-5,4.41827 3.5689,8 7.98718,8 3.36351,2e-5 6.25472,-2.05966 7.4375,-5 l 10.5625,0 0,4 1,1 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
sodipodi:nodetypes="ccccccccc"
inkscape:connector-curvature="0"
id="rect10944"
d="m 109,88 0,8 -1,1 -2,0 -1,-1 0,-8 1,-1 2,0 z"
style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="g4190"
transform="matrix(0,1,1,0,116.9905,-80.00893)">
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m 156,80 c -8.78916,1e-5 -16,7.21083 -16,16 1e-5,8.78916 7.21083,16 16,16 8.78916,-1e-5 16,-7.21083 16,-16 -1e-5,-8.78916 -7.21083,-16 -16,-16 z"
id="path4163"
transform="matrix(0,1,1,0,80.00893,-71.9905)"
inkscape:connector-curvature="0" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#8cd05f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m 176.00893,72.0095 c -6.62739,0 -11.99999,5.37261 -12,12 0,6.62739 5.37261,11.99999 12,12 6.62739,0 11.99999,-5.37261 12,-12 0,-6.62739 -5.37261,-11.99999 -12,-12 z"
id="path11970"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccccccccccccccccccc"
d="m 184.00894,85.00953 -2.99999,-4e-5 -3e-5,-2.99998 -0.99997,-2.00006 -1.00001,-0.99998 -2.00002,-0.99997 -3.00003,3e-5 -1.99994,0.99998 -1.00004,0.99999 -0.99998,2.00003 -1e-5,4.99996 1.00004,1.00001 0.99996,1e-5 1,-1 2e-5,-4.99996 0.99999,-1.00004 2.99997,0 1.00003,0.99999 -1e-5,3 -3.00003,-3e-5 4.50003,5.00003 z"
id="path2997-6-6"
style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
<g
id="g4206"
transform="matrix(0,1,1,0,167,-115)">
<path
sodipodi:nodetypes="sssss"
inkscape:connector-curvature="0"
id="path13862"
d="m 211,63 c -8.78917,0 -16.00001,7.21082 -16,16 0,8.78917 7.21082,16.00001 16,16 8.78917,0 16.00001,-7.21082 16,-16 0,-8.78917 -7.21082,-16.00001 -16,-16 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
inkscape:connector-curvature="0"
id="path12055"
d="m 211,67 c -6.62739,0 -12.00001,5.37261 -12,12 0,6.62739 5.37261,12.00001 12,12 6.62739,0 12.00001,-5.37261 12,-12 0,-6.62739 -5.37261,-12.00001 -12,-12 z"
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
inkscape:connector-curvature="0"
id="path12057"
d="m 215.5,72.13782 -5,5 -2,2 -4,4 0,1 1,1 1,0 4,-4 2,-2 5,-5 0,-1 -1,-1 -1,0 z m -6.5,0.875 -2,1 -1,1 -1,2 0,4.1875 2.78125,-2.78125 0.21875,-0.21875 0,-1.1875 1,-1 1.1875,0 2.59375,-2.59375 L 212,73.01282 l -3,0 z m 7,4.03125 -2.78125,2.78125 -0.21875,0.1875 -1.5625,1.59375 3.0625,3.40625 4.5,-5 -3,0 0,-2.96875 z"
style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
transform="matrix(0,1,-1,0,135,203)"
id="g3350">
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m -111,-49 c -2,0 -3.2899,1.14974 -4,2 l -7.03125,8.41907 c -1.27939,1.51205 -1.17862,4.00251 0.21875,5.40625 l 1,1 c 0.73414,0.74243 1.76843,1.17913 2.8125,1.1875 l 0,4.375 c -2.98748,2.1875 -4.99995,5.67161 -5,9.625 6e-5,6.58002 5.41995,11.98722 12,11.98718 6.58003,10e-6 12,-5.40714 12,-11.98718 -1e-5,-3.96861 -1.99295,-7.47078 -5,-9.65625 l 0,-4.34375 c 1.05451,-1.8e-4 2.10227,-0.43771 2.84375,-1.1875 l 1,-1 c 1.39737,-1.40374 1.49814,-3.8942 0.21875,-5.40625 L -107,-47 c -0.77221,-0.92054 -2,-2 -4,-2 z"
id="path3352"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssccccccsscccccss" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m -111,-45 -8,9.01282 1,1 4,0 0,10.59375 c -2.92023,1.19221 -4.99998,4.05725 -5,7.40625 4e-5,4.41829 3.58172,7.98721 8,7.98718 4.41827,10e-6 8,-3.5689 8,-7.98718 2e-5,-3.36351 -2.05966,-6.25472 -5,-7.4375 l 0,-10.5625 4,0 1,-1 z"
id="path3354"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccssccccc" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m -110.98512,-19.99704 c 1.65685,0 3,1.34313 2.99999,3.00001 -1e-5,1.65687 -1.34312,3 -2.99999,3.00002 -1.65687,-3e-5 -2.99999,-1.34314 -2.99999,-3.00001 -10e-6,-1.6569 1.34312,-3 2.99999,-3.00002 z"
id="path3356"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
style="font-size:10px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-indent:0;text-align:center;text-decoration:none;line-height:150%;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:middle;baseline-shift:baseline;opacity:0.50000000000000000;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Open Sans;-inkscape-font-specification:Open Sans Bold"
x="254.91199"
y="126.18999"
id="text3358"
sodipodi:linespacing="150%"><tspan
sodipodi:role="line"
id="tspan3360"
x="254.91199"
y="126.18999">ONLY</tspan></text>
<g
id="g4190-7"
transform="matrix(0,1,1,0,206.9905,-80.00893)">
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:8;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m 156,80 c -8.78916,1e-5 -16,7.21083 -16,16 1e-5,8.78916 7.21083,16 16,16 8.78916,-1e-5 16,-7.21083 16,-16 -1e-5,-8.78916 -7.21083,-16 -16,-16 z"
id="path4163-9"
transform="matrix(0,1,1,0,80.00893,-71.9905)"
inkscape:connector-curvature="0" />
<path
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#7092ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.25000000000000000;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
d="m 176.00893,72.0095 c -6.62739,0 -11.99999,5.37261 -12,12 0,6.62739 5.37261,11.99999 12,12 6.62739,0 11.99999,-5.37261 12,-12 0,-6.62739 -5.37261,-11.99999 -12,-12 z"
id="path11970-3"
inkscape:connector-curvature="0" />
<path
sodipodi:nodetypes="cccccccccccccccccccccc"
d="m 184.00894,85.00953 -2.99999,-4e-5 -3e-5,-2.99998 -0.99997,-2.00006 -1.00001,-0.99998 -2.00002,-0.99997 -3.00003,3e-5 -1.99994,0.99998 -1.00004,0.99999 -0.99998,2.00003 -1e-5,4.99996 1.00004,1.00001 0.99996,1e-5 1,-1 2e-5,-4.99996 0.99999,-1.00004 2.99997,0 1.00003,0.99999 -1e-5,3 -3.00003,-3e-5 4.50003,5.00003 z"
id="path2997-6-6-8"
style="opacity:0.5;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 176 KiB

+49
View File
@@ -182,6 +182,18 @@
},
"not_eligible": "Lines can't be split at their beginning or end.",
"multiple_ways": "There are too many lines here to split."
},
"restriction": {
"help": {
"select": "Click to select a road segment.",
"toggle": "Click to toggle turn restrictions.",
"toggle_on": "Click to add a \"{restriction}\" restriction.",
"toggle_off": "Click to remove the \"{restriction}\" restriction."
},
"annotation": {
"create": "Added a turn restriction",
"delete": "Deleted a turn restriction"
}
}
},
"undo": {
@@ -416,6 +428,9 @@
"category-rail": {
"name": "Rail"
},
"category-restriction": {
"name": "Restriction"
},
"category-road": {
"name": "Road"
},
@@ -598,6 +613,9 @@
"entrance": {
"label": "Type"
},
"except": {
"label": "Exceptions"
},
"fax": {
"label": "Fax",
"placeholder": "+31 42 123 4567"
@@ -822,6 +840,9 @@
"restriction": {
"label": "Type"
},
"restrictions": {
"label": "Turn Restrictions"
},
"route": {
"label": "Type"
},
@@ -2834,6 +2855,34 @@
"name": "Restriction",
"terms": ""
},
"type/restriction/no_left_turn": {
"name": "No Left Turn",
"terms": ""
},
"type/restriction/no_right_turn": {
"name": "No Right Turn",
"terms": ""
},
"type/restriction/no_straight_on": {
"name": "No Straight On",
"terms": ""
},
"type/restriction/no_u_turn": {
"name": "No U-turn",
"terms": ""
},
"type/restriction/only_left_turn": {
"name": "Left Turn Only",
"terms": ""
},
"type/restriction/only_right_turn": {
"name": "Right Turn Only",
"terms": ""
},
"type/restriction/only_straight_ahead": {
"name": "No Turns",
"terms": ""
},
"type/route": {
"name": "Route",
"terms": ""
+12 -7
View File
@@ -47,8 +47,9 @@
<script src="js/id/geo.js"></script>
<script src="js/id/geo/extent.js"></script>
<script src="js/id/geo/intersection.js"></script>
<script src="js/id/geo/multipolygon.js"></script>
<script src="js/id/geo/turn.js"></script>
<script src="js/id/geo/raw_mercator.js"></script>
<script src='js/id/renderer/background.js'></script>
<script src='js/id/renderer/background_source.js'></script>
@@ -58,14 +59,15 @@
<script src="js/id/svg.js"></script>
<script src="js/id/svg/areas.js"></script>
<script src="js/id/svg/defs.js"></script>
<script src="js/id/svg/labels.js"></script>
<script src="js/id/svg/lines.js"></script>
<script src="js/id/svg/midpoints.js"></script>
<script src="js/id/svg/points.js"></script>
<script src="js/id/svg/surface.js"></script>
<script src="js/id/svg/tag_classes.js"></script>
<script src="js/id/svg/turns.js"></script>
<script src="js/id/svg/vertices.js"></script>
<script src="js/id/svg/labels.js"></script>
<script src="js/id/svg/restrictions.js"></script>
<script src="js/id/ui.js"></script>
<script src='js/id/ui/intro.js'></script>
@@ -118,6 +120,7 @@
<script src='js/id/ui/preset/localized.js'></script>
<script src='js/id/ui/preset/maxspeed.js'></script>
<script src='js/id/ui/preset/radio.js'></script>
<script src='js/id/ui/preset/restrictions.js'></script>
<script src='js/id/ui/preset/textarea.js'></script>
<script src='js/id/ui/preset/wikipedia.js'></script>
@@ -135,6 +138,7 @@
<script src='js/id/actions/change_member.js'></script>
<script src='js/id/actions/change_preset.js'></script>
<script src='js/id/actions/change_tags.js'></script>
<script src='js/id/actions/circularize.js'></script>
<script src='js/id/actions/connect.js'></script>
<script src='js/id/actions/delete_member.js'></script>
<script src='js/id/actions/delete_multiple.js'></script>
@@ -148,13 +152,14 @@
<script src='js/id/actions/merge_polygon.js'></script>
<script src='js/id/actions/move_node.js'></script>
<script src='js/id/actions/move.js'></script>
<script src='js/id/actions/rotate_way.js'></script>
<script src='js/id/actions/circularize.js'></script>
<script src='js/id/actions/orthogonalize.js'></script>
<script src='js/id/actions/straighten.js'></script>
<script src='js/id/actions/noop.js'></script>
<script src='js/id/actions/orthogonalize.js'></script>
<script src='js/id/actions/rotate_way.js'></script>
<script src='js/id/actions/restrict_turn.js'></script>
<script src='js/id/actions/reverse.js'></script>
<script src='js/id/actions/straighten.js'></script>
<script src='js/id/actions/split.js'></script>
<script src='js/id/actions/unrestrict_turn.js'></script>
<script src='js/id/behavior.js'></script>
<script src='js/id/behavior/add_way.js'></script>
+89
View File
@@ -0,0 +1,89 @@
// Create a restriction relation for `turn`, which must have the following structure:
//
// {
// from: { node: <node ID>, way: <way ID> },
// via: { node: <node ID> },
// to: { node: <node ID>, way: <way ID> },
// restriction: <'no_right_turn', 'no_left_turn', etc.>
// }
//
// This specifies a restriction of type `restriction` when traveling from
// `from.node` in `from.way` toward `to.node` in `to.way` via `via.node`.
// (The action does not check that these entities form a valid intersection.)
//
// If `restriction` is not provided, it is automatically determined by the
// angle of the turn:
//
// 0-23 degrees: no_u_turn
// 23-158 degrees: no_right_turn
// 158-202 degrees: no_straight_on
// 202-326 degrees: no_left_turn
// 336-360 degrees: no_u_turn
//
// If necessary, the `from` and `to` ways are split. In these cases, `from.node`
// and `to.node` are used to determine which portion of the split ways become
// members of the restriction.
//
// For testing convenience, accepts an ID to assign to the new relation.
// Normally, this will be undefined and the relation will automatically
// be assigned a new ID.
//
iD.actions.RestrictTurn = function(turn, projection, restrictionId) {
return function(graph) {
var from = graph.entity(turn.from.way),
via = graph.entity(turn.via.node),
to = graph.entity(turn.to.way);
function split(toOrFrom) {
var newID = toOrFrom.newID || iD.Way().id;
graph = iD.actions.Split(via.id, [newID])
.limitWays([toOrFrom.way])(graph);
var a = graph.entity(newID),
b = graph.entity(toOrFrom.way);
if (a.nodes.indexOf(toOrFrom.node) !== -1) {
return [a, b];
} else {
return [b, a];
}
}
if (!from.affix(via.id)) {
if (turn.from.node === turn.to.node) {
// U-turn
from = to = split(turn.from)[0];
} else if (turn.from.way === turn.to.way) {
// Straight-on
var s = split(turn.from);
from = s[0];
to = s[1];
} else {
// Other
from = split(turn.from)[0];
}
}
if (!to.affix(via.id)) {
to = split(turn.to)[0];
}
return graph.replace(iD.Relation({
id: restrictionId,
tags: {
type: 'restriction',
restriction: turn.restriction ||
iD.geo.inferRestriction(
graph.entity(turn.from.node),
via,
graph.entity(turn.to.node),
projection)
},
members: [
{id: from.id, type: 'way', role: 'from'},
{id: via.id, type: 'node', role: 'via'},
{id: to.id, type: 'way', role: 'to'}
]
}));
};
};
+23
View File
@@ -0,0 +1,23 @@
// Remove the effects of `turn.restriction` on `turn`, which must have the
// following structure:
//
// {
// from: { node: <node ID>, way: <way ID> },
// via: { node: <node ID> },
// to: { node: <node ID>, way: <way ID> },
// restriction: <relation ID>
// }
//
// In the simple case, `restriction` is a reference to a `no_*` restriction
// on the turn itself. In this case, it is simply deleted.
//
// The more complex case is where `restriction` references an `only_*`
// restriction on a different turn in the same intersection. In that case,
// that restriction is also deleted, but at the same time restrictions on
// the turns other than the first two are created.
//
iD.actions.UnrestrictTurn = function(turn) {
return function(graph) {
return iD.actions.DeleteRelation(turn.restriction)(graph);
};
};
+8
View File
@@ -33,6 +33,14 @@ iD.geo.edgeEqual = function(a, b) {
(a[0] === b[1] && a[1] === b[0]);
};
// Return the counterclockwise angle in the range (-pi, pi)
// between the positive X axis and the line intersecting a and b.
iD.geo.angle = function(a, b, projection) {
a = projection(a.loc);
b = projection(b.loc);
return Math.atan2(b[1] - a[1], b[0] - a[0]);
};
// Choose the edge with the minimal distance from `point` to its orthogonal
// projection onto that edge, if such a projection exists, or the distance to
// the closest vertex on that edge. Returns an object with the `index` of the
+135
View File
@@ -0,0 +1,135 @@
iD.geo.Turn = function(turn) {
if (!(this instanceof iD.geo.Turn))
return new iD.geo.Turn(turn);
_.extend(this, turn);
};
iD.geo.Intersection = function(graph, vertexId) {
var vertex = graph.entity(vertexId),
highways = [];
// Pre-split ways that would need to be split in
// order to add a restriction. The real split will
// happen when the restriction is added.
graph.parentWays(vertex).forEach(function(way) {
if (!way.tags.highway || way.isArea() || way.isDegenerate())
return;
if (way.affix(vertexId)) {
highways.push(way);
} else {
var idx = _.indexOf(way.nodes, vertex.id, 1),
wayA = iD.Way({id: way.id + '-a', tags: way.tags, nodes: way.nodes.slice(0, idx + 1)}),
wayB = iD.Way({id: way.id + '-b', tags: way.tags, nodes: way.nodes.slice(idx)});
graph = graph.replace(wayA);
graph = graph.replace(wayB);
highways.push(wayA);
highways.push(wayB);
}
});
var intersection = {
highways: highways,
graph: graph
};
intersection.turns = function(fromNodeID) {
if (!fromNodeID)
return [];
var way = _.find(highways, function(way) { return way.contains(fromNodeID); });
if (way.first() === vertex.id && way.tags.oneway === 'yes')
return [];
if (way.last() === vertex.id && way.tags.oneway === '-1')
return [];
function withRestriction(turn) {
graph.parentRelations(graph.entity(turn.from.way)).forEach(function(relation) {
if (relation.tags.type !== 'restriction')
return;
var f = relation.memberByRole('from'),
t = relation.memberByRole('to'),
v = relation.memberByRole('via');
if (f && f.id === turn.from.way &&
v && v.id === turn.via.node &&
t && t.id === turn.to.way) {
turn.restriction = relation.id;
} else if (/^only_/.test(relation.tags.restriction) &&
f && f.id === turn.from.way &&
v && v.id === turn.via.node &&
t && t.id !== turn.to.way) {
turn.restriction = relation.id;
turn.indirect_restriction = true;
}
});
return iD.geo.Turn(turn);
}
var from = {
node: way.nodes[way.first() === vertex.id ? 1 : way.nodes.length - 2],
way: way.id.split(/-(a|b)/)[0]
},
via = {node: vertex.id},
turns = [];
highways.forEach(function(parent) {
if (parent === way)
return;
var index = parent.nodes.indexOf(vertex.id);
// backward
if (parent.first() !== vertex.id && parent.tags.oneway !== 'yes') {
turns.push(withRestriction({
from: from,
via: via,
to: {node: parent.nodes[index - 1], way: parent.id.split(/-(a|b)/)[0]}
}));
}
// forward
if (parent.last() !== vertex.id && parent.tags.oneway !== '-1') {
turns.push(withRestriction({
from: from,
via: via,
to: {node: parent.nodes[index + 1], way: parent.id.split(/-(a|b)/)[0]}
}));
}
});
// U-turn
if (way.tags.oneway !== 'yes' && way.tags.oneway !== '-1') {
turns.push(withRestriction({
from: from,
via: via,
to: from,
u: true
}));
}
return turns;
};
return intersection;
};
iD.geo.inferRestriction = function(from, via, to, projection) {
var angle = iD.geo.angle(via, from, projection) -
iD.geo.angle(via, to, projection);
angle = angle * 180 / Math.PI;
if (angle > 158 || angle < -158)
return 'no_straight_on';
if (angle > 23)
return 'no_right_turn';
if (angle < -22)
return 'no_left_turn';
return 'no_u_turn';
};
+50
View File
@@ -0,0 +1,50 @@
/*
Bypasses features of D3's default projection stream pipeline that are unnecessary:
* Antimeridian clipping
* Spherical rotation
* Resampling
*/
iD.geo.RawMercator = function () {
var project = d3.geo.mercator.raw,
k = 512 / Math.PI, // scale
x = 0, y = 0, // translate
clipExtent = [[0, 0], [0, 0]];
function projection(point) {
point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
return [point[0] * k + x, y - point[1] * k];
}
projection.invert = function(point) {
point = project.invert((point[0] - x) / k, (y - point[1]) / k);
return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
};
projection.scale = function(_) {
if (!arguments.length) return k;
k = +_;
return projection;
};
projection.translate = function(_) {
if (!arguments.length) return [x, y];
x = +_[0];
y = +_[1];
return projection;
};
projection.clipExtent = function(_) {
if (!arguments.length) return clipExtent;
clipExtent = _;
return projection;
};
projection.stream = d3.geo.transform({
point: function(x, y) {
x = projection([x, y]);
this.stream.point(x[0], x[1]);
}
}).stream;
return projection;
};
-62
View File
@@ -1,62 +0,0 @@
iD.geo.turns = function(graph, entityID) {
var way = graph.entity(entityID);
if (way.type !== 'way' || !way.tags.highway || way.isArea())
return [];
function withRestriction(turn) {
graph.parentRelations(turn.from).forEach(function(relation) {
if (relation.tags.type !== 'restriction')
return;
var f = relation.memberByRole('from'),
t = relation.memberByRole('to'),
v = relation.memberByRole('via');
if (f && f.id === turn.from.id &&
t && t.id === turn.to.id &&
v && v.id === turn.via.id) {
turn.restriction = relation;
}
});
return turn;
}
var turns = [];
[way.first(), way.last()].forEach(function(nodeID) {
var node = graph.entity(nodeID);
graph.parentWays(node).forEach(function(parent) {
if (parent === way || parent.isDegenerate() || !parent.tags.highway)
return;
if (way.first() === node.id && way.tags.oneway === 'yes')
return;
if (way.last() === node.id && way.tags.oneway === '-1')
return;
var index = parent.nodes.indexOf(node.id);
// backward
if (parent.first() !== node.id && parent.tags.oneway !== 'yes') {
turns.push(withRestriction({
from: way,
to: parent,
via: node,
toward: graph.entity(parent.nodes[index - 1])
}));
}
// forward
if (parent.last() !== node.id && parent.tags.oneway !== '-1') {
turns.push(withRestriction({
from: way,
to: parent,
via: node,
toward: graph.entity(parent.nodes[index + 1])
}));
}
});
});
return turns;
};
+1 -46
View File
@@ -197,52 +197,7 @@ window.iD = function () {
};
/* Projection */
function rawMercator() {
var project = d3.geo.mercator.raw,
k = 512 / Math.PI, // scale
x = 0, y = 0, // translate
clipExtent = [[0, 0], [0, 0]];
function projection(point) {
point = project(point[0] * Math.PI / 180, point[1] * Math.PI / 180);
return [point[0] * k + x, y - point[1] * k];
}
projection.invert = function(point) {
point = project.invert((point[0] - x) / k, (y - point[1]) / k);
return point && [point[0] * 180 / Math.PI, point[1] * 180 / Math.PI];
};
projection.scale = function(_) {
if (!arguments.length) return k;
k = +_;
return projection;
};
projection.translate = function(_) {
if (!arguments.length) return [x, y];
x = +_[0];
y = +_[1];
return projection;
};
projection.clipExtent = function(_) {
if (!arguments.length) return clipExtent;
clipExtent = _;
return projection;
};
projection.stream = d3.geo.transform({
point: function(x, y) {
x = projection([x, y]);
this.stream.point(x[0], x[1]);
}
}).stream;
return projection;
}
context.projection = rawMercator();
context.projection = iD.geo.RawMercator();
/* Background */
var background = iD.Background(context);
+4 -4
View File
@@ -36,15 +36,15 @@ iD.operations.Delete = function(selectedIDs, context) {
}
}
context.perform(
action,
annotation);
if (nextSelectedID && context.hasEntity(nextSelectedID)) {
context.enter(iD.modes.Select(context, [nextSelectedID]));
} else {
context.enter(iD.modes.Browse(context));
}
context.perform(
action,
annotation);
};
operation.available = function() {
+1 -1
View File
@@ -4,7 +4,7 @@ iD.presets.Field = function(id, field) {
field.id = id;
field.matchGeometry = function(geometry) {
return !field.geometry || field.geometry.indexOf(geometry) >= 0;
return !field.geometry || field.geometry === geometry;
};
field.t = function(scope, options) {
+129
View File
@@ -0,0 +1,129 @@
/*
A standalone SVG element that contains only a `defs` sub-element. To be
used once globally, since defs IDs must be unique within a document.
*/
iD.svg.Defs = function(context) {
function autosize(image) {
var img = document.createElement('img');
img.src = image.attr('xlink:href');
img.onload = function() {
image.attr({
width: img.width,
height: img.height
});
};
}
function SpriteDefinition(id, href, data) {
return function(defs) {
defs.append('image')
.attr('id', id)
.attr('xlink:href', href)
.call(autosize);
defs.selectAll()
.data(data)
.enter().append('use')
.attr('id', function(d) { return d.key; })
.attr('transform', function(d) { return 'translate(-' + d.value[0] + ',-' + d.value[1] + ')'; })
.attr('xlink:href', '#' + id);
};
}
return function (selection) {
var defs = selection.append('defs');
defs.append('marker')
.attr({
id: 'oneway-marker',
viewBox: '0 0 10 10',
refY: 2.5,
refX: 5,
markerWidth: 2,
markerHeight: 2,
orient: 'auto'
})
.append('path')
.attr('d', 'M 5 3 L 0 3 L 0 2 L 5 2 L 5 0 L 10 2.5 L 5 5 z');
var patterns = defs.selectAll('pattern')
.data([
// pattern name, pattern image name
['wetland', 'wetland'],
['construction', 'construction'],
['cemetery', 'cemetery'],
['orchard', 'orchard'],
['farmland', 'farmland'],
['beach', 'dots'],
['scrub', 'dots'],
['meadow', 'dots']
])
.enter()
.append('pattern')
.attr({
id: function (d) {
return 'pattern-' + d[0];
},
width: 32,
height: 32,
patternUnits: 'userSpaceOnUse'
});
patterns.append('rect')
.attr({
x: 0,
y: 0,
width: 32,
height: 32,
'class': function (d) {
return 'pattern-color-' + d[0];
}
});
patterns.append('image')
.attr({
x: 0,
y: 0,
width: 32,
height: 32
})
.attr('xlink:href', function (d) {
return context.imagePath('pattern/' + d[1] + '.png');
});
defs.selectAll()
.data([12, 18, 20, 32, 45])
.enter().append('clipPath')
.attr('id', function (d) {
return 'clip-square-' + d;
})
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', function (d) {
return d;
})
.attr('height', function (d) {
return d;
});
var maki = [];
_.forEach(iD.data.featureIcons, function (dimensions, name) {
if (dimensions['12'] && dimensions['18'] && dimensions['24']) {
maki.push({key: 'maki-' + name + '-12', value: dimensions['12']});
maki.push({key: 'maki-' + name + '-18', value: dimensions['18']});
maki.push({key: 'maki-' + name + '-24', value: dimensions['24']});
}
});
defs.call(SpriteDefinition(
'sprite',
context.imagePath('sprite.svg'),
d3.entries(iD.data.operations)));
defs.call(SpriteDefinition(
'maki-sprite',
context.imagePath('maki-sprite.png'),
maki));
};
};
-75
View File
@@ -1,75 +0,0 @@
iD.svg.Restrictions = function(context) {
var projection = context.projection;
function drawRestrictions(surface) {
var turns = drawRestrictions.turns(context.graph(), context.selectedIDs());
var groups = surface.select('.layer-hit').selectAll('g.restriction')
.data(turns, iD.Entity.key);
var enter = groups.enter().append('g')
.attr('class', 'restriction');
enter.append('circle')
.attr('class', 'restriction')
.attr('r', 4);
groups
.attr('transform', function(restriction) {
var via = context.entity(restriction.memberByRole('via').id);
return iD.svg.PointTransform(projection)(via);
});
groups.exit()
.remove();
return this;
}
drawRestrictions.turns = function (graph, selectedIDs) {
if (selectedIDs.length !== 1)
return [];
var from = graph.entity(selectedIDs[0]);
if (from.type !== 'way')
return [];
return graph.parentRelations(from).filter(function(relation) {
var f = relation.memberById(from.id),
t = relation.memberByRole('to'),
v = relation.memberByRole('via');
return relation.tags.type === 'restriction' && f.role === 'from' &&
t && t.type === 'way' && graph.hasEntity(t.id) &&
v && v.type === 'node' && graph.hasEntity(v.id) &&
!graph.entity(t.id).isDegenerate() &&
!graph.entity(f.id).isDegenerate() &&
graph.entity(t.id).affix(v.id) &&
graph.entity(f.id).affix(v.id);
});
};
drawRestrictions.datum = function(graph, from, restriction, projection) {
var to = graph.entity(restriction.memberByRole('to').id),
a = graph.entity(restriction.memberByRole('via').id),
b;
if (to.first() === a.id) {
b = graph.entity(to.nodes[1]);
} else {
b = graph.entity(to.nodes[to.nodes.length - 2]);
}
a = projection(a.loc);
b = projection(b.loc);
return {
from: from,
to: to,
restriction: restriction,
angle: Math.atan2(b[1] - a[1], b[0] - a[0])
};
};
return drawRestrictions;
};
+2 -111
View File
@@ -1,114 +1,5 @@
iD.svg.Surface = function(context) {
function autosize(image) {
var img = document.createElement('img');
img.src = image.attr('xlink:href');
img.onload = function() {
image.attr({
width: img.width,
height: img.height
});
};
}
function SpriteDefinition(id, href, data) {
return function(defs) {
defs.append('image')
.attr('id', id)
.attr('xlink:href', href)
.call(autosize);
defs.selectAll()
.data(data)
.enter().append('use')
.attr('id', function(d) { return d.key; })
.attr('transform', function(d) { return 'translate(-' + d.value[0] + ',-' + d.value[1] + ')'; })
.attr('xlink:href', '#' + id);
};
}
return function drawSurface(selection) {
var defs = selection.append('defs');
defs.append('marker')
.attr({
id: 'oneway-marker',
viewBox: '0 0 10 10',
refY: 2.5,
refX: 5,
markerWidth: 2,
markerHeight: 2,
orient: 'auto'
})
.append('path')
.attr('d', 'M 5 3 L 0 3 L 0 2 L 5 2 L 5 0 L 10 2.5 L 5 5 z');
var patterns = defs.selectAll('pattern')
.data([
// pattern name, pattern image name
['wetland', 'wetland'],
['construction', 'construction'],
['cemetery', 'cemetery'],
['orchard', 'orchard'],
['farmland', 'farmland'],
['beach', 'dots'],
['scrub', 'dots'],
['meadow', 'dots']])
.enter()
.append('pattern')
.attr({
id: function(d) { return 'pattern-' + d[0]; },
width: 32,
height: 32,
patternUnits: 'userSpaceOnUse'
});
patterns.append('rect')
.attr({
x: 0,
y: 0,
width: 32,
height: 32,
'class': function(d) { return 'pattern-color-' + d[0]; }
});
patterns.append('image')
.attr({
x: 0,
y: 0,
width: 32,
height: 32
})
.attr('xlink:href', function(d) { return context.imagePath('pattern/' + d[1] + '.png'); });
defs.selectAll()
.data([12, 18, 20])
.enter().append('clipPath')
.attr('id', function(d) { return 'clip-square-' + d; })
.append('rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', function(d) { return d; })
.attr('height', function(d) { return d; });
var maki = [];
_.forEach(iD.data.featureIcons, function(dimensions, name) {
if (dimensions['12'] && dimensions['18'] && dimensions['24']) {
maki.push({key: 'maki-' + name + '-12', value: dimensions['12']});
maki.push({key: 'maki-' + name + '-18', value: dimensions['18']});
maki.push({key: 'maki-' + name + '-24', value: dimensions['24']});
}
});
defs.call(SpriteDefinition(
'sprite',
context.imagePath('sprite.svg'),
d3.entries(iD.data.operations)));
defs.call(SpriteDefinition(
'maki-sprite',
context.imagePath('maki-sprite.png'),
maki));
iD.svg.Surface = function() {
return function (selection) {
var layers = selection.selectAll('.layer')
.data(['fill', 'shadow', 'casing', 'stroke', 'oneway', 'hit', 'halo', 'label']);
+67
View File
@@ -0,0 +1,67 @@
iD.svg.Turns = function(projection) {
return function(surface, graph, turns) {
function icon(turn) {
if (!turn.restriction)
return '#icon-restriction-yes';
var restriction = graph.entity(turn.restriction).tags.restriction;
return '#icon-restriction-' +
(!turn.indirect_restriction && /^only_/.test(restriction) ? 'only' : 'no') +
(turn.u ? '-u' : '');
}
var groups = surface.select('.layer-hit').selectAll('g.turn')
.data(turns);
// Enter
var enter = groups.enter().append('g')
.attr('class', 'turn');
var nEnter = enter.filter(function (turn) { return !turn.u; });
nEnter.append('rect')
.attr('transform', 'translate(-12, -12)')
.attr('width', '45')
.attr('height', '25');
nEnter.append('use')
.attr('transform', 'translate(-12, -12)')
.attr('clip-path', 'url(#clip-square-45)');
var uEnter = enter.filter(function (turn) { return turn.u; });
uEnter.append('circle')
.attr('r', '16');
uEnter.append('use')
.attr('transform', 'translate(-16, -16)')
.attr('clip-path', 'url(#clip-square-32)');
// Update
groups
.attr('transform', function (turn) {
var v = graph.entity(turn.via.node),
t = graph.entity(turn.to.node),
a = iD.geo.angle(v, t, projection),
p = projection(v.loc),
r = turn.u ? 0 : 60;
return 'translate(' + (r * Math.cos(a) + p[0]) + ',' + (r * Math.sin(a) + p[1]) + ')' +
'rotate(' + a * 180 / Math.PI + ')';
});
groups.select('use')
.attr('xlink:href', icon);
groups.select('rect');
groups.select('circle');
// Exit
groups.exit()
.remove();
return this;
};
};
+4
View File
@@ -12,6 +12,10 @@ iD.ui = function(context) {
map.centerZoom([-77.02271, 38.90085], 20);
}
container.append('svg')
.attr('id', 'defs')
.call(iD.svg.Defs(context));
container.append('div')
.attr('id', 'sidebar')
.attr('class', 'col4')
+4 -3
View File
@@ -5,6 +5,8 @@ iD.ui.EntityEditor = function(context) {
preset,
reference;
var presetEditor = iD.ui.preset(context)
.on('change', changeTags);
var rawTagEditor = iD.ui.RawTagEditor(context)
.on('change', changeTags);
@@ -91,12 +93,11 @@ iD.ui.EntityEditor = function(context) {
.text(preset.name());
$body.select('.inspector-preset')
.call(iD.ui.preset(context)
.call(presetEditor
.preset(preset)
.entityID(id)
.tags(tags)
.state(state)
.on('change', changeTags));
.state(state));
$body.select('.raw-tag-editor')
.call(rawTagEditor
+6 -1
View File
@@ -31,7 +31,12 @@ iD.ui.Inspector = function(context) {
var $presetPane = $wrap.select('.preset-list-pane');
var $editorPane = $wrap.select('.entity-editor-pane');
var showEditor = state === 'hover' || context.entity(entityID).isUsed(context.graph());
var graph = context.graph(),
entity = context.entity(entityID),
showEditor = state === 'hover' ||
entity.isUsed(graph) ||
entity.isIntersection(graph);
if (showEditor) {
$wrap.style('right', '0%');
$editorPane.call(entityEditor);
+7 -1
View File
@@ -72,6 +72,10 @@ iD.ui.preset = function(context) {
}
});
if (geometry === 'vertex' && entity.isIntersection(context.graph())) {
fields.push(UIField(context.presets().field('restrictions'), entity, true));
}
context.presets().universal().forEach(function(field) {
if (preset.fields.indexOf(field) < 0) {
fields.push(UIField(field, entity));
@@ -177,7 +181,7 @@ iD.ui.preset = function(context) {
function show(field) {
field.show = true;
presets(selection);
context.presets()(selection);
field.input.focus();
}
@@ -196,6 +200,7 @@ iD.ui.preset = function(context) {
presets.preset = function(_) {
if (!arguments.length) return preset;
if (preset && preset.id === _.id) return presets;
preset = _;
fields = null;
return presets;
@@ -216,6 +221,7 @@ iD.ui.preset = function(context) {
presets.entityID = function(_) {
if (!arguments.length) return id;
if (id === _) return presets;
id = _;
fields = null;
return presets;
+137
View File
@@ -0,0 +1,137 @@
iD.ui.preset.restrictions = function(field, context) {
var event = d3.dispatch('change'),
vertexID,
fromNodeID;
function restrictions(selection) {
var wrap = selection.selectAll('.preset-input-wrap')
.data([0]);
var enter = wrap.enter().append('div')
.attr('class', 'preset-input-wrap');
enter.append('div')
.attr('class', 'restriction-help');
enter.append('svg')
.call(iD.svg.Surface(context))
.call(iD.behavior.Hover(context));
var intersection = iD.geo.Intersection(context.graph(), vertexID),
graph = intersection.graph,
vertex = graph.entity(vertexID),
surface = wrap.selectAll('svg'),
filter = function () { return true; },
extent = iD.geo.Extent(),
projection = iD.geo.RawMercator(),
lines = iD.svg.Lines(projection, context),
vertices = iD.svg.Vertices(projection, context),
turns = iD.svg.Turns(projection, context);
var d = wrap.dimensions(),
c = [d[0] / 2, d[1] / 2],
z = 21;
projection
.scale(256 * Math.pow(2, z) / (2 * Math.PI));
var s = projection(vertex.loc);
projection
.translate([c[0] - s[0], c[1] - s[1]])
.clipExtent([[0, 0], d]);
surface
.call(vertices, graph, [vertex], filter, extent, z)
.call(lines, graph, intersection.highways, filter)
.call(turns, graph, intersection.turns(fromNodeID));
surface
.on('click.restrictions', click)
.on('mouseover.restrictions', mouseover)
.on('mouseout.restrictions', mouseout);
surface
.selectAll('.selected')
.classed('selected', false);
if (fromNodeID) {
surface
.selectAll('.' + _.find(intersection.highways, function(way) { return way.contains(fromNodeID); }).id)
.classed('selected', true);
}
mouseout();
context.history()
.on('change.restrictions', render);
d3.select(window)
.on('resize.restrictions', render);
function click() {
var datum = d3.event.target.__data__;
if (datum instanceof iD.Entity) {
fromNodeID = datum.nodes[(datum.first() === vertexID) ? 1 : datum.nodes.length - 2];
render();
} else if (datum instanceof iD.geo.Turn) {
if (datum.restriction) {
context.perform(
iD.actions.UnrestrictTurn(datum, projection),
t('operations.restriction.annotation.delete'));
} else {
context.perform(
iD.actions.RestrictTurn(datum, projection),
t('operations.restriction.annotation.create'));
}
}
}
function mouseover() {
var datum = d3.event.target.__data__;
if (datum instanceof iD.geo.Turn) {
var graph = context.graph(),
presets = context.presets(),
preset;
if (datum.restriction) {
preset = presets.match(graph.entity(datum.restriction), graph);
} else {
preset = presets.item('type/restriction/' +
iD.geo.inferRestriction(
graph.entity(datum.from.node),
graph.entity(datum.via.node),
graph.entity(datum.to.node),
projection));
}
wrap.selectAll('.restriction-help')
.text(t('operations.restriction.help.' +
(datum.restriction ? 'toggle_off' : 'toggle_on'),
{restriction: preset.name()}));
}
}
function mouseout() {
wrap.selectAll('.restriction-help')
.text(t('operations.restriction.help.' +
(fromNodeID ? 'toggle' : 'select')));
}
function render() {
restrictions(selection);
}
}
restrictions.entity = function(_) {
if (!vertexID || vertexID !== _.id) {
fromNodeID = null;
vertexID = _.id;
}
};
restrictions.tags = function() {};
restrictions.focus = function() {};
return d3.rebind(restrictions, event, 'on');
};
+53 -1
View File
@@ -1,7 +1,7 @@
/**
* @license
* Lo-Dash 2.3.0 (Custom Build) <http://lodash.com/>
* Build: `lodash include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge" exports="global,node"`
* Build: `lodash --debug --output js/lib/lodash.js include="any,assign,bind,clone,compact,contains,debounce,difference,each,every,extend,filter,find,first,forEach,groupBy,indexOf,intersection,isEmpty,isEqual,isFunction,keys,last,map,omit,pairs,pluck,reject,some,throttle,union,uniq,unique,values,without,flatten,value,chain,cloneDeep,merge,pick" exports="global,node"`
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
@@ -2304,6 +2304,57 @@
return result;
}
/**
* Creates a shallow clone of `object` composed of the specified properties.
* Property names may be specified as individual arguments or as arrays of
* property names. If a callback is provided it will be executed for each
* property of `object` picking the properties the callback returns truey
* for. The callback is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
* @param {Function|...string|string[]} [callback] The function called per
* iteration or property names to pick, specified as individual property
* names or arrays of property names.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns an object composed of the picked properties.
* @example
*
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
* // => { 'name': 'fred' }
*
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
* return key.charAt(0) != '_';
* });
* // => { 'name': 'fred' }
*/
function pick(object, callback, thisArg) {
var result = {};
if (typeof callback != 'function') {
var index = -1,
props = baseFlatten(arguments, true, false, 1),
length = isObject(object) ? props.length : 0;
while (++index < length) {
var key = props[index];
if (key in object) {
result[key] = object[key];
}
}
} else {
callback = lodash.createCallback(callback, thisArg, 3);
forIn(object, function(value, key, object) {
if (callback(value, key, object)) {
result[key] = value;
}
});
}
return result;
}
/**
* Creates an array composed of the own enumerable property values of `object`.
*
@@ -3830,6 +3881,7 @@
lodash.merge = merge;
lodash.omit = omit;
lodash.pairs = pairs;
lodash.pick = pick;
lodash.pluck = pluck;
lodash.reject = reject;
lodash.throttle = throttle;
+1
View File
@@ -22,6 +22,7 @@
"devDependencies": {
"d3": "3.4.6",
"smash": "0.0",
"lodash-cli": "2.3.0",
"uglify-js": "~2.2.5",
"maki": "0.4",
"jshint": "2.3.0",
+226 -19
View File
@@ -14,8 +14,11 @@
height="100"
id="svg7013"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="relation-presets.svg">
inkscape:version="0.48.2 r9819"
sodipodi:docname="relation-presets.svg"
inkscape:export-filename="/Users/saman/work_repos/iD/css/img/relation-presets@2x.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180">
<defs
id="defs7015">
<linearGradient
@@ -228,22 +231,22 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="4"
inkscape:cx="819.66056"
inkscape:cy="31.76926"
inkscape:zoom="1"
inkscape:cx="1031.6793"
inkscape:cy="27.373969"
inkscape:document-units="px"
inkscape:current-layer="g3421"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1721"
inkscape:window-height="881"
inkscape:window-x="25"
inkscape:window-y="76"
inkscape:window-x="140"
inkscape:window-y="28"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
showguides="true"
showguides="false"
inkscape:guide-bbox="true"
inkscape:snap-nodes="true"
inkscape:snap-bbox="true"
@@ -360,6 +363,30 @@
orientation="1,0"
position="740,75"
id="guide25051" />
<sodipodi:guide
orientation="0,1"
position="111,29"
id="guide4515" />
<sodipodi:guide
orientation="1,0"
position="110,130"
id="guide5153" />
<sodipodi:guide
orientation="1,0"
position="50,100"
id="guide5155" />
<sodipodi:guide
orientation="1,0"
position="170,100"
id="guide5157" />
<sodipodi:guide
orientation="1,0"
position="230,115"
id="guide5159" />
<sodipodi:guide
orientation="1,0"
position="290,125"
id="guide5161" />
</sodipodi:namedview>
<metadata
id="metadata7018">
@@ -369,7 +396,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@@ -1505,7 +1532,7 @@
id="path23512"
transform="translate(-400,12.362183)"
d="M 110 39.5 C 101.43958 39.5 94.5 46.439584 94.5 55 C 94.5 63.560416 101.43958 70.5 110 70.5 C 118.56042 70.5 125.5 63.560416 125.5 55 C 125.5 46.439584 118.56042 39.5 110 39.5 z M 110 43.5 C 116.35127 43.5 121.5 48.648725 121.5 55 C 121.5 57.454314 120.71623 59.726293 119.40625 61.59375 L 103.40625 45.59375 C 105.27371 44.283773 107.54569 43.5 110 43.5 z M 100.59375 48.40625 L 116.59375 64.40625 C 114.72629 65.716227 112.45431 66.5 110 66.5 C 103.64873 66.5 98.5 61.351275 98.5 55 C 98.5 52.545686 99.283773 50.273707 100.59375 48.40625 z "
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:#70372f;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="relation-multipolygon"
@@ -2681,7 +2708,7 @@
style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#58a9ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
<path
transform="matrix(1.6666717,0,0,1.6666717,-333.33349,-41.971489)"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
sodipodi:ry="1.5"
sodipodi:rx="1.5"
sodipodi:cy="66.5"
@@ -2697,11 +2724,11 @@
sodipodi:cy="66.5"
sodipodi:rx="1.5"
sodipodi:ry="1.5"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
transform="matrix(1.6666647,0,0,1.6666647,-368.33327,-33.971018)" />
<path
transform="matrix(1.6666676,0,0,1.6666676,-356.33336,-41.971209)"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
sodipodi:ry="1.5"
sodipodi:rx="1.5"
sodipodi:cy="66.5"
@@ -2717,11 +2744,11 @@
sodipodi:cy="66.5"
sodipodi:rx="1.5"
sodipodi:ry="1.5"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
transform="matrix(1.6666676,0,0,1.6666676,-345.33336,-33.971209)" />
<path
transform="translate(-313.00001,2.3621875)"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
sodipodi:ry="1.5"
sodipodi:rx="1.5"
sodipodi:cy="66.5"
@@ -2731,7 +2758,7 @@
sodipodi:type="arc" />
<path
transform="translate(-325.00001,10.362187)"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
sodipodi:ry="1.5"
sodipodi:rx="1.5"
sodipodi:cy="66.5"
@@ -2747,7 +2774,7 @@
sodipodi:cy="66.5"
sodipodi:rx="1.5"
sodipodi:ry="1.5"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
transform="translate(-336,2.3621875)" />
<path
sodipodi:type="arc"
@@ -2757,7 +2784,7 @@
sodipodi:cy="66.5"
sodipodi:rx="1.5"
sodipodi:ry="1.5"
d="m 32,66.5 a 1.5,1.5 0 1 1 -3,0 1.5,1.5 0 1 1 3,0 z"
d="M 32,66.5 C 32,67.328427 31.328427,68 30.5,68 29.671573,68 29,67.328427 29,66.5 29,65.671573 29.671573,65 30.5,65 c 0.828427,0 1.5,0.671573 1.5,1.5 z"
transform="translate(-348,10.362187)" />
</g>
<g
@@ -3139,5 +3166,185 @@
sodipodi:type="arc" />
</g>
</g>
<g
id="g4552-0"
transform="translate(-292,0)"
style="opacity:1">
<g
inkscape:label="#g26733"
id="g4560-9"
transform="matrix(-1,0,0,1,-1043,0)" />
</g>
<g
id="g5135"
transform="translate(150,0)">
<g
id="g5043"
transform="translate(-290,-60)" />
</g>
<g
id="relation-no-straight"
inkscape:label="#g5191">
<g
transform="translate(1363,0)"
id="g4998">
<path
inkscape:connector-curvature="0"
style="color:#000000;fill:#989898;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m -353,40 -9,10 7.53125,0 L -350,54.46875 -350,50 l 6,0 -9,-10 z m -3,15.5 0,10.09375 c 0.95313,0.268897 1.96012,0.40625 3,0.40625 1.05145,0 2.04673,-0.167575 3,-0.46875 l 0,-4.03125 -6,-6 z"
transform="translate(-400,12.362183)"
id="path4995" />
<path
inkscape:connector-curvature="0"
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="M -754.53125,52.424683 C -762.09932,53.192204 -768,59.590124 -768,67.362183 c 0,8.290196 6.7098,15 15,15 8.2902,0 15,-6.709804 15,-15 0,-7.814285 -5.96643,-14.227286 -13.59375,-14.9375 l 3.6875,4.09375 c 4.06888,1.921523 6.90625,6.049966 6.90625,10.84375 0,2.560762 -0.82187,4.928217 -2.1875,6.875 l -0.34375,0.46875 -0.40625,-0.40625 -10.9375,-10.9375 -1.125,0 -3.09375,0 13.03125,13.03125 0.4375,0.4375 -0.5,0.34375 c -1.94679,1.365622 -4.31424,2.1875 -6.875,2.1875 -6.62149,0 -12,-5.378505 -12,-12 0,-2.560762 0.82187,-4.959467 2.1875,-6.90625 l 0.34375,-0.46875 0.40625,0.40625 0.25,0.25 2,-2.21875 -0.125,-0.125 -0.4375,-0.4375 0.5,-0.34375 c 0.56267,-0.394698 1.15379,-0.737822 1.78125,-1.03125 l 3.6875,-4.0625 c -0.0412,0.0038 -0.0839,-0.0042 -0.125,0 z"
id="path4989" />
</g>
<rect
y="37.362183"
x="580"
height="60"
width="60"
id="rect3423-6"
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="relation-no-u"
inkscape:label="#g5215">
<path
sodipodi:nodetypes="ccccccccccccccccccccccc"
inkscape:connector-curvature="0"
id="path4860"
d="m 669,59.362183 -2.3125,1.15625 4.8438,4.84375 3.4687,0 2,2 0,6 -6,0 9,10 9,-10 -6,0 0,-6 -1.2812,-2.59375 -0.7188,-1.40625 -2,-2 -4,-2 z m -6.1562,4.34375 -1.8438,3.65625 0,6.3125 c 1.3431,1.91263 3.2666,3.407065 5.5313,4.15625 l 0.4687,-0.46875 0,-9.5 z"
style="color:#000000;fill:#989898;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<path
sodipodi:nodetypes="cssccsscccccccccscccccscc"
inkscape:connector-curvature="0"
id="path4852"
d="m 670,52.362183 c -8.2902,0 -15,6.709804 -15,15 0,8.290196 6.7098,15 15,15 2.3067,0 4.486,-0.540289 6.4375,-1.46875 l -2.125,-2.375 c -1.3379,0.522442 -2.7902,0.84375 -4.3125,0.84375 -6.6215,0 -12,-5.378505 -12,-12 0,-2.560762 0.8219,-4.959467 2.1875,-6.90625 l 0.3438,-0.46875 0.4062,0.40625 11.9688,11.96875 3.0937,0 0,-1.125 -12.9375,-12.9375 -0.4375,-0.4375 0.5,-0.34375 c 1.9468,-1.365622 4.3142,-2.15625 6.875,-2.15625 4.3849,0 8.2188,2.358661 10.3125,5.875 l 1.5,1.53125 2.1874,4.34375 0,5.25 0.125,0 c 0.552,-1.563865 0.875,-3.246086 0.875,-5 0,-8.290196 -6.7098,-15 -15,-15 z"
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
y="37.362183"
x="640"
height="60"
width="60"
id="rect3423-8"
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="relation-no-left"
inkscape:label="#g5239">
<g
transform="translate(1483,0)"
id="g4600">
<path
inkscape:connector-curvature="0"
id="path4604"
transform="translate(-190,12.362183)"
d="m -573,42 -10,9 10,9 0,-6 5.5,0 -5.5,-5.5 0,-6.5 z m 6.53125,6 L -555,59.46875 -555,56 l -2,-4 -2,-2 -4,-2 -3.46875,0 z M -561,60.5 l 0,5.3125 c 1.35428,-0.282284 2.61071,-0.854283 3.75,-1.5625 L -561,60.5 z"
style="color:#000000;fill:#989898;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
inkscape:label="#g26733"
id="g4608"
transform="matrix(-1,0,0,1,-1043,0)">
<path
sodipodi:nodetypes="sccccsccsssssccccs"
inkscape:connector-curvature="0"
id="path4612"
transform="matrix(-1,0,0,1,-853,12.362183)"
d="m -563,40 c -3.38799,0 -6.49001,1.140087 -9,3.03125 l 0,5.0625 16.375,16.375 -0.5,0.34375 C -558.07179,66.178122 -560.43924,67 -563,67 c -4.47323,0 -8.37387,-2.459469 -10.4375,-6.09375 L -577.875,56.9375 C -576.92394,64.308739 -570.63203,70 -563,70 c 8.2902,0 15,-6.709804 15,-15 0,-8.290196 -6.7098,-15 -15,-15 z m 0,3 c 6.62149,0 12,5.378505 12,12 0,2.560762 -0.82187,4.928217 -2.1875,6.875 l -0.34375,0.46875 L -570.375,45.5 l 0.5,-0.34375 C -567.92821,43.790628 -565.56076,43 -563,43 z"
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<rect
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect5205"
width="60"
height="60"
x="700"
y="37.362183" />
</g>
<g
id="relation-no-right"
inkscape:label="#g5220">
<g
transform="translate(1543,0)"
id="g4552">
<path
sodipodi:nodetypes="ccccccccccccccccc"
inkscape:connector-curvature="0"
id="path4620"
transform="translate(-400,12.362183)"
d="m -344,42 0,6 -10,0 -1.65625,0.8125 5.1875,5.1875 6.46875,0 0,6 10,-9 z m -15.75,9.75 -0.25,0.25 -2,3 0,6.3125 c 1.43412,2.042224 3.53588,3.586075 6,4.28125 L -356,56 l 0.25,-0.25 z"
style="color:#000000;fill:#989898;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<g
transform="matrix(-1,0,0,1,-1043,0)"
id="g4560"
inkscape:label="#g26733">
<path
sodipodi:nodetypes="ssscccccccsccscccccsscc"
inkscape:connector-curvature="0"
id="path4564"
transform="matrix(-1,0,0,1,-643,12.362183)"
d="m -353,40 c -8.2902,0 -15,6.709804 -15,15 0,8.290196 6.7098,15 15,15 7.97307,0 14.48226,-6.212939 14.96875,-14.0625 l -3.8125,3.4375 c -0.35358,0.892816 -0.80074,1.725912 -1.34375,2.5 l -0.34375,0.46875 -16.40625,-16.40625 -0.4375,-0.4375 0.5,-0.34375 C -357.92821,43.790628 -355.56076,43 -353,43 c 3.07998,0 5.8741,1.182524 8,3.09375 l 0,-3.75 C -347.31948,40.874094 -350.04832,40 -353,40 z m -9.46875,7.625 0.40625,0.40625 16,16 0.4375,0.4375 -0.5,0.34375 C -348.07179,66.178122 -350.43924,67 -353,67 c -6.62149,0 -12,-5.378505 -12,-12 0,-2.560762 0.82187,-4.959467 2.1875,-6.90625 z"
style="color:#000000;fill:#e06d5f;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
</g>
<rect
y="37.362183"
x="760"
height="60"
width="60"
id="rect5207"
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="relation-only-straight"
inkscape:label="#g5227">
<path
style="color:#000000;fill:#58a9ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 850,51.362183 c -8.83656,0 -16,7.163444 -16,16 0,8.836556 7.16344,16 16,16 8.83656,0 16,-7.163444 16,-16 0,-8.836556 -7.16344,-16 -16,-16 z m 0,4 9,10 -6,0 0,12.53125 c -0.95327,0.301175 -1.94855,0.46875 -3,0.46875 -1.03988,0 -2.04687,-0.137353 -3,-0.40625 l 0,-12.59375 -6,0 9,-10 z"
id="path5016"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect5209"
width="60"
height="60"
x="820"
y="37.362183" />
</g>
<g
id="relation-only-left"
inkscape:label="#g5231">
<path
inkscape:connector-curvature="0"
id="path5144"
d="m 910,51.362183 c -8.83656,0 -16,7.163444 -16,16 0,8.836556 7.16344,16 16,16 8.83656,0 16,-7.163444 16,-16 0,-8.836556 -7.16344,-16 -16,-16 z m -2,4 0,1.78125 0,4.21875 6,0 2,1 3,3 1,2 0,4.5625 c -1.18289,2.589597 -3.33627,4.614165 -6,5.65625 l 0,-8.21875 -2,-2 -4,0 0,6 -6.96875,-6.28125 -3.03125,-2.71875 10,-9 z"
style="color:#000000;fill:#58a9ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
y="37.362183"
x="880"
height="60"
width="60"
id="rect5211"
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
</g>
<g
id="relation-only-right"
inkscape:label="#g5235">
<path
inkscape:connector-curvature="0"
id="path5148"
d="m 970,51.362183 c -8.83656,0 -16,7.163444 -16,16 0,8.836556 7.16344,16 16,16 8.83656,0 16,-7.163444 16,-16 0,-8.836556 -7.16344,-16 -16,-16 z m 2,4 10,9 -3.03125,2.71875 -6.96875,6.28125 0,-6 -4,0 -2,2 0,8.21875 c -2.6637,-1.042085 -4.8171,-3.066653 -6,-5.65625 l 0,-4.5625 1,-2 3,-3 2,-1 6,0 0,-4.21875 0,-1.78125 z"
style="color:#000000;fill:#58a9ed;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
<rect
style="color:#000000;fill:none;stroke:none;stroke-width:0.99999988;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect5213"
width="60"
height="60"
x="940"
y="37.362183" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 220 KiB

+13 -9
View File
@@ -45,8 +45,9 @@
<script src="../js/id/geo.js"></script>
<script src="../js/id/geo/extent.js"></script>
<script src="../js/id/geo/intersection.js"></script>
<script src="../js/id/geo/multipolygon.js"></script>
<script src="../js/id/geo/turn.js"></script>
<script src="../js/id/geo/raw_mercator.js"></script>
<script src='../js/id/renderer/background.js'></script>
<script src='../js/id/renderer/background_source.js'></script>
@@ -56,14 +57,14 @@
<script src="../js/id/svg.js"></script>
<script src="../js/id/svg/areas.js"></script>
<script src="../js/id/svg/labels.js"></script>
<script src="../js/id/svg/lines.js"></script>
<script src="../js/id/svg/midpoints.js"></script>
<script src="../js/id/svg/points.js"></script>
<script src="../js/id/svg/surface.js"></script>
<script src="../js/id/svg/tag_classes.js"></script>
<script src="../js/id/svg/turns.js"></script>
<script src="../js/id/svg/vertices.js"></script>
<script src="../js/id/svg/labels.js"></script>
<script src="../js/id/svg/restrictions.js"></script>
<script src="../js/id/ui.js"></script>
<script src='../js/id/ui/attribution.js'></script>
@@ -117,6 +118,7 @@
<script src='../js/id/actions/change_member.js'></script>
<script src='../js/id/actions/change_preset.js'></script>
<script src='../js/id/actions/change_tags.js'></script>
<script src='../js/id/actions/circularize.js'></script>
<script src='../js/id/actions/connect.js'></script>
<script src='../js/id/actions/delete_member.js'></script>
<script src='../js/id/actions/delete_multiple.js'></script>
@@ -130,13 +132,14 @@
<script src='../js/id/actions/merge_polygon.js'></script>
<script src='../js/id/actions/move_node.js'></script>
<script src='../js/id/actions/move.js'></script>
<script src='../js/id/actions/rotate_way.js'></script>
<script src='../js/id/actions/circularize.js'></script>
<script src='../js/id/actions/orthogonalize.js'></script>
<script src='../js/id/actions/straighten.js'></script>
<script src='../js/id/actions/noop.js'></script>
<script src='../js/id/actions/orthogonalize.js'></script>
<script src='../js/id/actions/restrict_turn.js'></script>
<script src='../js/id/actions/reverse.js'></script>
<script src='../js/id/actions/rotate_way.js'></script>
<script src='../js/id/actions/split.js'></script>
<script src='../js/id/actions/straighten.js'></script>
<script src='../js/id/actions/unrestrict_turn.js'></script>
<script src='../js/id/behavior.js'></script>
<script src='../js/id/behavior/add_way.js'></script>
@@ -229,12 +232,14 @@
<script src="spec/actions/move_node.js"></script>
<script src="spec/actions/move.js"></script>
<script src="spec/actions/noop.js"></script>
<script src="spec/actions/restrict_turn.js"></script>
<script src="spec/actions/reverse.js"></script>
<script src="spec/actions/split.js"></script>
<script src="spec/actions/unrestrict_turn.js"></script>
<script src="spec/geo/extent.js"></script>
<script src="spec/geo/intersection.js"></script>
<script src="spec/geo/multipolygon.js"></script>
<script src="spec/geo/turn.js"></script>
<script src="spec/core/connection.js"></script>
<script src="spec/core/graph.js"></script>
@@ -257,7 +262,6 @@
<script src="spec/svg/points.js"></script>
<script src="spec/svg/vertices.js"></script>
<script src="spec/svg/tag_classes.js"></script>
<script src="spec/svg/restrictions.js"></script>
<script src="spec/ui/inspector.js"></script>
<script src="spec/ui/raw_tag_editor.js"></script>
+2 -2
View File
@@ -47,10 +47,11 @@
<script src="spec/actions/noop.js"></script>
<script src="spec/actions/reverse.js"></script>
<script src="spec/actions/split.js"></script>
<script src="spec/actions/unrestrict_turn.js"></script>
<script src="spec/geo/extent.js"></script>
<script src="spec/geo/intersection.js"></script>
<script src="spec/geo/multipolygon.js"></script>
<script src="spec/geo/turn.js"></script>
<script src="spec/core/connection.js"></script>
<script src="spec/core/graph.js"></script>
@@ -73,7 +74,6 @@
<script src="spec/svg/points.js"></script>
<script src="spec/svg/vertices.js"></script>
<script src="spec/svg/tag_classes.js"></script>
<script src="spec/svg/restrictions.js"></script>
<script src="spec/ui/inspector.js"></script>
<script src="spec/ui/raw_tag_editor.js"></script>
+295
View File
@@ -0,0 +1,295 @@
describe("iD.actions.RestrictTurn", function() {
var projection = d3.geo.mercator().scale(250 / Math.PI);
it('adds a restriction to an unrestricted turn', function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*']}),
iD.Way({id: '-', nodes: ['*', 'w']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'},
restriction: 'no_right_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: 'way'});
});
it('splits the from way when necessary (forward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'x', way: '-'},
restriction: 'no_right_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: 'way'});
});
it('splits the from way when necessary (backward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'w', way: '=', newID: '=='},
via: {node: '*'},
to: {node: 'x', way: '-'},
restriction: 'no_left_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '-', type: 'way'});
});
it('splits the from way when necessary (straight on forward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'u', way: '=', newID: '=='},
via: {node: '*'},
to: {node: 'w', way: '='},
restriction: 'no_straight_on'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_straight_on'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: 'way'});
});
it('splits the from way when necessary (straight on backward)', function() {
// u<===*====w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['w', '*', 'u']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'u', way: '=', newID: '=='},
via: {node: '*'},
to: {node: 'w', way: '='},
restriction: 'no_straight_on'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_straight_on'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: 'way'});
});
it('splits the to way when necessary (forward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'x', way: '-'},
via: {node: '*'},
to: {node: 'w', way: '=', newID: '=='},
restriction: 'no_right_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_right_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '-', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: 'way'});
});
it('splits the to way when necessary (backward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'x', way: '-'},
via: {node: '*'},
to: {node: 'u', way: '='},
restriction: 'no_left_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_left_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '-', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: 'way'});
});
it('splits the from/to way of a U-turn (forward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'u', way: '='},
restriction: 'no_u_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_u_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '=', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '=', type: 'way'});
});
it('splits the from/to way of a U-turn (backward)', function() {
// u====*===>w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*', 'w']}),
iD.Way({id: '-', nodes: ['*', 'x']})
]),
action = iD.actions.RestrictTurn({
from: {node: 'w', way: '=', newID: '=='},
via: {node: '*'},
to: {node: 'w', way: '=', newID: '~~'},
restriction: 'no_u_turn'
}, projection, 'r');
graph = action(graph);
var r = graph.entity('r');
expect(r.tags).to.eql({type: 'restriction', restriction: 'no_u_turn'});
expect(_.pick(r.memberByRole('from'), 'id', 'type')).to.eql({id: '==', type: 'way'});
expect(_.pick(r.memberByRole('via'), 'id', 'type')).to.eql({id: '*', type: 'node'});
expect(_.pick(r.memberByRole('to'), 'id', 'type')).to.eql({id: '==', type: 'way'});
});
it('guesses the restriction type based on the turn angle', function() {
// u====*~~~~w
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u', loc: [-1, 0]}),
iD.Node({id: '*', loc: [ 0, 0]}),
iD.Node({id: 'w', loc: [ 0, 1]}),
iD.Node({id: 'x', loc: [ 0, -1]}),
iD.Way({id: '=', nodes: ['u', '*']}),
iD.Way({id: '-', nodes: ['*', 'x']}),
iD.Way({id: '~', nodes: ['*', 'w']})
]);
var r = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'x', way: '-'}
}, projection, 'r')(graph);
expect(r.entity('r').tags.restriction).to.equal('no_right_turn');
var l = iD.actions.RestrictTurn({
from: {node: 'x', way: '-'},
via: {node: '*'},
to: {node: 'u', way: '='}
}, projection, 'r')(graph);
expect(l.entity('r').tags.restriction).to.equal('no_left_turn');
var s = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '~'}
}, projection, 'r')(graph);
expect(s.entity('r').tags.restriction).to.equal('no_straight_on');
var u = iD.actions.RestrictTurn({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'u', way: '='}
}, projection, 'r')(graph);
expect(u.entity('r').tags.restriction).to.equal('no_u_turn');
});
});
+24
View File
@@ -0,0 +1,24 @@
describe("iD.actions.UnrestrictTurn", function() {
it('removes a restriction from a restricted turn', function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{id: '=', role: 'from', type: 'way'},
{id: '-', role: 'to', type: 'way'},
{id: '*', role: 'via', type: 'node'}
]})
]),
action = iD.actions.UnrestrictTurn({
restriction: 'r'
});
graph = action(graph);
expect(graph.hasEntity('r')).to.be.undefined;
});
});
+376
View File
@@ -0,0 +1,376 @@
describe("iD.geo.Intersection", function() {
describe('highways', function() {
it('excludes non-highways', function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*']}),
iD.Way({id: '-', nodes: ['*', 'w']})
]);
expect(iD.geo.Intersection(graph, '*').highways).to.eql([]);
});
it("excludes degenerate highways", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*'], tags: {highway: 'residential'}})
]);
expect(_.pluck(iD.geo.Intersection(graph, '*').highways, 'id')).to.eql(['=']);
});
it('includes line highways', function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w']})
]);
expect(_.pluck(iD.geo.Intersection(graph, '*').highways, 'id')).to.eql(['=']);
});
it('excludes area highways', function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'pedestrian', area: 'yes'}})
]);
expect(iD.geo.Intersection(graph, '*').highways).to.eql([]);
});
it('auto-splits highways at the intersection', function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*', 'w'], tags: {highway: 'residential'}})
]);
expect(_.pluck(iD.geo.Intersection(graph, '*').highways, 'id')).to.eql(['=-a', '=-b']);
});
});
describe('#turns', function() {
it("permits turns onto a way forward", function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
});
});
it("permits turns onto a way backward", function() {
// u====*<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
});
});
it("permits turns from a way that must be split", function() {
// w
// |
// u===*
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('w');
expect(turns.length).to.eql(3);
expect(turns[0]).to.eql({
from: {node: 'w', way: '-'},
via: {node: '*'},
to: {node: 'u', way: '='}
});
expect(turns[1]).to.eql({
from: {node: 'w', way: '-'},
via: {node: '*'},
to: {node: 'x', way: '-'}
});
expect(turns[2]).to.eql({
from: {node: 'w', way: '-'},
via: {node: '*'},
to: {node: 'w', way: '-'},
u: true
});
});
it("permits turns to a way that must be split", function() {
// w
// |
// u===*
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*', 'x'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(3);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
});
expect(turns[1]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'x', way: '-'}
});
expect(turns[2]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'u', way: '='},
u: true
});
});
it("permits turns from a oneway forward", function() {
// u===>v----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: 'yes'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns).to.eql([{
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
}]);
});
it("permits turns from a reverse oneway backward", function() {
// u<===*----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: '-1'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns).to.eql([{
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
}]);
});
it("omits turns from a oneway backward", function() {
// u<===*----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['*', 'u'], tags: {highway: 'residential', oneway: 'yes'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.Intersection(graph, '*').turns('u')).to.eql([]);
});
it("omits turns from a reverse oneway forward", function() {
// u===>*----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential', oneway: '-1'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.Intersection(graph, '*').turns('u')).to.eql([]);
});
it("permits turns onto a oneway forward", function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: 'yes'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
});
});
it("permits turns onto a reverse oneway backward", function() {
// u====*<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: '-1'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'}
});
});
it("omits turns onto a oneway backward", function() {
// u====*<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential', oneway: 'yes'}})
]);
expect(iD.geo.Intersection(graph, '*').turns('u').length).to.eql(1);
});
it("omits turns onto a reverse oneway forward", function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential', oneway: '-1'}})
]);
expect(iD.geo.Intersection(graph, '*').turns('u').length).to.eql(1);
});
it("includes U-turns", function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[1]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'u', way: '='},
u: true
});
});
it("restricts turns with a restriction relation", function() {
// u====*--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: '*'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['*', 'w'], tags: {highway: 'residential'}}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{id: '=', role: 'from', type: 'way'},
{id: '-', role: 'to', type: 'way'},
{id: '*', role: 'via', type: 'node'}
]})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(2);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'},
restriction: 'r'
});
});
it("restricts turns affected by an only_* restriction relation", function() {
// u====*~~~~v
// |
// w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Node({id: '*'}),
iD.Way({id: '=', nodes: ['u', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '~', nodes: ['v', '*'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', '*'], tags: {highway: 'residential'}}),
iD.Relation({id: 'r', tags: {type: 'restriction', restriction: 'only_right_turn'}, members: [
{id: '=', role: 'from', type: 'way'},
{id: '-', role: 'to', type: 'way'},
{id: '*', role: 'via', type: 'node'}
]})
]),
turns = iD.geo.Intersection(graph, '*').turns('u');
expect(turns.length).to.eql(3);
expect(turns[0]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'v', way: '~'},
restriction: 'r',
indirect_restriction: true
});
expect(turns[1]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'w', way: '-'},
restriction: 'r'
});
expect(turns[2]).to.eql({
from: {node: 'u', way: '='},
via: {node: '*'},
to: {node: 'u', way: '='},
restriction: 'r',
indirect_restriction: true,
u: true
});
});
});
});
-276
View File
@@ -1,276 +0,0 @@
describe("iD.geo.turns", function() {
it("returns an empty array for non-ways", function() {
var graph = iD.Graph([
iD.Node({id: 'n'})
]);
expect(iD.geo.turns(graph, 'n')).to.eql([]);
});
it("returns an empty array for non-lines", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', area: 'yes'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("returns an empty array for an unconnected way", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Way({id: '=', nodes: ['u', 'v']})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns onto degenerate ways", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns from non-highways", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v']}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns onto non-highways", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w']})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns onto non-lines", function() {
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w', 'x', 'v'], tags: {highway: 'residential', area: 'yes'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("permits turns onto a way forward", function() {
// u====v--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("permits turns onto a way backward", function() {
// u====v<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("permits turns onto a way in both directions", function() {
// w
// |
// u===v
// |
// x
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Node({id: 'x'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', 'v', 'x'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}, {
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('x')
}]);
});
it("permits turns from a oneway forward", function() {
// u===>v----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', oneway: 'yes'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("permits turns from a reverse oneway backward", function() {
// u<===v----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['v', 'u'], tags: {highway: 'residential', oneway: '-1'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("omits turns from a oneway backward", function() {
// u<===v----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['v', 'u'], tags: {highway: 'residential', oneway: 'yes'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns from a reverse oneway forward", function() {
// u===>v----w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential', oneway: '-1'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("permits turns onto a oneway forward", function() {
// u====v--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential', oneway: 'yes'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("permits turns onto a reverse oneway backward", function() {
// u====v<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential', oneway: '-1'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w')
}]);
});
it("omits turns onto a oneway backward", function() {
// u====v<---w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['w', 'v'], tags: {highway: 'residential', oneway: 'yes'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("omits turns onto a reverse oneway forward", function() {
// u====v--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential', oneway: '-1'}})
]);
expect(iD.geo.turns(graph, '=')).to.eql([]);
});
it("restricts turns with a restriction relation", function() {
// u====v--->w
var graph = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: '=', nodes: ['u', 'v'], tags: {highway: 'residential'}}),
iD.Way({id: '-', nodes: ['v', 'w'], tags: {highway: 'residential'}}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{id: '=', role: 'from', type: 'way'},
{id: '-', role: 'to', type: 'way'},
{id: 'v', role: 'via', type: 'node'}
]})
]);
expect(iD.geo.turns(graph, '=')).to.eql([{
from: graph.entity('='),
to: graph.entity('-'),
via: graph.entity('v'),
toward: graph.entity('w'),
restriction: graph.entity('r')
}]);
});
// U-turns
// Self-intersections
// Split point
});
-151
View File
@@ -1,151 +0,0 @@
describe("iD.svg.Restrictions", function() {
var restrictions = iD.svg.Restrictions({});
describe("#turns", function() {
it("returns an empty array with no selection", function() {
var graph = iD.Graph();
expect(restrictions.turns(graph, [])).to.eql([]);
});
it("returns an empty array with a multiselection", function() {
var graph = iD.Graph();
expect(restrictions.turns(graph, ['a', 'b'])).to.eql([]);
});
var valid = iD.Graph([
iD.Node({id: 'u'}),
iD.Node({id: 'v'}),
iD.Node({id: 'w'}),
iD.Way({id: 'f', nodes: ['u', 'v']}),
iD.Way({id: 't', nodes: ['v', 'w']}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{ role: 'via', id: 'v', type: 'node' },
{ role: 'from', id: 'f', type: 'way' },
{ role: 'to', id: 't', type: 'way' }
]})
]);
it("returns a valid restriction when the selected way has role 'from'", function() {
expect(restrictions.turns(valid, ['f'])).to.eql([valid.entity('r')]);
});
it("returns an empty array when the selected way has role 'to'", function() {
expect(restrictions.turns(valid, ['t'])).to.eql([]);
});
it("ignores restrictions missing a 'to' role", function() {
var graph = valid.replace(valid.entity('r').removeMembersWithID('t'));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions with an incomplete 'to' role", function() {
var graph = valid.remove(valid.entity('t'));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions missing a 'via' role", function() {
var graph = valid.replace(valid.entity('r').removeMembersWithID('v'));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions with an incomplete 'via' role", function() {
var graph = valid.remove(valid.entity('v'));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'from' role is not a way", function() {
var graph = valid.replace(iD.Node({id: 'f2'}))
.replace(valid.entity('r').replaceMember({id: 'f'}, {id: 'f2', type: 'node'}));
expect(restrictions.turns(graph, ['f2'])).to.eql([]);
});
it("ignores restrictions whose 'to' role is not a way", function() {
var graph = valid.replace(iD.Node({id: 't2'}))
.replace(valid.entity('r').replaceMember({id: 't'}, {id: 't2', type: 'node'}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'via' role is not a node", function() {
var graph = valid.replace(iD.Way({id: 'v2'}))
.replace(valid.entity('r').replaceMember({id: 'v'}, {id: 'v2', type: 'way'}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'from' role does not start or end with the via node", function() {
var graph = valid.replace(valid.entity('f').update({nodes: ['o']}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'to' role does not start or end with the via node", function() {
var graph = valid.replace(valid.entity('t').update({nodes: ['o']}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'from' role has less than two nodes", function() {
var graph = valid.replace(valid.entity('f').update({nodes: ['v']}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restrictions whose 'to' role has less than two nodes", function() {
var graph = valid.replace(valid.entity('t').update({nodes: ['v']}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
it("ignores restriction subtypes", function() {
var graph = valid.replace(valid.entity('r').update({tags: {type: 'restriction:hgv'}}));
expect(restrictions.turns(graph, ['f'])).to.eql([]);
});
});
describe("#datum", function() {
function projection(x) { return x; }
it("calculates the angle of a forward 'to' role", function() {
// w---x--->y
// |
// u====>v
// From = to - via v
var graph = iD.Graph([
iD.Node({id: 'u', loc: [0, 0]}),
iD.Node({id: 'v', loc: [1, 0]}),
iD.Node({id: 'w', loc: [1, 1]}),
iD.Node({id: 'x', loc: [2, 1]}),
iD.Node({id: 'y', loc: [3, 1]}),
iD.Way({id: '=', nodes: ['u', 'v']}),
iD.Way({id: '-', nodes: ['v', 'w', 'x', 'y']}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{ role: 'via', id: 'v', type: 'node' },
{ role: 'from', id: '=', type: 'way' },
{ role: 'to', id: '-', type: 'way' }
]})
]);
expect(restrictions.datum(graph, graph.entity('='), graph.entity('r'), projection).angle).to.eql(Math.PI / 2);
});
it("calculates the angle of a reverse 'to' role", function() {
// w<---x---y
// |
// u====>v
// From = to - via v
var graph = iD.Graph([
iD.Node({id: 'u', loc: [0, 0]}),
iD.Node({id: 'v', loc: [1, 0]}),
iD.Node({id: 'w', loc: [1, 1]}),
iD.Node({id: 'x', loc: [2, 1]}),
iD.Node({id: 'y', loc: [3, 1]}),
iD.Way({id: '=', nodes: ['u', 'v']}),
iD.Way({id: '-', nodes: ['y', 'x', 'w', 'v']}),
iD.Relation({id: 'r', tags: {type: 'restriction'}, members: [
{ role: 'via', id: 'v', type: 'node' },
{ role: 'from', id: '=', type: 'way' },
{ role: 'to', id: '-', type: 'way' }
]})
]);
expect(restrictions.datum(graph, graph.entity('='), graph.entity('r'), projection).angle).to.eql(Math.PI / 2);
});
});
});