Merge branch 'master' into validation

# Conflicts:
#	data/core.yaml
#	dist/locales/en.json
#	modules/ui/commit_warnings.js
#	modules/ui/entity_editor.js
#	modules/util/index.js
#	modules/util/util.js
#	modules/validations/index.js
#	modules/validations/many_deletions.js
#	modules/validations/missing_tag.js
This commit is contained in:
Quincy Morgan
2019-01-14 10:13:56 -05:00
119 changed files with 4618 additions and 635 deletions
+5
View File
@@ -2,6 +2,11 @@
host = https://www.transifex.com
minimum_perc = 1
[id-editor.community]
file_filter = .tx/tmp/community/<lang>.yaml
source_lang = en
type = YAML
[id-editor.core]
file_filter = .tx/tmp/core/<lang>.yaml
source_file = data/core.yaml
+1 -1
View File
@@ -431,7 +431,7 @@ Then change to the master branch and get everything from upstream (the main repo
If you want to submit Documentation, Spelling improvements, etc. which do not need testing,
you can do this with your browser in GitHub. Please don't use this to change Code and create untested Pull Requests.
You also need a GitHub account and may find this [Article about Editing](https://help.github.com/articles/editing-files-in-another-user-s-repository/) and this [Article about Pull Requests](https://help.github.com/articles/about-pull-requests/) usefull.
You also need a GitHub account and may find this [Article about Editing](https://help.github.com/articles/editing-files-in-another-user-s-repository/) and this [Article about Pull Requests](https://help.github.com/articles/about-pull-requests/) useful.
### Step by Step with Browser
+1 -1
View File
@@ -50,7 +50,7 @@ To run the current development version of iD on your own computer:
#### Cloning the repository
The repository is reasonably large, and it's unlikely that you need the full history. If you are happy to wait for it all to download, run:
The repository is reasonably large, and it's unlikely that you need the full history (~200 MB). If you are happy to wait for it all to download, run:
```
git clone https://github.com/openstreetmap/iD.git
+6 -1
View File
@@ -533,7 +533,12 @@ function writeFaIcons(faIcons) {
var prefix = key.substring(0, 3); // `fas`, `far`, `fab`
var name = key.substring(4);
var def = fontawesome.findIconDefinition({ prefix: prefix, iconName: name });
writeFileProm('svg/fontawesome/' + key + '.svg', fontawesome.icon(def).html);
try {
writeFileProm('svg/fontawesome/' + key + '.svg', fontawesome.icon(def).html);
} catch (error) {
console.error('Error: No FontAwesome icon for ' + key);
throw (error);
}
}
}
+13 -3
View File
@@ -31,7 +31,9 @@
/* No interactivity except what we specifically allow */
.layer-osm * {
.data-layer.osm *,
.data-layer.notes *,
.data-layer.keepRight * {
pointer-events: none;
}
@@ -42,6 +44,8 @@
/* `.target` objects are interactive */
/* They can be picked up, clicked, hovered, or things can connect to them */
.kr_error.target,
.note.target,
.node.target,
.turn .target {
pointer-events: fill;
@@ -75,8 +79,11 @@
pointer-events: none !important;
}
/* NOTE: when more QA layers are added, replace kr_error with generic QA layer selector */
/* points, notes & QA */
/* points & notes */
/* points, notes, markers */
g.kr_error .stroke,
g.note .stroke {
stroke: #222;
stroke-width: 1;
@@ -84,6 +91,7 @@ g.note .stroke {
opacity: 0.6;
}
g.kr_error.active .stroke,
g.note.active .stroke {
stroke: #222;
stroke-width: 1;
@@ -97,6 +105,7 @@ g.point .stroke {
fill: #fff;
}
g.kr_error .shadow,
g.point .shadow,
g.note .shadow {
fill: none;
@@ -105,13 +114,14 @@ g.note .shadow {
stroke-opacity: 0;
}
g.note.related:not(.selected) .shadow,
g.kr_error.hover:not(.selected) .shadow,
g.note.hover:not(.selected) .shadow,
g.point.related:not(.selected) .shadow,
g.point.hover:not(.selected) .shadow {
stroke-opacity: 0.5;
}
g.kr_error.selected .shadow,
g.note.selected .shadow,
g.point.selected .shadow {
stroke-opacity: 0.7;
+5
View File
@@ -96,7 +96,12 @@
cursor: url(img/cursor-draw.png) 9 9, crosshair; /* FF */
}
.mode-browse .note,
.mode-browse .kr_error,
.mode-select .note,
.mode-select .kr_error,
.turn rect,
.turn circle {
cursor: pointer;
}
+83 -22
View File
@@ -1,39 +1,27 @@
/* OSM Notes Layer */
.layer-notes {
pointer-events: none;
}
.layer-notes .note * {
pointer-events: none;
}
.mode-browse .layer-notes .note .note-fill,
.mode-select .layer-notes .note .note-fill,
.mode-select-data .layer-notes .note .note-fill,
.mode-select-note .layer-notes .note .note-fill {
pointer-events: visible;
cursor: pointer; /* Opera */
cursor: url(img/cursor-select-point.png), pointer; /* FF */
/* OSM Notes and KeepRight Layers */
.kr_error-header-icon .kr_error-fill,
.layer-keepRight .kr_error .kr_error-fill {
stroke: #333;
stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */
}
.note-header-icon .note-shadow,
.layer-notes .note .note-shadow {
color: #000;
}
.note-header-icon .note-fill,
.layer-notes .note .note-fill {
color: #ff3300;
color: #f30;
stroke: #333;
stroke-width: 40px;
}
.note-header-icon.new .note-fill,
.layer-notes .note.new .note-fill {
color: #ffee00;
color: #fe0;
stroke: #333;
stroke-width: 40px;
}
.note-header-icon.closed .note-fill,
.layer-notes .note.closed .note-fill {
color: #55dd00;
color: #5d0;
stroke: #333;
stroke-width: 40px;
}
@@ -54,8 +42,81 @@
}
/* Custom Map Data (geojson, gpx, kml, vector tile) */
/* Keep Right Errors
------------------------------------------------------- */
.kr_error_type_20, /* multiple nodes on same spot */
.kr_error_type_40, /* impossible oneways */
.kr_error_type_210, /* self intersecting ways */
.kr_error_type_270, /* unusual motorway connection */
.kr_error_type_310, /* roundabout issues */
.kr_error_type_320, /* improper _link */
.kr_error_type_350 { /* improper bridge tag */
color: #ff9;
}
.kr_error_type_50 { /* almost junctions */
color: #88f;
}
.kr_error_type_60, /* deprecated tags */
.kr_error_type_70, /* tagging issues */
.kr_error_type_90, /* motorway without ref */
.kr_error_type_100, /* place of worship without religion */
.kr_error_type_110, /* poi without name */
.kr_error_type_150, /* railway crossing without tag */
.kr_error_type_220, /* misspelled tag */
.kr_error_type_380 { /* non-physical sport tag */
color: #5d0;
}
.kr_error_type_130 { /* disconnected ways */
color: #fa3;
}
.kr_error_type_170 { /* FIXME tag */
color: #ff0;
}
.kr_error_type_190 { /* intersection without junction */
color: #f33;
}
.kr_error_type_200 { /* overlapping ways */
color: #fdbf6f;
}
.kr_error_type_160, /* railway layer conflict */
.kr_error_type_230 { /* layer conflict */
color: #b60;
}
.kr_error_type_280 { /* boundary issues */
color: #5f47a0;
}
.kr_error_type_180, /* relation without type */
.kr_error_type_290 { /* turn restriction issues */
color: #ace;
}
.kr_error_type_300, /* missing maxspeed */
.kr_error_type_390 { /* missing tracktype */
color: #090;
}
.kr_error_type_360, /* language unknown */
.kr_error_type_370, /* doubled places */
.kr_error_type_410 { /* website issues */
color: #f9b;
}
.kr_error_type_120, /* way without nodes */
.kr_error_type_400 { /* geometry / turn angles */
color: #c35;
}
/* Custom Map Data (geojson, gpx, kml, vector tile) */
.layer-mapdata {
pointer-events: none;
}
+90 -47
View File
@@ -586,6 +586,7 @@ button.add-note svg.icon {
.field-help-title button.close,
.sidebar-component .header button.data-editor-close,
.sidebar-component .header button.note-editor-close,
.sidebar-component .header button.keepRight-editor-close,
.entity-editor-pane .header button.preset-close,
.preset-list-pane .header button.preset-choose {
position: absolute;
@@ -595,6 +596,7 @@ button.add-note svg.icon {
[dir='rtl'] .field-help-title button.close,
[dir='rtl'] .sidebar-component .header button.data-editor-close,
[dir='rtl'] .sidebar-component .header button.note-editor-close,
[dir='rtl'] .sidebar-component .header button.keepRight-editor-close,
[dir='rtl'] .entity-editor-pane .header button.preset-close,
[dir='rtl'] .preset-list-pane .header button.preset-choose {
left: 0;
@@ -782,10 +784,6 @@ a.hide-toggle {
bottom: 0;
}
.inspector-border {
border-bottom: 1px solid #ccc
}
.feature-list-pane .inspector-body,
.preset-list-pane .inspector-body {
top: 120px;
@@ -796,7 +794,7 @@ a.hide-toggle {
}
.inspector-inner {
padding: 20px;
padding: 20px 20px 5px 20px;
position: relative;
}
@@ -849,6 +847,10 @@ a.hide-toggle {
border-radius: 2px;
}
[dir='rtl'] .geocode-item {
left: -25%;
}
.geocode-item:hover {
background-color: #aaa;
}
@@ -1155,11 +1157,30 @@ img.tag-reference-wiki-image {
}
/* Quick links
------------------------------------------------------- */
.quick-links {
display: flex;
flex-flow: row wrap;
justify-content: flex-end;
padding: 0 20px;
}
.quick-link {
margin: 0 5px;
}
.data-editor .quick-links,
.keepRight-editor .quick-links,
.note-editor .quick-links {
padding: 5px 0 0 0;
}
/* Entity/Preset Editor
------------------------------------------------------- */
.preset-editor {
overflow: hidden;
padding-bottom: 10px;
padding: 10px 0px 5px 0px;
}
.preset-editor a.hide-toggle {
margin: 0 20px 5px 20px;
@@ -2254,6 +2275,12 @@ div.combobox {
padding-left: 10px;
}
[dir='rtl'] .raw-member-editor .member-row .member-entity-name,
[dir='rtl'] .raw-membership-editor .member-row .member-entity-name {
padding-left:0;
padding-right: 10px;
}
.form-field-input-member > input.member-role {
border-radius: 0 0 0 4px;
}
@@ -2354,6 +2381,7 @@ input.key-trap {
/* hide but preserve in layout */
.inspector-hover .combobox-caret,
.inspector-hover .header button,
.inspector-hover .quick-links,
.inspector-hover .form-field-input-multicombo .chips .remove,
.inspector-hover .hide-toggle:before,
.inspector-hover .more-fields,
@@ -2430,9 +2458,10 @@ input.key-trap {
}
/* OSM Note Editor
/* OSM Note / KeepRight Editors
------------------------------------------------------- */
.note-header {
.note-header,
.kr_error-header {
background-color: #f6f6f6;
border-radius: 5px;
border: 1px solid #ccc;
@@ -2441,7 +2470,8 @@ input.key-trap {
align-items: center;
}
.note-header-icon {
.note-header-icon,
.kr_error-header-icon {
background-color: #fff;
padding: 10px;
flex: 0 0 62px;
@@ -2451,18 +2481,21 @@ input.key-trap {
border-right: 1px solid #ccc;
border-radius: 5px 0 0 5px;
}
[dir='rtl'] .note-header-icon {
[dir='rtl'] .note-header-icon,
[dir='rtl'] .kr_error-header-icon {
border-right: unset;
border-left: 1px solid #ccc;
border-radius: 0 5px 5px 0;
}
.note-header-icon .icon-wrap {
.note-header-icon .icon-wrap,
.kr_error-header-icon .icon-wrap {
position: absolute;
top: 0px;
}
.note-header-label {
.note-header-label,
.kr_error-header-label {
background-color: #f6f6f6;
padding: 0 15px;
flex: 1 1 100%;
@@ -2470,7 +2503,8 @@ input.key-trap {
font-weight: bold;
border-radius: 0 5px 5px 0;
}
[dir='rtl'] .note-header-label {
[dir='rtl'] .note-header-label,
[dir='rtl'] .kr_error-header-label {
border-radius: 5px 0 0 5px;
}
@@ -2536,17 +2570,24 @@ input.key-trap {
border-left: none;
}
.note-save {
.note-save,
.keepRight-save {
padding-top: 20px;
}
.kr_error-details,
.kr_error-comment-container {
padding: 10px;
}
.note-save #new-comment-input {
.keepRight-save .new-comment-input,
.note-save .new-comment-input {
width: 100%;
height: 100px;
max-height: 300px;
min-height: 100px;
}
.keepRight-save .detail-section,
.note-save .detail-section {
margin: 10px 0;
}
@@ -2555,6 +2596,21 @@ input.key-trap {
float: right;
}
.kr_error-details-container {
background: #ececec;
padding: 10px;
margin-top: 20px;
border-radius: 4px;
border: 1px solid #ccc;
}
.kr_error-details-description {
margin-bottom: 10px;
}
.kr_error-details-description-text::first-letter {
text-transform: capitalize;
}
/* Custom Data Editor
------------------------------------------------------- */
@@ -2864,7 +2920,6 @@ div.full-screen > button:hover {
.entity-issues {
padding: 0 20px 20px 20px;
margin-bottom: 20px;
}
.entity-issues .issue {
border-radius: 4px;
@@ -4380,29 +4435,24 @@ svg.mouseclick use.right {
font-size: 12px;
white-space: initial;
}
.tooltip.in {
opacity: 0.9;
z-index: 1030;
height: auto;
display: block;
}
.tooltip.top {
margin-top: -20px;
text-align: center;
}
.tooltip.right {
margin-left: 20px;
text-align: left;
}
.tooltip.bottom {
margin-top: 20px;
text-align: center;
}
.tooltip.left {
margin-left: -20px;
text-align: right;
@@ -4427,7 +4477,6 @@ svg.mouseclick use.right {
position: absolute;
background: transparent;
}
.tail::after {
content: "";
position: absolute;
@@ -4474,7 +4523,6 @@ svg.mouseclick use.right {
border-color: transparent;
border-style: solid;
}
.tooltip.top .tooltip-arrow {
bottom: -5px;
left: 50%;
@@ -4482,7 +4530,6 @@ svg.mouseclick use.right {
border-top-color: #fff;
border-width: 5px 5px 0;
}
.tooltip.right .tooltip-arrow {
top: 50%;
left: -5px;
@@ -4490,7 +4537,6 @@ svg.mouseclick use.right {
border-right-color: #fff;
border-width: 5px 5px 5px 0;
}
.tooltip.left .tooltip-arrow {
top: 50%;
right: -5px;
@@ -4498,7 +4544,6 @@ svg.mouseclick use.right {
border-left-color: #fff;
border-width: 5px 0 5px 5px;
}
.tooltip.bottom .tooltip-arrow {
top: -5px;
left: 50%;
@@ -4509,7 +4554,7 @@ svg.mouseclick use.right {
.tooltip-heading {
font-weight: bold;
background: #F6F6F6;
background: #f6f6f6;
padding: 10px;
margin: -10px -10px 10px -10px;
border-radius: 3px 3px 0 0;
@@ -4517,52 +4562,50 @@ svg.mouseclick use.right {
}
.keyhint-wrap {
background: #F6F6F6;
background: #f6f6f6;
padding: 10px;
margin: 10px -10px -10px -10px;
border-radius: 0 0 3px 3px;
}
.tooltip-inner .keyhint {
font-weight: bold;
margin-left: 5px;
}
/* Exceptions for tooltip layouts */
[dir='rtl'] .tooltip-inner .keyhint {
margin-left: 0;
margin-right: 5px;
}
/* make tooltips in panels dark */
/* dark tooltips for sidebar / panels */
.map-pane .tooltip.top .tooltip-arrow,
.entity-editor-pane .tooltip.top .tooltip-arrow,
.warning-section .tooltip.top .tooltip-arrow {
#sidebar .tooltip.top .tooltip-arrow {
border-top-color: #000;
}
.map-pane .tooltip.bottom .tooltip-arrow,
.entity-editor-pane .tooltip.bottom .tooltip-arrow,
.warning-section .tooltip.bottom .tooltip-arrow {
#sidebar .tooltip.bottom .tooltip-arrow {
border-bottom-color: #000;
}
.map-pane .tooltip.left .tooltip-arrow,
.entity-editor-pane .tooltip.left .tooltip-arrow,
.warning-section .tooltip.left .tooltip-arrow {
#sidebar .tooltip.left .tooltip-arrow {
border-left-color: #000;
}
.map-pane .tooltip.right .tooltip-arrow,
.entity-editor-pane .tooltip.right .tooltip-arrow,
.warning-section .tooltip.right .tooltip-arrow {
#sidebar .tooltip.right .tooltip-arrow {
border-right-color: #000;
}
.map-pane .tooltip-inner,
.map-pane .tooltip-heading,
.map-pane .keyhint-wrap,
.entity-editor-pane .tooltip-inner,
.warning-section .tooltip-inner {
#sidebar .tooltip-inner,
#sidebar .tooltip-heading,
#sidebar .keyhint-wrap {
background: #000;
color: #ccc;
}
/* Exceptions for tooltip layouts */
/* commit warning tooltips need to be closer */
.warning-section .tooltip.top {
margin-top: -5px;
@@ -4578,11 +4621,11 @@ svg.mouseclick use.right {
/* Move over tooltips that are near the edge of screen */
button.sidebar-toggle .tooltip .tooltip-arrow {
left: 32px;
left: 36px;
}
[dir='rtl'] button.sidebar-toggle .tooltip .tooltip-arrow {
left: auto;
right: 32px;
right: 36px;
}
li:first-of-type .badge .tooltip,
+260 -1
View File
@@ -403,11 +403,18 @@ en:
title: Show My Location
locating: "Locating, please wait..."
inspector:
zoom_to:
key: Z
title: Zoom to this
tooltip_feature: "Center and zoom the map to focus on this feature."
tooltip_note: "Center and zoom the map to focus on this note."
tooltip_data: "Center and zoom the map to focus on this data."
tooltip_issue: "Center and zoom the map to focus on this issue."
no_documentation_combination: There is no documentation available for this tag combination
no_documentation_key: There is no documentation available for this key
documentation_redirect: This documentation has been redirected to a new page
show_more: Show More
view_on_osm: View on openstreetmap.org
view_on_keepRight: View on keepright.at
all_fields: All fields
all_tags: All tags
all_members: All members
@@ -418,6 +425,7 @@ en:
choose: Select feature type
results: "{n} results for {search}"
reference: View on OpenStreetMap Wiki
edit_reference: Edit or translate on OSM Wiki
back_tooltip: Change feature
remove: Remove
search: Search
@@ -479,6 +487,9 @@ en:
notes:
tooltip: Note data from OpenStreetMap
title: OpenStreetMap notes
keepRight:
tooltip: Automatically detected map issues from keepright.at
title: KeepRight Issues
custom:
tooltip: "Drag and drop a data file onto the page, or click the button to setup"
title: Custom Map Data
@@ -627,6 +638,246 @@ en:
out: Zoom out
cannot_zoom: "Cannot zoom out further in current mode."
full_screen: Toggle Full Screen
QA:
keepRight:
title: KeepRight Error
detail_title: Error
detail_description: Description
comment: Comment
comment_placeholder: Enter a comment to share with other users.
close: Close (Error Fixed)
ignore: Ignore (Not an Error)
save_comment: Save Comment
close_comment: Close and Comment
ignore_comment: Ignore and Comment
error_parts:
node: 'this node'
way: 'this way'
relation: 'this relation'
oneway: 'this oneway'
highway: 'this highway'
railway: 'this railway'
waterway: 'this waterway'
cycleway: 'this cycleway'
cycleway_footpath: 'this cycleway/footpath'
riverbank: 'this riverbank'
crossing: 'this crossing'
railway_crossing: 'this railway crossing'
bridge: 'this bridge'
tunnel: 'this tunnel'
boundary: 'this boundary'
turn_restriction: 'this turn restriction'
roundabout: 'this roundabout'
mini_roundabout: 'this mini-roundabout'
track: 'this track'
feature: 'this feature'
place_of_worship: 'place of worship'
pub: 'pub'
restaurant: 'restaurant'
school: 'school'
university: 'university'
hospital: 'hospital'
library: 'library'
theatre: 'theatre'
courthouse: 'courthouse'
bank: 'bank'
cinema: 'cinema'
pharmacy: 'pharmacy'
cafe: 'cafe'
fast_food: 'fast food'
fuel: 'fuel'
from: 'from'
to: 'to'
left_hand: 'left-hand'
right_hand: 'right-hand'
errorTypes:
20:
title: 'Multiple nodes on the same spot'
description: 'There is more than one node in this spot. Node IDs: {var1}.'
30:
title: 'Non-closed area'
description: '{var1} is tagged with "{var2}" and should be a closed loop.'
40:
title: 'Impossible oneway'
description: 'The first node {var1} of {var2} is not connected to any other way.'
41:
description: 'The last node {var1} of {var2} is not connected to any other way.'
42:
description: 'You cannot reach {var1} because all ways leading from it are oneway.'
43:
description: 'You cannot escape from {var1} because all ways leading to it are oneway.'
50:
title: 'Almost junction'
description: '{var1} is very close but not connected to way {var2}.'
60:
title: 'Deprecated tag'
description: '{var1} uses deprecated tag "{var2}". Please use "{var3}" instead.'
70:
title: 'Missing tag'
description: '{var1} has an empty tag: "{var2}".'
71:
description: '{var1} has no tags.'
72:
description: '{var1} is not member of any way and doesn''t have any tags.'
73:
description: '{var1} has a "{var2}" tag but no "highway" tag.'
74:
description: '{var1} has an empty tag: "{var2}".'
75:
description: '{var1} has a name "{var2}" but no other tags.'
90:
title: 'Motorway without ref tag'
description: '{var1} is tagged as a motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.'
100:
title: 'Place of worship without religion'
description: '{var1} is tagged as a place of worship and therefore needs a religion tag.'
110:
title: 'Point of interest without name'
description: '{var1} is tagged as a "{var2}" and therefore needs a name tag.'
120:
title: 'Way without nodes'
description: '{var1} has just one single node.'
130:
title: 'Disconnected way'
description: '{var1} is not connected to the rest of the map.'
150:
title: 'Railway crossing without tag'
description: '{var1} of a highway and a railway needs to be tagged as "railway=crossing" or "railway=level_crossing".'
160:
title: 'Railway layer conflict'
description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at {var1}.'
170:
title: 'FIXME tagged item'
description: '{var1} has a FIXME tag: {var2}'
180:
title: 'Relation without type'
description: '{var1} is missing a "type" tag.'
190:
title: 'Intersection without junction'
description: '{var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel.'
200:
title: 'Overlapping ways'
description: '{var1} overlaps the {var2} {var3}.'
210:
title: 'Self-intersecting way'
description: 'There is an unspecified issue with self intersecting ways.'
211:
description: '{var1} contains more than one node multiple times. Nodes are {var2}. This may or may not be an error.'
212:
description: '{var1} has only two different nodes and contains one of them more than once.'
220:
title: 'Misspelled tag'
description: '{var1} is tagged "{var2}" where "{var3}" looks like "{var4}".'
221:
description: '{var1} has a suspicious tag "{var2}".'
230:
title: 'Layer conflict'
description: '{var1} is a junction of ways on different layers.'
231:
description: '{var1} is a junction of ways on different layers: {var2}.'
layer: '(layer: {layer})'
232:
description: '{var1} is tagged with "layer={var2}". This need not be an error but it looks strange.'
270:
title: 'Unusual motorway connection'
description: '{var1} is a junction of a motorway and a highway other than "motorway", "motorway_link", "trunk", "rest_area", or "construction". Connection to "service" or "unclassified" is only valid if it has "access=no/private", or it leads to a motorway service area, or if it is a "service=parking_aisle".'
280:
title: 'Boundary issue'
description: 'There is an unspecified issue with this boundary.'
281:
title: 'Boundary missing name'
description: '{var1} has no name.'
282:
title: 'Boundary missing admin level'
description: 'The boundary of {var1} has no valid numeric admin_level. Please do not mix admin levels (e.g. "6;7"). Always tag the lowest admin_level of all boundaries.'
283:
title: 'Boundary not a closed loop'
description: 'The boundary of {var1} is not a closed loop.'
284:
title: 'Boundary is split'
description: 'The boundary of {var1} splits here.'
285:
title: 'Boundary admin_level too high'
description: '{var1} has "admin_level={var2}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.'
290:
title: 'Restriction issue'
description: 'There is an unspecified issue with this restriction.'
291:
title: 'Restriction missing type'
description: '{var1} has an unrecognized restriction type.'
292:
title: 'Restriction missing "from" way'
description: '{var1} has {var2} "from" members, but it should have 1.'
293:
title: 'Restriction missing "to" way'
description: '{var1} has {var2} "to" members, but it should have 1.'
294:
title: 'Restriction "from" or "to" is not a way'
description: '{var1} has "from" or "to" members which should be ways. {var2}.'
295:
title: 'Restriction "via" is not an endpoint'
description: '{var1} has a "via" (node {var2}) which is not the first or the last member of "{var3}" (way {var4}).'
296:
title: 'Unusual restriction angle'
description: '{var1} has a restriction type "{var2}" but the angle is {var3} degrees. Maybe the restriction type is not appropriate?'
297:
title: 'Wrong direction of to member'
description: '{var1} does not match the direction of "to" way {var2}.'
298:
title: 'Redundant restriction - oneway'
description: '{var1} may be redundant. Entry already prohibited by "oneway" tag on {var2}.'
300:
title: 'Missing maxspeed'
description: '{var1} is missing a "maxspeed" tag and is tagged as motorway, trunk, primary, or secondary.'
310:
title: 'Roundabout issue'
description: 'There is an unspecified issue with this roundabout.'
311:
title: 'Roundabout not closed loop'
description: '{var1} is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout).'
312:
title: 'Roundabout wrong direction'
description: 'If {var1} is in a country with {var2} traffic then its orientation goes the wrong way around.'
313:
title: 'Roundabout weakly connected'
description: '{var1} has only {var2} other road(s) connected. Roundabouts typically have 3 or more.'
320:
title: 'Improper link connection'
description: '{var1} is tagged as "{var2}" but doesn''t have a connection to any other "{var3}" or "{var4}".'
350:
title: 'Improper bridge tag'
description: '{var1} doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var2}.'
360:
title: 'Missing local name tag'
description: 'It would be nice if {var1} had a local name tag "name:XX={var2}" where XX shows the language of its common name "{var2}".'
370:
title: 'Doubled places'
description: '{var1} has tags in common with the surrounding way {var2} {var3} and seems to be redundant.'
including_the_name: "(including the name {name})"
380:
title: 'Non-physical use of sport tag'
description: '{var1} is tagged "{var2}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway").'
390:
title: 'Missing tracktype'
description: '{var1} doesn''t have a "tracktype" tag.'
400:
title: 'Geometry issue'
description: 'There is an unspecified issue with the geometry here.'
401:
title: 'Missing turn restriction'
description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning.'
402:
title: 'Impossible angle'
description: '{var1} bends in a very sharp angle here.'
410:
title: 'Website issue'
description: 'There is an unspecified issue with a contact website or URL.'
411:
description: '{var1} may have an outdated URL: {var2} returned HTTP status code {var3}.'
412:
description: '{var1} may have an outdated URL: {var2} contained suspicious text "{var3}".'
413:
description: '{var1} may have an outdated URL: {var2} did not contain keywords "{var3}".'
streetside:
tooltip: "Streetside photos from Microsoft"
title: "Photo Overlay (Bing Streetside)"
@@ -834,6 +1085,13 @@ en:
using: "To use a GPS trace for mapping, drag and drop the data file onto the map editor. If it's recognized, it will be drawn on the map as a bright purple line. Click the {data} **Map data** panel on the side of the map to enable, disable, or zoom to your GPS data."
tracing: "The GPS track isn't sent to OpenStreetMap - the best way to use it is to draw on the map, using it as a guide for the new features that you add."
upload: "You can also [upload your GPS data to OpenStreetMap](https://www.openstreetmap.org/trace/create) for other users to use."
qa:
title: Quality Assurance
intro: "*Quality Assurance* (Q/A) 3rd party tools help lead to better quality of OSM data. They list automatically deteted bugs, conflics, and issues with the data, which mappers can then go and fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer."
tools_h: "Tools"
tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/), [ImproveOSM](https://improveosm.org/en/), and more Q/A tools in the future."
issues_h: "Handling Issues"
issues: "Handling Q/A issues is similar to handling notes. Clicking an existing Q/A issue populates the sidebar with details on the issue type and related features. Each tool has its own capabilities, but generally you can comment and/or close an issue. Expect iD to support a 'fix me' button to automatically fix simple issues in the future."
field:
restrictions:
title: Turn Restrictions Help
@@ -1238,6 +1496,7 @@ en:
with_selected:
title: "With feature selected"
edit_menu: "Toggle edit menu"
zoom_to: "Zoom to selected feature"
vertex_selected:
title: "With node selected"
previous: "Jump to previous node"
+497
View File
@@ -0,0 +1,497 @@
{
"localizeStrings": {
"this node": "node",
"this way": "way",
"this relation": "relation",
"this one-way": "oneway",
"this highway": "highway",
"this railway": "railway",
"this waterway": "waterway",
"this cycleway": "cycleway",
"this footpath": "footpath",
"this cycleway/footpath": "cycleway_footpath",
"this riverbank": "riverbank",
"this crossing": "crossing",
"this railway crossing": "railway_crossing",
"this bridge": "bridge",
"this tunnel": "tunnel",
"this boundary": "boundary",
"this turn-restriction": "turn_restriction",
"this roundabout": "roundabout",
"this mini-roundabout": "mini_roundabout",
"this track": "track",
"this feature": "feature",
"place_of_worship": "place_of_worship",
"pub": "pub",
"restaurant": "restaurant",
"school": "school",
"university": "university",
"hospital": "hospital",
"library": "library",
"theatre": "theatre",
"courthouse": "courthouse",
"bank": "bank",
"cinema": "cinema",
"pharmacy": "pharmacy",
"cafe": "cafe",
"fast_food": "fast_food",
"fuel": "fuel",
"from": "from",
"to": "to",
"left-hand": "left_hand",
"right-hand": "right_hand"
},
"errorTypes": {
"20": {
"title": "multiple nodes on the same spot",
"severity": "warning",
"description": "There is more than one node in this spot. Offending node IDs: $1",
"IDs": ["20"],
"regex": "IDs: ((?:#\\d+,?)+)"
},
"30": {
"title": "non-closed_areas",
"severity": "error",
"description": "This way is tagged with '$1' and should be closed-loop.",
"IDs": ["this", ""],
"regex": "(this way) is tagged with '(.+)'"
},
"40": {
"title": "dead-ended one-ways",
"severity": "error",
"description": "The first node (id $1) of this one-way is not connected to any other way",
"IDs": ["n", "this"],
"regex": "\\(id (\\d+)\\) of (this one-way)"
},
"41": {
"title": "",
"severity": "error",
"description": "The last node (id $1) of this one-way is not connected to any other way",
"IDs": ["n", "this"],
"regex": "\\(id (\\d+)\\) of (this one-way)"
},
"42": {
"title": "",
"severity": "error",
"description": "This node cannot be reached because one-ways only lead away from here",
"IDs": ["this"],
"regex": "(this node)"
},
"43": {
"title": "",
"severity": "error",
"description": "You cannot escape from this node because one-ways only lead to here",
"IDs": ["this"],
"regex": "(this node)"
},
"50": {
"title": "almost-junctions",
"severity": "error",
"description": "This node is very close but not connected to way #$1",
"IDs": ["this", "w"],
"regex": "(this node) is very close but not connected to way #(\\d+)"
},
"60": {
"title": "deprecated tags",
"severity": "warning",
"description": "This $1 uses deprecated tag $2. Please use $3 instead!",
"IDs": ["this", "", ""],
"regex": "(this (?:node|way|relation)) uses deprecated tag '(.+)'\\. Please use &quot;(.+)&quot;"
},
"70": {
"title": "missing tags",
"severity": "error",
"description": "This $1 has an empty tag: $2",
"IDs": ["this", ""],
"regex": "(this (?:node|way|relation)) has an empty tag: &quot;(.+)=&quot;"
},
"71": {
"title": "",
"severity": "error",
"description": "This way has no tags",
"IDs": ["this"],
"regex": "(this way)"
},
"72": {
"title": "",
"severity": "error",
"description": "This node is not member of any way and does not have any tags",
"IDs": ["this"],
"regex": "(this node)"
},
"73": {
"title": "",
"severity": "error",
"description": "This way has a $1 tag but no highway tag",
"IDs": ["this", ""],
"regex": "(this way) has a (.+) tag"
},
"74": {
"title": "missing tags",
"severity": "error",
"description": "This $1 has an empty tag: $2",
"IDs": ["this", ""],
"regex": "(this (?:node|way|relation)) has an empty tag: &quot;(.+)=&quot;"
},
"75": {
"description": "This (node|way|relation) has a name \\((.+)\\) but no other tag",
"IDs": ["this", ""],
"regex": "(this (?:node|way|relation)) has a name \\((.+)\\)"
},
"90": {
"title": "motorways without ref",
"severity": "error",
"description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag",
"IDs": ["this"],
"regex": "(this way)"
},
"100": {
"title": "places of worship without religion",
"severity": "error",
"description": "This $1 is tagged as place of worship and therefore needs a religion tag",
"IDs": ["this"],
"regex": "(this (?:node|way|relation))"
},
"110": {
"title": "point of interest without name",
"severity": "error",
"description": "This node is tagged as $1 and therefore needs a name tag",
"IDs": ["this", ""],
"regex": "(this (?:node|way|relation)) is tagged as (.+) and"
},
"120": {
"title": "ways without nodes",
"severity": "error",
"description": "This way has just one single node",
"IDs": ["this"],
"regex": "(this way)"
},
"130": {
"title": "floating islands",
"severity": "error",
"description": "This way is not connected to the rest of the map",
"IDs": ["this"],
"regex": "(this way)"
},
"150": {
"title": "railway crossing without tag",
"severity": "error",
"description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing",
"IDs": ["this"],
"regex": "(this crossing)"
},
"160": {
"title": "wrongly used railway tag",
"severity": "error",
"description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing",
"IDs": ["this"],
"regex": "(this railway crossing)"
},
"170": {
"title": "FIXME tagged items",
"severity": "error",
"description": "This feature has a FIXME tag: (.*)",
"IDs": ["this"],
"regex": "(this feature) has a FIXME tag: (.*)"
},
"180": {
"title": "relations without type",
"severity": "error",
"description": "This relation has no type tag which is mandatory for relations",
"IDs": ["this"],
"regex": "(this relation)"
},
"190": {
"title": "intersections without junctions",
"severity": "error",
"description": "This $1 intersects the $2 #$3 but there is no junction node",
"IDs": ["this", "", "w"],
"regex": "(this .+) intersects the (.+) #(\\d+)"
},
"200": {
"title": "overlapping ways",
"severity": "error",
"description": "This $1 overlaps the $2 #$3",
"IDs": ["this", "", "w"],
"regex": "(this .+) overlaps the (.+) #(\\d+)"
},
"210": {
"title": "loopings",
"severity": "error",
"description": "These errors contain self intersecting ways"
},
"211": {
"title": "",
"severity": "error",
"description": "This way contains more than one node at least twice. Nodes are $1.",
"IDs": ["this", "211"],
"regex": "(this way) contains more than one node at least twice. Nodes are ((?:#\\d+(?:, )?)+)\\."
},
"212": {
"title": "",
"severity": "error",
"description": "This way has only two different nodes and contains one of them more than once",
"IDs": ["this"],
"regex": "(this way)"
},
"220": {
"title": "misspelled tags",
"severity": "error",
"description": "This $1 is tagged '$2' where $3 looks like $4",
"regex": "(this (?:node|way|relation)) is tagged '(.+)' where &quot;(.+)&quot; looks like &quot;(.+)&quot;"
},
"221": {
"title": "",
"severity": "error",
"description": "The key of this $1's tag is 'key': $2",
"regex": "(this (?:node|way|relation))\\'s tag is \\'key\\': (.+)"
},
"230": {
"title": "layer conflicts",
"severity": "error",
"description": "This node is a junction of ways on different layers.",
"IDs": ["this"],
"regex": "(this node)"
},
"231": {
"title": "mixed layers intersection",
"severity": "error",
"description": "This node is a junction of ways on different layers: $1",
"IDs": ["this", "231"],
"regex": "(this node) is a junction of ways on different layers: (.+)"
},
"232": {
"title": "strange layers",
"severity": "error",
"description": "This $1 is tagged with layer $2. This need not be an error, but it looks strange",
"IDs": ["this", ""],
"regex": "(this (?:bridge|tunnel)) is tagged with layer (-?\\d+)\\."
},
"270": {
"title": "motorways connected directly",
"severity": "error",
"description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.",
"IDs": ["this"],
"regex": "(this node)"
},
"280": {
"title": "boundaries",
"severity": "error",
"description": "Administrative Boundaries can be expressed either by tagging ways or by adding them to a relation. They should be closed-loop sequences of ways, they must not self-intersect or split and they must have a name and an admin_level."
},
"281": {
"title": "missing name",
"severity": "error",
"description": "This boundary has no name",
"IDs": ["this"],
"regex": "(this boundary)"
},
"282": {
"title": "missing admin level",
"severity": "error",
"description": "The boundary of $1 has no (?:valid numeric)?admin_level",
"regex": "of (.+) has"
},
"283": {
"title": "no closed loop",
"severity": "error",
"description": "The boundary of $1 is not closed-loop",
"regex": "boundary of (.+) is"
},
"284": {
"title": "splitting boundary",
"severity": "error",
"description": "The boundary of $1 splits here",
"regex": "boundary of (.+) splits"
},
"285": {
"title": "admin_level too high",
"severity": "error",
"description": "This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations",
"IDs": ["this", ""],
"regex": "(this boundary)-way has admin_level (-?\\d+) but"
},
"290": {
"title": "restrictions",
"severity": "error",
"description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat"
},
"291": {
"title": "missing type",
"severity": "error",
"description": "This turn-restriction has no (?:known )?restriction type",
"IDs": ["this"],
"regex": "(this turn-restriction) has"
},
"292": {
"title": "missing from way",
"severity": "error",
"description": "This turn-restriction needs exactly one from member. This one has $1",
"IDs": ["this", ""],
"regex": "(this turn-restriction) needs.+has (\\d+)"
},
"293": {
"title": "missing to way",
"severity": "error",
"description": "This turn-restriction needs exactly one to member. This one has $1",
"IDs": ["this", ""],
"regex": "(this turn-restriction) needs.+has (\\d+)"
},
"294": {
"title": "from or to not a way",
"severity": "error",
"description": "From- and To-members of this turn-restriction need to be ways. $1",
"IDs": ["this", "294"],
"regex": "(this turn-restriction)~.+ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)"
},
"295": {
"title": "via is not on the way ends",
"severity": "error",
"description": "via (node #$1) is not the first or the last member of (from|to) (way #$3)",
"IDs": ["this", "n", "", "w"],
"regex": "(this turn-restriction)~via \\(node #(\\d+)\\).+ of (from|to) \\(way #(\\d+)\\)"
},
"296": {
"title": "wrong restriction angle",
"severity": "error",
"description": "This turn-restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?",
"IDs": ["this", "", ""],
"regex": "(this turn-restriction)~.+is (\\w+), but angle is (-?\\d+)"
},
"297": {
"title": "wrong direction of to member",
"severity": "error",
"description": "wrong direction of to way $1",
"IDs": ["this", "w"],
"regex": "(this turn-restriction)~.+to way (\\d+)"
},
"298": {
"title": "already restricted by oneway",
"severity": "error",
"description": "entry already prohibited by oneway tag on $1",
"IDs": ["this", "w"],
"regex": "(this turn-restriction)~.+tag on (\\d+)"
},
"300": {
"title": "missing maxspeed",
"severity": "warning",
"description": "This highway is missing a maxspeed tag",
"IDs": ["this"],
"regex": "(this highway)"
},
"310": {
"title": "roundabouts",
"severity": "error",
"description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1"
},
"311": {
"title": "not closed loop",
"severity": "error",
"description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)",
"IDs": ["this"],
"regex": "(this way)"
},
"312": {
"title": "wrong direction",
"severity": "error",
"description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around",
"IDs": ["this", ""],
"regex": "(this (?:mini_)?roundabout) is in a country with ((?:left|right)-hand)"
},
"313": {
"title": "faintly connected",
"severity": "error",
"description": "This roundabout has only $1 other roads connected. Roundabouts typically have three",
"IDs": ["this", ""],
"regex": "(this roundabout) has only (\\d) other"
},
"320": {
"title": "*_link connections",
"severity": "error",
"description": "This way is tagged as highway=$1_link but doesn't have a connection to any other $1 or $1_link",
"IDs": ["this", "", "", "", ""],
"regex": "(this way) is tagged as (highway=.+) but doesn't have a connection to any other (.+) or (.+)"
},
"350": {
"title": "bridge-tags",
"severity": "error",
"description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)",
"NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...",
"IDs": ["this", ""],
"regex": "(this bridge).*tags: (.+)"
},
"360": {
"title": "language unknown",
"severity": "warning",
"description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'",
"IDs": ["this", ""],
"regex": "(this (?:node|way|relation)) had an additional tag 'name:XX=(.+)' where"
},
"370": {
"title": "doubled places",
"severity": "error",
"description": "This node has tags in common with the surrounding way #$1 ((?:\\(including the name '.+'\\) )?)and seems to be redundand",
"IDs": ["this", "w", "370"],
"regex": "(this node) has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and"
},
"380": {
"title": "non-physical use of sport-tag",
"severity": "error",
"description": "This way is tagged sport=$1 but has no physical tag like e.g. leisure, building, amenity or highway",
"IDs": ["this", ""],
"regex": "(this way) is tagged (sport=.+) but"
},
"390": {
"title": "missing tracktype",
"severity": "warning",
"description": "This track doesn''t have a tracktype",
"IDs": ["this"],
"regex": "(this track)"
},
"400": {
"title": "geometry glitches",
"severity": "error",
"description": ""
},
"401": {
"title": "missing turn restriction",
"severity": "error",
"description": "ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?",
"IDs": ["w", "w"],
"regex": "ways (\\d+) and (\\d+) join"
},
"402": {
"title": "impossible angles",
"severity": "error",
"description": "this way bends in a very sharp angle here",
"IDs": ["this"],
"regex": "(this way)"
},
"410": {
"title": "website",
"severity": "error",
"description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*"
},
"411": {
"title": "http error",
"severity": "error",
"description": "The URL (<a target=_blank href=$1>$1</a>) cannot be opened (HTTP status code $2)",
"IDs": ["this", "url", ""],
"regex": "(this feature)~.+ href=([^>]+)>.+ code (\\d+)\\)"
},
"412": {
"title": "domain hijacking",
"severity": "error",
"description": "Possible domain squatting: <a target=_blank href=$1>$1</a>. Suspicious text is: \"$2\"",
"IDs": ["this", "url", ""],
"regex": "(this feature)~.+ href=([^>]+)>.+ is: &quot;(.+)&quot;"
},
"413": {
"title": "non-match",
"severity": "error",
"description": "Content of the URL (<a target=_blank href=$1>$1</a>) did not contain these keywords: ($2)",
"IDs": ["this", "url", ""],
"regex": "(this feature)~.+ href=([^>]+)>.+ keywords: \\((.+)\\)"
}
}
}
+51 -15
View File
@@ -2553,9 +2553,9 @@ en:
terms: '<translate with synonyms or related terms for ''Payment Terminal'', separated by commas>'
amenity/pharmacy:
# amenity=pharmacy
name: Pharmacy
# 'terms: apothecary,drug*,med*,prescription'
terms: '<translate with synonyms or related terms for ''Pharmacy'', separated by commas>'
name: Pharmacy Counter
# 'terms: apothecary,drug store,drugstore,med*,prescription'
terms: '<translate with synonyms or related terms for ''Pharmacy Counter'', separated by commas>'
amenity/place_of_worship:
# amenity=place_of_worship
name: Place of Worship
@@ -2568,9 +2568,9 @@ en:
terms: '<translate with synonyms or related terms for ''Buddhist Temple'', separated by commas>'
amenity/place_of_worship/christian:
# 'amenity=place_of_worship, religion=christian'
name: Church
name: Christian Church
# 'terms: christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple'
terms: '<translate with synonyms or related terms for ''Church'', separated by commas>'
terms: '<translate with synonyms or related terms for ''Christian Church'', separated by commas>'
amenity/place_of_worship/hindu:
# 'amenity=place_of_worship, religion=hindu'
name: Hindu Temple
@@ -2578,14 +2578,14 @@ en:
terms: '<translate with synonyms or related terms for ''Hindu Temple'', separated by commas>'
amenity/place_of_worship/jewish:
# 'amenity=place_of_worship, religion=jewish'
name: Synagogue
name: Jewish Synagogue
# 'terms: jewish'
terms: '<translate with synonyms or related terms for ''Synagogue'', separated by commas>'
terms: '<translate with synonyms or related terms for ''Jewish Synagogue'', separated by commas>'
amenity/place_of_worship/muslim:
# 'amenity=place_of_worship, religion=muslim'
name: Mosque
name: Muslim Mosque
# 'terms: muslim'
terms: '<translate with synonyms or related terms for ''Mosque'', separated by commas>'
terms: '<translate with synonyms or related terms for ''Muslim Mosque'', separated by commas>'
amenity/place_of_worship/shinto:
# 'amenity=place_of_worship, religion=shinto'
name: Shinto Shrine
@@ -4674,11 +4674,6 @@ en:
# leisure=resort
name: Resort
terms: '<translate with synonyms or related terms for ''Resort'', separated by commas>'
leisure/running_track:
# 'leisure=track, sport=running'
name: Racetrack (Running)
# 'terms: race*,running,sprint,track'
terms: '<translate with synonyms or related terms for ''Racetrack (Running)'', separated by commas>'
leisure/sauna:
# leisure=sauna
name: Sauna
@@ -4711,6 +4706,16 @@ en:
name: Racetrack (Non-Motorsport)
# 'terms: cycle,dog,greyhound,horse,race*,track'
terms: '<translate with synonyms or related terms for ''Racetrack (Non-Motorsport)'', separated by commas>'
leisure/track/horse_racing:
# 'leisure=track, sport=horse_racing'
name: Racetrack (Horse Racing)
# 'terms: race*,horse,track'
terms: '<translate with synonyms or related terms for ''Racetrack (Horse Racing)'', separated by commas>'
leisure/track/running:
# 'leisure=track, sport=running'
name: Racetrack (Running)
# 'terms: race*,running,sprint,track'
terms: '<translate with synonyms or related terms for ''Racetrack (Running)'', separated by commas>'
leisure/water_park:
# leisure=water_park
name: Water Park
@@ -5249,6 +5254,11 @@ en:
name: Downhill Piste/Ski Run
# 'terms: ski,alpine,snowboard,downhill,piste'
terms: '<translate with synonyms or related terms for ''Downhill Piste/Ski Run'', separated by commas>'
piste/downhill/halfpipe:
# 'piste:type=downhill, man_made=piste:halfpipe'
name: Halfpipe
# 'terms: ski,alpine,halfpipe,half pipe,snowboard,downhill,piste'
terms: '<translate with synonyms or related terms for ''Halfpipe'', separated by commas>'
piste/hike:
# 'piste:type=hike'
name: Snowshoeing or Winter Hiking Trail
@@ -5951,6 +5961,11 @@ en:
name: Car Repair Shop
# 'terms: auto,garage,service'
terms: '<translate with synonyms or related terms for ''Car Repair Shop'', separated by commas>'
shop/caravan:
# shop=caravan
name: RV Dealership
# 'terms: auto'
terms: '<translate with synonyms or related terms for ''RV Dealership'', separated by commas>'
shop/carpet:
# shop=carpet
name: Carpet Store
@@ -5972,7 +5987,7 @@ en:
shop/chemist:
# shop=chemist
name: Drugstore
# 'terms: apothecary,med*,drug*,gift'
# 'terms: apothecary,beauty,drug store,drugstore,gift,hair,med*,pharmacy,prescription,tooth'
terms: '<translate with synonyms or related terms for ''Drugstore'', separated by commas>'
shop/chocolate:
# shop=chocolate
@@ -6072,6 +6087,15 @@ en:
# shop=fashion
name: Fashion Store
terms: '<translate with synonyms or related terms for ''Fashion Store'', separated by commas>'
shop/fireplace:
# shop=fireplace
name: Fireplace Store
# 'terms: fireplace,stove,masonry heater'
terms: '<translate with synonyms or related terms for ''Fireplace Store'', separated by commas>'
shop/fishing:
# shop=fishing
name: Fishing Shop
terms: '<translate with synonyms or related terms for ''Fishing Shop'', separated by commas>'
shop/fishmonger:
# shop=fishmonger
name: Fishmonger
@@ -6085,6 +6109,10 @@ en:
name: Framing Shop
# 'terms: art*,paint*,photo*,frame'
terms: '<translate with synonyms or related terms for ''Framing Shop'', separated by commas>'
shop/fuel:
# shop=fuel
name: Fuel Shop
terms: '<translate with synonyms or related terms for ''Fuel Shop'', separated by commas>'
shop/funeral_directors:
# shop=funeral_directors
name: Funeral Home
@@ -6155,6 +6183,10 @@ en:
name: Houseware Store
# 'terms: home,household'
terms: '<translate with synonyms or related terms for ''Houseware Store'', separated by commas>'
shop/hunting:
# shop=hunting
name: Hunting Shop
terms: '<translate with synonyms or related terms for ''Hunting Shop'', separated by commas>'
shop/interior_decoration:
# shop=interior_decoration
name: Interior Decoration Store
@@ -6408,6 +6440,10 @@ en:
# shop=watches
name: Watches Shop
terms: '<translate with synonyms or related terms for ''Watches Shop'', separated by commas>'
shop/water:
# shop=water
name: Drinking Water Shop
terms: '<translate with synonyms or related terms for ''Drinking Water Shop'', separated by commas>'
shop/water_sports:
# shop=water_sports
name: Watersport/Swim Shop
+28 -20
View File
@@ -117,16 +117,16 @@
"amenity/parking/multi-storey": {"icon": "maki-car", "fields": ["name", "operator", "building", "levels", "height", "address", "capacity", "fee", "access_simple"], "geometry": ["area"], "tags": {"amenity": "parking", "parking": "multi-storey"}, "addTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "removeTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "reference": {"key": "parking", "value": "multi-storey"}, "terms": ["car", "indoor parking", "multistorey car park", "parkade", "parking building", "parking deck", "parking garage", "parking ramp", "parking structure"], "name": "Multilevel Parking Garage"},
"amenity/payment_centre": {"icon": "maki-bank", "fields": ["name", "brand", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["check", "tax pay", "bill pay", "currency", "finance", "cash", "money"], "tags": {"amenity": "payment_centre"}, "name": "Payment Center"},
"amenity/payment_terminal": {"icon": "maki-bank", "fields": ["name", "brand", "address", "opening_hours", "payment_multi"], "geometry": ["point"], "terms": ["interactive kiosk", "ekiosk", "atm", "bill pay", "tax pay", "phone pay", "finance", "cash", "money transfer", "card"], "tags": {"amenity": "payment_terminal"}, "name": "Payment Terminal"},
"amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug*", "med*", "prescription"], "name": "Pharmacy"},
"amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug store", "drugstore", "med*", "prescription"], "name": "Pharmacy Counter"},
"amenity/place_of_worship": {"icon": "maki-place-of-worship", "fields": ["name", "religion", "denomination", "address", "building_area", "service_times"], "geometry": ["point", "area"], "terms": ["abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "church", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "mosque", "oratory", "parish", "sacellum", "sanctuary", "shrine", "synagogue", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship"}, "name": "Place of Worship"},
"amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"},
"amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"},
"amenity/place_of_worship/hindu": {"icon": "temaki-hinduism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kovil", "devasthana", "mandir", "kshetram", "alayam", "shrine", "temple"], "tags": {"amenity": "place_of_worship", "religion": "hindu"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Hindu Temple"},
"amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Synagogue"},
"amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Mosque"},
"amenity/place_of_worship/shinto": {"icon": "temaki-shinto", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kami", "torii"], "tags": {"amenity": "place_of_worship", "religion": "shinto"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Shinto Shrine"},
"amenity/place_of_worship/sikh": {"icon": "temaki-sikhism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["gurudwara", "temple"], "tags": {"amenity": "place_of_worship", "religion": "sikh"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Sikh Temple"},
"amenity/place_of_worship/taoist": {"icon": "temaki-taoism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["daoist", "monastery", "temple"], "tags": {"amenity": "place_of_worship", "religion": "taoist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Taoist Temple"},
"amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"},
"amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Christian Church"},
"amenity/place_of_worship/hindu": {"icon": "temaki-hinduism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kovil", "devasthana", "mandir", "kshetram", "alayam", "shrine", "temple"], "tags": {"amenity": "place_of_worship", "religion": "hindu"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Hindu Temple"},
"amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Jewish Synagogue"},
"amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Muslim Mosque"},
"amenity/place_of_worship/shinto": {"icon": "temaki-shinto", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kami", "torii"], "tags": {"amenity": "place_of_worship", "religion": "shinto"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Shinto Shrine"},
"amenity/place_of_worship/sikh": {"icon": "temaki-sikhism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["gurudwara", "temple"], "tags": {"amenity": "place_of_worship", "religion": "sikh"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Sikh Temple"},
"amenity/place_of_worship/taoist": {"icon": "temaki-taoism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["daoist", "monastery", "temple"], "tags": {"amenity": "place_of_worship", "religion": "taoist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Taoist Temple"},
"amenity/planetarium": {"icon": "maki-museum", "fields": ["name", "operator", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "terms": ["museum", "astronomy", "observatory"], "tags": {"amenity": "planetarium"}, "name": "Planetarium"},
"amenity/police": {"icon": "maki-police", "fields": ["name", "operator", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "terms": ["badge", "constable", "constabulary", "cop", "detective", "fed", "law", "enforcement", "officer", "patrol"], "tags": {"amenity": "police"}, "name": "Police"},
"amenity/post_box": {"icon": "maki-post", "fields": ["operator", "collection_times", "drive_through", "ref"], "geometry": ["point", "vertex"], "tags": {"amenity": "post_box"}, "terms": ["letter", "post"], "name": "Mailbox"},
@@ -191,7 +191,7 @@
"amenity/vending_machine/excrement_bags": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "fee", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["excrement bags", "poop", "dog", "animal"], "tags": {"amenity": "vending_machine", "vending": "excrement_bags"}, "reference": {"key": "vending", "value": "excrement_bags"}, "name": "Excrement Bag Vending Machine"},
"amenity/vending_machine/feminine_hygiene": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["condom", "tampon", "pad", "woman", "women", "menstrual hygiene products", "personal care"], "tags": {"amenity": "vending_machine", "vending": "feminine_hygiene"}, "reference": {"key": "vending", "value": "feminine_hygiene"}, "name": "Feminine Hygiene Vending Machine"},
"amenity/vending_machine/food": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["food"], "tags": {"amenity": "vending_machine", "vending": "food"}, "reference": {"key": "vending", "value": "food"}, "name": "Food Vending Machine"},
"amenity/vending_machine/fuel": {"icon": "maki-fuel", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["petrol", "fuel", "gasoline", "propane", "diesel", "lng", "cng", "biodiesel"], "tags": {"amenity": "vending_machine", "vending": "fuel"}, "reference": {"key": "vending", "value": "fuel"}, "name": "Gas Pump"},
"amenity/vending_machine/fuel": {"icon": "maki-fuel", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["petrol", "fuel", "gasoline", "propane", "diesel", "lng", "cng", "biodiesel"], "tags": {"amenity": "vending_machine", "vending": "fuel"}, "reference": {"key": "vending", "value": "fuel"}, "name": "Gas Pump", "matchScore": 0.5},
"amenity/vending_machine/ice_cream": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["chocolate", "ice cream", "frozen", "popsicle", "vanilla"], "tags": {"amenity": "vending_machine", "vending": "ice_cream"}, "reference": {"key": "vending", "value": "ice_cream"}, "name": "Ice Cream Vending Machine"},
"amenity/vending_machine/newspapers": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "fee", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["newspaper"], "tags": {"amenity": "vending_machine", "vending": "newspapers"}, "reference": {"key": "vending", "value": "newspapers"}, "name": "Newspaper Vending Machine"},
"amenity/vending_machine/parcel_pickup_dropoff": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["mail", "parcel", "pickup"], "tags": {"amenity": "vending_machine", "vending": "parcel_pickup;parcel_mail_in"}, "reference": {"key": "vending", "value": "parcel_pickup;parcel_mail_in"}, "name": "Parcel Pickup/Dropoff Locker"},
@@ -549,7 +549,7 @@
"leisure/golf_course": {"icon": "maki-golf", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "terms": ["links"], "tags": {"leisure": "golf_course"}, "name": "Golf Course"},
"leisure/hackerspace": {"icon": "maki-commercial", "fields": ["name", "address", "building_area", "opening_hours", "website"], "geometry": ["point", "area"], "terms": ["makerspace", "hackspace", "hacklab"], "tags": {"leisure": "hackerspace"}, "name": "Hackerspace"},
"leisure/horse_riding": {"icon": "maki-horse-riding", "fields": ["name", "access_simple", "operator", "address", "building"], "geometry": ["point", "area"], "terms": ["equestrian", "stable"], "tags": {"leisure": "horse_riding"}, "name": "Horseback Riding Facility"},
"leisure/ice_rink": {"icon": "maki-pitch", "fields": ["name", "seasonal", "sport_ice", "operator", "address", "building", "opening_hours"], "geometry": ["point", "area"], "terms": ["hockey", "skating", "curling"], "tags": {"leisure": "ice_rink"}, "name": "Ice Rink"},
"leisure/ice_rink": {"icon": "fas-skating", "fields": ["name", "seasonal", "sport_ice", "operator", "address", "building", "opening_hours"], "geometry": ["point", "area"], "terms": ["hockey", "skating", "curling"], "tags": {"leisure": "ice_rink"}, "name": "Ice Rink"},
"leisure/marina": {"icon": "maki-harbor", "fields": ["name", "operator", "address", "capacity", "fee", "sanitary_dump_station", "power_supply", "internet_access", "internet_access/fee"], "moreFields": ["internet_access/ssid"], "geometry": ["point", "vertex", "area"], "terms": ["boat"], "tags": {"leisure": "marina"}, "name": "Marina"},
"leisure/miniature_golf": {"icon": "maki-golf", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "terms": ["crazy golf", "mini golf", "putt-putt"], "tags": {"leisure": "miniature_golf"}, "name": "Miniature Golf"},
"leisure/nature_reserve": {"icon": "maki-park", "geometry": ["point", "area"], "fields": ["name"], "tags": {"leisure": "nature_reserve"}, "terms": ["protected", "wildlife"], "name": "Nature Reserve"},
@@ -578,7 +578,6 @@
"leisure/pitch/volleyball": {"icon": "maki-volleyball", "fields": ["name", "surface", "lit"], "geometry": ["point", "area"], "tags": {"leisure": "pitch", "sport": "volleyball"}, "reference": {"key": "sport", "value": "volleyball"}, "terms": [], "name": "Volleyball Court"},
"leisure/playground": {"icon": "maki-playground", "fields": ["name", "operator", "surface", "playground/max_age", "playground/min_age", "access_simple"], "geometry": ["point", "area"], "terms": ["jungle gym", "play area"], "tags": {"leisure": "playground"}, "name": "Playground"},
"leisure/resort": {"icon": "maki-lodging", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "tags": {"leisure": "resort"}, "name": "Resort"},
"leisure/running_track": {"icon": "maki-pitch", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "running"}, "terms": ["race*", "running", "sprint", "track"], "name": "Racetrack (Running)"},
"leisure/sauna": {"icon": "fas-thermometer-three-quarters", "fields": ["name", "operator", "address", "opening_hours", "access_simple", "fee"], "geometry": ["point", "area"], "tags": {"leisure": "sauna"}, "name": "Sauna"},
"leisure/slipway": {"icon": "maki-slipway", "fields": ["name", "access_simple", "fee"], "geometry": ["point", "vertex", "line"], "terms": ["boat launch", "boat ramp", "boat landing"], "tags": {"leisure": "slipway"}, "name": "Slipway"},
"leisure/sports_centre": {"icon": "maki-pitch", "fields": ["name", "sport", "building", "address", "opening_hours"], "geometry": ["point", "area"], "tags": {"leisure": "sports_centre"}, "terms": [], "name": "Sports Center / Complex"},
@@ -586,6 +585,8 @@
"leisure/stadium": {"icon": "maki-pitch", "fields": ["name", "sport", "address"], "geometry": ["point", "area"], "tags": {"leisure": "stadium"}, "name": "Stadium"},
"leisure/swimming_pool": {"icon": "maki-swimming", "fields": ["name", "access_simple", "operator", "address", "lit", "location_pool", "length", "swimming_pool"], "geometry": ["point", "area"], "terms": ["dive", "water", "aquatics"], "tags": {"leisure": "swimming_pool"}, "name": "Swimming Pool"},
"leisure/track": {"icon": "iD-highway-road", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track"}, "terms": ["cycle", "dog", "greyhound", "horse", "race*", "track"], "name": "Racetrack (Non-Motorsport)"},
"leisure/track/horse_racing": {"icon": "maki-horse-riding", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "horse_racing"}, "terms": ["race*", "horse", "track"], "name": "Racetrack (Horse Racing)"},
"leisure/track/running": {"icon": "maki-pitch", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "running"}, "terms": ["race*", "running", "sprint", "track"], "name": "Racetrack (Running)"},
"leisure/water_park": {"icon": "maki-swimming", "fields": ["name", "operator", "address"], "geometry": ["point", "area"], "terms": ["swim", "pool", "dive"], "tags": {"leisure": "water_park"}, "name": "Water Park"},
"line": {"fields": ["name"], "geometry": ["line"], "tags": {}, "name": "Line", "matchScore": 0.1},
"man_made": {"icon": "temaki-storage_tank", "fields": ["name", "man_made"], "geometry": ["point", "vertex", "line", "area"], "tags": {"man_made": "*"}, "name": "Man Made"},
@@ -703,14 +704,15 @@
"office/telecommunication": {"icon": "maki-telephone", "fields": ["name", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "tags": {"office": "telecommunication"}, "terms": ["communication", "internet", "phone", "voice"], "name": "Telecom Office"},
"office/therapist": {"icon": "maki-suitcase", "fields": ["name", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "tags": {"office": "therapist"}, "terms": ["therapy"], "name": "Therapist Office"},
"office/water_utility": {"icon": "maki-suitcase", "fields": ["name", "address", "building_area", "opening_hours", "operator"], "geometry": ["point", "area"], "tags": {"office": "water_utility"}, "terms": ["water board", "utility"], "name": "Water Utility Office"},
"piste/downhill": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill"}, "name": "Downhill Piste/Ski Run"},
"piste/hike": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming_hike", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["hike", "winter hiking", "snowshoe", "snowshoeing", "piste", "ski"], "tags": {"piste:type": "hike"}, "name": "Snowshoeing or Winter Hiking Trail"},
"piste/ice_skate": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "sport_ice", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ice", "skating", "ski", "piste"], "tags": {"piste:type": "ice_skate"}, "name": "Ice Skating Piste"},
"piste/nordic": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_nordic", "piste/grooming_nordic", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "skating", "piste"], "tags": {"piste:type": "nordic"}, "name": "Nordic or Crosscountry Piste/Ski Trail"},
"piste/piste": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "downhill", "alpine", "snowboard", "skitour", "ski touring", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse", "winter hiking", "snowshoe", "snowshoeing", "ice", "skating"], "tags": {"piste:type": "*"}, "name": "Winter Sport Trails"},
"piste/skitour": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_skitour", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "skitour", "crosscountry", "ski touring", "piste"], "tags": {"piste:type": "skitour"}, "name": "Ski Touring Trail"},
"piste/downhill": {"icon": "fas-skiing", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill"}, "name": "Downhill Piste/Ski Run"},
"piste/downhill/halfpipe": {"icon": "fas-snowboarding", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "halfpipe", "half pipe", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill", "man_made": "piste:halfpipe"}, "name": "Halfpipe"},
"piste/hike": {"icon": "fas-skating", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming_hike", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["hike", "winter hiking", "snowshoe", "snowshoeing", "piste", "ski"], "tags": {"piste:type": "hike"}, "name": "Snowshoeing or Winter Hiking Trail"},
"piste/ice_skate": {"icon": "fas-skating", "fields": ["name", "piste/type", "sport_ice", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ice", "skating", "ski", "piste"], "tags": {"piste:type": "ice_skate"}, "name": "Ice Skating Piste"},
"piste/nordic": {"icon": "fas-skiing-nordic", "fields": ["name", "piste/type", "piste/difficulty_nordic", "piste/grooming_nordic", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "skating", "piste"], "tags": {"piste:type": "nordic"}, "name": "Nordic or Crosscountry Piste/Ski Trail"},
"piste/piste": {"icon": "fas-skiing", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "downhill", "alpine", "snowboard", "skitour", "ski touring", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse", "winter hiking", "snowshoe", "snowshoeing", "ice", "skating"], "tags": {"piste:type": "*"}, "name": "Winter Sport Trails"},
"piste/skitour": {"icon": "fas-skiing-nordic", "fields": ["name", "piste/type", "piste/difficulty_skitour", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "skitour", "crosscountry", "ski touring", "piste"], "tags": {"piste:type": "skitour"}, "name": "Ski Touring Trail"},
"piste/sled": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "sled", "luge", "sleigh", "sledge", "piste"], "tags": {"piste:type": "sled"}, "name": "Sled Piste"},
"piste/sleigh": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "piste", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse"], "tags": {"piste:type": "sleigh"}, "name": "Sleigh Piste"},
"piste/sleigh": {"icon": "fas-sleigh", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "piste", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse"], "tags": {"piste:type": "sleigh"}, "name": "Sleigh Piste"},
"place/farm": {"icon": "maki-farm", "geometry": ["point", "area"], "fields": ["name"], "tags": {"place": "farm"}, "name": "Farm", "searchable": false},
"place/city_block": {"icon": "maki-triangle-stroked", "fields": ["name"], "geometry": ["point", "area"], "tags": {"place": "city_block"}, "name": "City Block"},
"place/city": {"icon": "maki-city", "fields": ["name", "population"], "geometry": ["point", "area"], "tags": {"place": "city"}, "name": "City"},
@@ -856,11 +858,12 @@
"shop/car_parts": {"icon": "maki-car", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "car_parts"}, "name": "Car Parts Store"},
"shop/car_repair": {"icon": "maki-car-repair", "fields": ["name", "operator", "address", "building_area", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto", "garage", "service"], "tags": {"shop": "car_repair"}, "name": "Car Repair Shop"},
"shop/car": {"icon": "maki-car", "fields": ["name", "brand", "operator", "address", "building_area", "second_hand", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "car"}, "name": "Car Dealership"},
"shop/caravan": {"icon": "maki-car", "fields": ["name", "brand", "operator", "address", "building_area", "second_hand", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "caravan"}, "name": "RV Dealership"},
"shop/carpet": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["rug"], "tags": {"shop": "carpet"}, "name": "Carpet Store"},
"shop/catalogue": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "catalogue"}, "name": "Catalog Shop"},
"shop/charity": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "second_hand", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["thrift", "op shop", "nonprofit"], "tags": {"shop": "charity"}, "name": "Charity Store"},
"shop/cheese": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "cheese"}, "name": "Cheese Store"},
"shop/chemist": {"icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chemist"}, "terms": ["apothecary", "med*", "drug*", "gift"], "name": "Drugstore"},
"shop/chemist": {"icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chemist"}, "terms": ["apothecary", "beauty", "drug store", "drugstore", "gift", "hair", "med*", "pharmacy", "prescription", "tooth"], "name": "Drugstore"},
"shop/chocolate": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chocolate"}, "name": "Chocolate Store"},
"shop/clothes": {"icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes"}, "name": "Clothing Store"},
"shop/coffee": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "coffee"}, "name": "Coffee Store"},
@@ -883,8 +886,11 @@
"shop/fabric": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["sew"], "tags": {"shop": "fabric"}, "name": "Fabric Store"},
"shop/farm": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["farm shop", "farm stand"], "tags": {"shop": "farm"}, "name": "Produce Stand"},
"shop/fashion": {"icon": "maki-shop", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fashion"}, "name": "Fashion Store"},
"shop/fireplace": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["fireplace", "stove", "masonry heater"], "tags": {"shop": "fireplace"}, "name": "Fireplace Store"},
"shop/fishing": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fishing"}, "name": "Fishing Shop"},
"shop/florist": {"icon": "maki-florist", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["flower"], "tags": {"shop": "florist"}, "name": "Florist"},
"shop/frame": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "frame"}, "terms": ["art*", "paint*", "photo*", "frame"], "name": "Framing Shop"},
"shop/fuel": {"icon": "maki-shop", "fields": ["name", "operator", "address", "fuel_multi", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fuel"}, "name": "Fuel Shop", "matchScore": 0.5},
"shop/funeral_directors": {"icon": "maki-cemetery", "fields": ["name", "operator", "address", "building_area", "religion", "denomination"], "geometry": ["point", "area"], "terms": ["undertaker", "memorial home"], "tags": {"shop": "funeral_directors"}, "name": "Funeral Home"},
"shop/furniture": {"icon": "fas-couch", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["chair", "sofa", "table"], "tags": {"shop": "furniture"}, "name": "Furniture Store"},
"shop/garden_centre": {"icon": "maki-garden-centre", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["landscape", "mulch", "shrub", "tree"], "tags": {"shop": "garden_centre"}, "name": "Garden Center"},
@@ -899,6 +905,7 @@
"shop/herbalist": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "herbalist"}, "name": "Herbalist"},
"shop/hifi": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["stereo", "video"], "tags": {"shop": "hifi"}, "name": "Hifi Store"},
"shop/houseware": {"icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["home", "household"], "tags": {"shop": "houseware"}, "name": "Houseware Store"},
"shop/hunting": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "hunting"}, "name": "Hunting Shop"},
"shop/interior_decoration": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration"}, "name": "Interior Decoration Store"},
"shop/jewelry": {"icon": "maki-jewelry-store", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["diamond", "gem", "ring"], "tags": {"shop": "jewelry"}, "name": "Jeweler"},
"shop/kiosk": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi", "levels"], "geometry": ["point", "area"], "tags": {"shop": "kiosk"}, "name": "Kiosk"},
@@ -957,6 +964,7 @@
"shop/video": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["DVD"], "tags": {"shop": "video"}, "name": "Video Store"},
"shop/watches": {"icon": "maki-watch", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "watches"}, "name": "Watches Shop"},
"shop/water_sports": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "water_sports"}, "name": "Watersport/Swim Shop"},
"shop/water": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "water"}, "name": "Drinking Water Shop"},
"shop/weapons": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["ammo", "gun", "knife", "knives"], "tags": {"shop": "weapons"}, "name": "Weapon Shop"},
"shop/wholesale": {"icon": "maki-warehouse", "fields": ["name", "operator", "wholesale", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["warehouse club", "cash and carry"], "tags": {"shop": "wholesale"}, "name": "Wholesale Store"},
"shop/window_blind": {"icon": "temaki-window", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "window_blind"}, "name": "Window Blind Store"},
+3 -2
View File
@@ -31,9 +31,10 @@
},
"terms": [
"apothecary",
"drug*",
"drug store",
"drugstore",
"med*",
"prescription"
],
"name": "Pharmacy"
"name": "Pharmacy Counter"
}
@@ -2,6 +2,7 @@
"icon": "maki-religious-buddhist",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -2,6 +2,7 @@
"icon": "maki-religious-christian",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -42,5 +43,5 @@
"key": "amenity",
"value": "place_of_worship"
},
"name": "Church"
"name": "Christian Church"
}
@@ -2,6 +2,7 @@
"icon": "temaki-hinduism",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -2,6 +2,7 @@
"icon": "maki-religious-jewish",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -22,5 +23,5 @@
"key": "amenity",
"value": "place_of_worship"
},
"name": "Synagogue"
"name": "Jewish Synagogue"
}
@@ -2,6 +2,7 @@
"icon": "maki-religious-muslim",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -22,5 +23,5 @@
"key": "amenity",
"value": "place_of_worship"
},
"name": "Mosque"
"name": "Muslim Mosque"
}
@@ -2,6 +2,7 @@
"icon": "temaki-shinto",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -2,6 +2,7 @@
"icon": "temaki-sikhism",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -2,6 +2,7 @@
"icon": "temaki-taoism",
"fields": [
"name",
"religion",
"denomination",
"building_area",
"address",
@@ -27,5 +27,6 @@
"key": "vending",
"value": "fuel"
},
"name": "Gas Pump"
"name": "Gas Pump",
"matchScore": 0.5
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "maki-pitch",
"icon": "fas-skating",
"fields": [
"name",
"seasonal",
@@ -0,0 +1,25 @@
{
"icon": "maki-horse-riding",
"fields": [
"surface",
"sport_racing_nonmotor",
"lit",
"width",
"lanes"
],
"geometry": [
"point",
"line",
"area"
],
"tags": {
"leisure": "track",
"sport": "horse_racing"
},
"terms": [
"race*",
"horse",
"track"
],
"name": "Racetrack (Horse Racing)"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "maki-skiing",
"icon": "fas-skiing",
"fields": [
"name",
"piste/type",
@@ -0,0 +1,29 @@
{
"icon": "fas-snowboarding",
"fields": [
"name",
"piste/type",
"piste/difficulty_downhill",
"piste/grooming_downhill",
"oneway",
"lit"
],
"geometry": [
"line",
"area"
],
"terms": [
"ski",
"alpine",
"halfpipe",
"half pipe",
"snowboard",
"downhill",
"piste"
],
"tags": {
"piste:type": "downhill",
"man_made": "piste:halfpipe"
},
"name": "Halfpipe"
}
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "fas-snowflake",
"icon": "fas-skating",
"fields": [
"name",
"piste/type",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "fas-snowflake",
"icon": "fas-skating",
"fields": [
"name",
"piste/type",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "maki-skiing",
"icon": "fas-skiing-nordic",
"fields": [
"name",
"piste/type",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "maki-skiing",
"icon": "fas-skiing",
"fields": [
"name",
"piste/type",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "maki-skiing",
"icon": "fas-skiing-nordic",
"fields": [
"name",
"piste/type",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"icon": "fas-snowflake",
"icon": "fas-sleigh",
"fields": [
"name",
"piste/type",
+25
View File
@@ -0,0 +1,25 @@
{
"icon": "maki-car",
"fields": [
"name",
"brand",
"operator",
"address",
"building_area",
"second_hand",
"service/vehicle",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"terms": [
"auto"
],
"tags": {
"shop": "caravan"
},
"name": "RV Dealership"
}
+8 -2
View File
@@ -17,9 +17,15 @@
},
"terms": [
"apothecary",
"beauty",
"drug store",
"drugstore",
"gift",
"hair",
"med*",
"drug*",
"gift"
"pharmacy",
"prescription",
"tooth"
],
"name": "Drugstore"
}
+24
View File
@@ -0,0 +1,24 @@
{
"icon": "maki-shop",
"fields": [
"name",
"operator",
"address",
"building_area",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"terms": [
"fireplace",
"stove",
"masonry heater"
],
"tags": {
"shop": "fireplace"
},
"name": "Fireplace Store"
}
+19
View File
@@ -0,0 +1,19 @@
{
"icon": "maki-shop",
"fields": [
"name",
"operator",
"address",
"building_area",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"tags": {
"shop": "fishing"
},
"name": "Fishing Shop"
}
+21
View File
@@ -0,0 +1,21 @@
{
"icon": "maki-shop",
"fields": [
"name",
"operator",
"address",
"fuel_multi",
"building_area",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"tags": {
"shop": "fuel"
},
"name": "Fuel Shop",
"matchScore": 0.5
}
+19
View File
@@ -0,0 +1,19 @@
{
"icon": "maki-shop",
"fields": [
"name",
"operator",
"address",
"building_area",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"tags": {
"shop": "hunting"
},
"name": "Hunting Shop"
}
+19
View File
@@ -0,0 +1,19 @@
{
"icon": "maki-shop",
"fields": [
"name",
"operator",
"address",
"building_area",
"opening_hours",
"payment_multi"
],
"geometry": [
"point",
"area"
],
"tags": {
"shop": "water"
},
"name": "Drinking Water Shop"
}
+16 -16
View File
@@ -41,7 +41,7 @@
"text": "shortcuts.browsing.help.help"
},
{
"shortcuts": ["shortcuts.toggle.key"],
"shortcuts": ["shortcuts.toggle.key", "?"],
"text": "shortcuts.browsing.help.keyboard"
},
{
@@ -62,12 +62,12 @@
"text": "shortcuts.browsing.display_options.map_data"
},
{
"modifiers": ["⌃","⌘"],
"modifiers": ["⌃", "⌘"],
"shortcuts": ["F", "F11"],
"text": "shortcuts.browsing.display_options.fullscreen"
},
{
"shortcuts": ["sidebar.key"],
"shortcuts": ["sidebar.key", "`", "²"],
"text": "shortcuts.browsing.display_options.sidebar"
},
{
@@ -115,8 +115,8 @@
"text": "shortcuts.browsing.with_selected.edit_menu"
},
{
"shortcuts": [],
"text": ""
"shortcuts": ["inspector.zoom_to.key"],
"text": "shortcuts.browsing.with_selected.zoom_to"
},
{
"section": "vertex_selected",
@@ -127,19 +127,19 @@
"text": "shortcuts.browsing.vertex_selected.previous"
},
{
"shortcuts": ["]","↘"],
"shortcuts": ["]", "↘"],
"text": "shortcuts.browsing.vertex_selected.next"
},
{
"shortcuts": ["{","⇞"],
"shortcuts": ["{", "⇞"],
"text": "shortcuts.browsing.vertex_selected.first"
},
{
"shortcuts": ["}","⇟"],
"shortcuts": ["}", "⇟"],
"text": "shortcuts.browsing.vertex_selected.last"
},
{
"shortcuts": ["\\","shortcuts.key.pause"],
"shortcuts": ["\\", "shortcuts.key.pause"],
"text": "shortcuts.browsing.vertex_selected.change_parent"
}
]
@@ -173,7 +173,7 @@
"text": "shortcuts.editing.drawing.add_note"
},
{
"shortcuts": ["Left-click","shortcuts.key.space"],
"shortcuts": ["Left-click", "shortcuts.key.space"],
"text": "shortcuts.editing.drawing.place_point"
},
{
@@ -181,7 +181,7 @@
"text": "shortcuts.editing.drawing.disable_snap"
},
{
"shortcuts": ["↵","⎋"],
"shortcuts": ["↵", "⎋"],
"text": "shortcuts.editing.drawing.stop_line"
},
{
@@ -204,7 +204,7 @@
"text": "shortcuts.editing.commands.undo"
},
{
"modifiers": ["⌘","⇧"],
"modifiers": ["⌘", "⇧"],
"shortcuts": ["Z"],
"text": "shortcuts.editing.commands.redo"
},
@@ -294,22 +294,22 @@
"text": "shortcuts.tools.info.all"
},
{
"modifiers": ["⌘","⇧"],
"modifiers": ["⌘", "⇧"],
"shortcuts": ["info_panels.background.key"],
"text": "shortcuts.tools.info.background"
},
{
"modifiers": ["⌘","⇧"],
"modifiers": ["⌘", "⇧"],
"shortcuts": ["info_panels.history.key"],
"text": "shortcuts.tools.info.history"
},
{
"modifiers": ["⌘","⇧"],
"modifiers": ["⌘", "⇧"],
"shortcuts": ["info_panels.location.key"],
"text": "shortcuts.tools.info.location"
},
{
"modifiers": ["⌘","⇧"],
"modifiers": ["⌘", "⇧"],
"shortcuts": ["info_panels.measurement.key"],
"text": "shortcuts.tools.info.measurement"
}
+75 -19
View File
@@ -794,7 +794,7 @@
{
"key": "amenity",
"value": "pharmacy",
"description": "🄿 Pharmacy",
"description": "🄿 Pharmacy Counter",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pharmacy-15.svg?sanitize=true"
},
@@ -815,7 +815,7 @@
{
"key": "religion",
"value": "christian",
"description": "🄿 Church",
"description": "🄿 Christian Church",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-christian-15.svg?sanitize=true"
},
@@ -829,14 +829,14 @@
{
"key": "religion",
"value": "jewish",
"description": "🄿 Synagogue",
"description": "🄿 Jewish Synagogue",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-jewish-15.svg?sanitize=true"
},
{
"key": "religion",
"value": "muslim",
"description": "🄿 Mosque",
"description": "🄿 Muslim Mosque",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-muslim-15.svg?sanitize=true"
},
@@ -3678,7 +3678,7 @@
"value": "ice_rink",
"description": "🄿 Ice Rink",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true"
},
{
"key": "leisure",
@@ -3876,13 +3876,6 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/lodging-15.svg?sanitize=true"
},
{
"key": "sport",
"value": "running",
"description": "🄿 Racetrack (Running)",
"object_types": ["node", "way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true"
},
{
"key": "leisure",
"value": "sauna",
@@ -3932,6 +3925,20 @@
"object_types": ["node", "way", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/highway-road.svg?sanitize=true"
},
{
"key": "sport",
"value": "horse_racing",
"description": "🄿 Racetrack (Horse Racing)",
"object_types": ["node", "way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/horse-riding-15.svg?sanitize=true"
},
{
"key": "sport",
"value": "running",
"description": "🄿 Racetrack (Running)",
"object_types": ["node", "way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true"
},
{
"key": "leisure",
"value": "water_park",
@@ -4710,41 +4717,48 @@
"value": "downhill",
"description": "🄿 Downhill Piste/Ski Run, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing.svg?sanitize=true"
},
{
"key": "man_made",
"value": "piste:halfpipe",
"description": "🄿 Halfpipe",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowboarding.svg?sanitize=true"
},
{
"key": "piste:type",
"value": "hike",
"description": "🄿 Snowshoeing or Winter Hiking Trail, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true"
},
{
"key": "piste:type",
"value": "ice_skate",
"description": "🄿 Ice Skating Piste, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true"
},
{
"key": "piste:type",
"value": "nordic",
"description": "🄿 Nordic or Crosscountry Piste/Ski Trail, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing-nordic.svg?sanitize=true"
},
{
"key": "piste:type",
"description": "🄿 Winter Sport Trails",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing.svg?sanitize=true"
},
{
"key": "piste:type",
"value": "skitour",
"description": "🄿 Ski Touring Trail, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing-nordic.svg?sanitize=true"
},
{
"key": "piste:type",
@@ -4758,7 +4772,7 @@
"value": "sleigh",
"description": "🄿 Sleigh Piste, 🄵 Type",
"object_types": ["way", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true"
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-sleigh.svg?sanitize=true"
},
{
"key": "place",
@@ -5547,6 +5561,13 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "caravan",
"description": "🄿 RV Dealership",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "carpet",
@@ -5736,6 +5757,20 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "fireplace",
"description": "🄿 Fireplace Store",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "fishing",
"description": "🄿 Fishing Shop",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "florist",
@@ -5750,6 +5785,13 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "fuel",
"description": "🄿 Fuel Shop",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "funeral_directors",
@@ -5848,6 +5890,13 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-blender.svg?sanitize=true"
},
{
"key": "shop",
"value": "hunting",
"description": "🄿 Hunting Shop",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "interior_decoration",
@@ -6254,6 +6303,13 @@
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "water",
"description": "🄿 Drinking Water Shop",
"object_types": ["node", "area"],
"icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true"
},
{
"key": "shop",
"value": "weapons",
+377 -12
View File
@@ -497,11 +497,19 @@
"locating": "Locating, please wait..."
},
"inspector": {
"zoom_to": {
"key": "Z",
"title": "Zoom to this",
"tooltip_feature": "Center and zoom the map to focus on this feature.",
"tooltip_note": "Center and zoom the map to focus on this note.",
"tooltip_data": "Center and zoom the map to focus on this data.",
"tooltip_issue": "Center and zoom the map to focus on this issue."
},
"no_documentation_combination": "There is no documentation available for this tag combination",
"no_documentation_key": "There is no documentation available for this key",
"documentation_redirect": "This documentation has been redirected to a new page",
"show_more": "Show More",
"view_on_osm": "View on openstreetmap.org",
"view_on_keepRight": "View on keepright.at",
"all_fields": "All fields",
"all_tags": "All tags",
"all_members": "All members",
@@ -512,6 +520,7 @@
"choose": "Select feature type",
"results": "{n} results for {search}",
"reference": "View on OpenStreetMap Wiki",
"edit_reference": "Edit or translate on OSM Wiki",
"back_tooltip": "Change feature",
"remove": "Remove",
"search": "Search",
@@ -582,6 +591,10 @@
"tooltip": "Note data from OpenStreetMap",
"title": "OpenStreetMap notes"
},
"keepRight": {
"tooltip": "Automatically detected map issues from keepright.at",
"title": "KeepRight Issues"
},
"custom": {
"tooltip": "Drag and drop a data file onto the page, or click the button to setup",
"title": "Custom Map Data",
@@ -765,6 +778,317 @@
},
"cannot_zoom": "Cannot zoom out further in current mode.",
"full_screen": "Toggle Full Screen",
"QA": {
"keepRight": {
"title": "KeepRight Error",
"detail_title": "Error",
"detail_description": "Description",
"comment": "Comment",
"comment_placeholder": "Enter a comment to share with other users.",
"close": "Close (Error Fixed)",
"ignore": "Ignore (Not an Error)",
"save_comment": "Save Comment",
"close_comment": "Close and Comment",
"ignore_comment": "Ignore and Comment",
"error_parts": {
"node": "this node",
"way": "this way",
"relation": "this relation",
"oneway": "this oneway",
"highway": "this highway",
"railway": "this railway",
"waterway": "this waterway",
"cycleway": "this cycleway",
"cycleway_footpath": "this cycleway/footpath",
"riverbank": "this riverbank",
"crossing": "this crossing",
"railway_crossing": "this railway crossing",
"bridge": "this bridge",
"tunnel": "this tunnel",
"boundary": "this boundary",
"turn_restriction": "this turn restriction",
"roundabout": "this roundabout",
"mini_roundabout": "this mini-roundabout",
"track": "this track",
"feature": "this feature",
"place_of_worship": "place of worship",
"pub": "pub",
"restaurant": "restaurant",
"school": "school",
"university": "university",
"hospital": "hospital",
"library": "library",
"theatre": "theatre",
"courthouse": "courthouse",
"bank": "bank",
"cinema": "cinema",
"pharmacy": "pharmacy",
"cafe": "cafe",
"fast_food": "fast food",
"fuel": "fuel",
"from": "from",
"to": "to",
"left_hand": "left-hand",
"right_hand": "right-hand"
},
"errorTypes": {
"20": {
"title": "Multiple nodes on the same spot",
"description": "There is more than one node in this spot. Node IDs: {var1}."
},
"30": {
"title": "Non-closed area",
"description": "{var1} is tagged with \"{var2}\" and should be a closed loop."
},
"40": {
"title": "Impossible oneway",
"description": "The first node {var1} of {var2} is not connected to any other way."
},
"41": {
"description": "The last node {var1} of {var2} is not connected to any other way."
},
"42": {
"description": "You cannot reach {var1} because all ways leading from it are oneway."
},
"43": {
"description": "You cannot escape from {var1} because all ways leading to it are oneway."
},
"50": {
"title": "Almost junction",
"description": "{var1} is very close but not connected to way {var2}."
},
"60": {
"title": "Deprecated tag",
"description": "{var1} uses deprecated tag \"{var2}\". Please use \"{var3}\" instead."
},
"70": {
"title": "Missing tag",
"description": "{var1} has an empty tag: \"{var2}\"."
},
"71": {
"description": "{var1} has no tags."
},
"72": {
"description": "{var1} is not member of any way and doesn't have any tags."
},
"73": {
"description": "{var1} has a \"{var2}\" tag but no \"highway\" tag."
},
"74": {
"description": "{var1} has an empty tag: \"{var2}\"."
},
"75": {
"description": "{var1} has a name \"{var2}\" but no other tags."
},
"90": {
"title": "Motorway without ref tag",
"description": "{var1} is tagged as a motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag."
},
"100": {
"title": "Place of worship without religion",
"description": "{var1} is tagged as a place of worship and therefore needs a religion tag."
},
"110": {
"title": "Point of interest without name",
"description": "{var1} is tagged as a \"{var2}\" and therefore needs a name tag."
},
"120": {
"title": "Way without nodes",
"description": "{var1} has just one single node."
},
"130": {
"title": "Disconnected way",
"description": "{var1} is not connected to the rest of the map."
},
"150": {
"title": "Railway crossing without tag",
"description": "{var1} of a highway and a railway needs to be tagged as \"railway=crossing\" or \"railway=level_crossing\"."
},
"160": {
"title": "Railway layer conflict",
"description": "There are ways in different layers (e.g. tunnel or bridge) meeting at {var1}."
},
"170": {
"title": "FIXME tagged item",
"description": "{var1} has a FIXME tag: {var2}"
},
"180": {
"title": "Relation without type",
"description": "{var1} is missing a \"type\" tag."
},
"190": {
"title": "Intersection without junction",
"description": "{var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel."
},
"200": {
"title": "Overlapping ways",
"description": "{var1} overlaps the {var2} {var3}."
},
"210": {
"title": "Self-intersecting way",
"description": "There is an unspecified issue with self intersecting ways."
},
"211": {
"description": "{var1} contains more than one node multiple times. Nodes are {var2}. This may or may not be an error."
},
"212": {
"description": "{var1} has only two different nodes and contains one of them more than once."
},
"220": {
"title": "Misspelled tag",
"description": "{var1} is tagged \"{var2}\" where \"{var3}\" looks like \"{var4}\"."
},
"221": {
"description": "{var1} has a suspicious tag \"{var2}\"."
},
"230": {
"title": "Layer conflict",
"description": "{var1} is a junction of ways on different layers."
},
"231": {
"description": "{var1} is a junction of ways on different layers: {var2}.",
"layer": "(layer: {layer})"
},
"232": {
"description": "{var1} is tagged with \"layer={var2}\". This need not be an error but it looks strange."
},
"270": {
"title": "Unusual motorway connection",
"description": "{var1} is a junction of a motorway and a highway other than \"motorway\", \"motorway_link\", \"trunk\", \"rest_area\", or \"construction\". Connection to \"service\" or \"unclassified\" is only valid if it has \"access=no/private\", or it leads to a motorway service area, or if it is a \"service=parking_aisle\"."
},
"280": {
"title": "Boundary issue",
"description": "There is an unspecified issue with this boundary."
},
"281": {
"title": "Boundary missing name",
"description": "{var1} has no name."
},
"282": {
"title": "Boundary missing admin level",
"description": "The boundary of {var1} has no valid numeric admin_level. Please do not mix admin levels (e.g. \"6;7\"). Always tag the lowest admin_level of all boundaries."
},
"283": {
"title": "Boundary not a closed loop",
"description": "The boundary of {var1} is not a closed loop."
},
"284": {
"title": "Boundary is split",
"description": "The boundary of {var1} splits here."
},
"285": {
"title": "Boundary admin_level too high",
"description": "{var1} has \"admin_level={var2}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations."
},
"290": {
"title": "Restriction issue",
"description": "There is an unspecified issue with this restriction."
},
"291": {
"title": "Restriction missing type",
"description": "{var1} has an unrecognized restriction type."
},
"292": {
"title": "Restriction missing \"from\" way",
"description": "{var1} has {var2} \"from\" members, but it should have 1."
},
"293": {
"title": "Restriction missing \"to\" way",
"description": "{var1} has {var2} \"to\" members, but it should have 1."
},
"294": {
"title": "Restriction \"from\" or \"to\" is not a way",
"description": "{var1} has \"from\" or \"to\" members which should be ways. {var2}."
},
"295": {
"title": "Restriction \"via\" is not an endpoint",
"description": "{var1} has a \"via\" (node {var2}) which is not the first or the last member of \"{var3}\" (way {var4})."
},
"296": {
"title": "Unusual restriction angle",
"description": "{var1} has a restriction type \"{var2}\" but the angle is {var3} degrees. Maybe the restriction type is not appropriate?"
},
"297": {
"title": "Wrong direction of to member",
"description": "{var1} does not match the direction of \"to\" way {var2}."
},
"298": {
"title": "Redundant restriction - oneway",
"description": "{var1} may be redundant. Entry already prohibited by \"oneway\" tag on {var2}."
},
"300": {
"title": "Missing maxspeed",
"description": "{var1} is missing a \"maxspeed\" tag and is tagged as motorway, trunk, primary, or secondary."
},
"310": {
"title": "Roundabout issue",
"description": "There is an unspecified issue with this roundabout."
},
"311": {
"title": "Roundabout not closed loop",
"description": "{var1} is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout)."
},
"312": {
"title": "Roundabout wrong direction",
"description": "If {var1} is in a country with {var2} traffic then its orientation goes the wrong way around."
},
"313": {
"title": "Roundabout weakly connected",
"description": "{var1} has only {var2} other road(s) connected. Roundabouts typically have 3 or more."
},
"320": {
"title": "Improper link connection",
"description": "{var1} is tagged as \"{var2}\" but doesn't have a connection to any other \"{var3}\" or \"{var4}\"."
},
"350": {
"title": "Improper bridge tag",
"description": "{var1} doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var2}."
},
"360": {
"title": "Missing local name tag",
"description": "It would be nice if {var1} had a local name tag \"name:XX={var2}\" where XX shows the language of its common name \"{var2}\"."
},
"370": {
"title": "Doubled places",
"description": "{var1} has tags in common with the surrounding way {var2} {var3} and seems to be redundant.",
"including_the_name": "(including the name {name})"
},
"380": {
"title": "Non-physical use of sport tag",
"description": "{var1} is tagged \"{var2}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\")."
},
"390": {
"title": "Missing tracktype",
"description": "{var1} doesn't have a \"tracktype\" tag."
},
"400": {
"title": "Geometry issue",
"description": "There is an unspecified issue with the geometry here."
},
"401": {
"title": "Missing turn restriction",
"description": "Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning."
},
"402": {
"title": "Impossible angle",
"description": "{var1} bends in a very sharp angle here."
},
"410": {
"title": "Website issue",
"description": "There is an unspecified issue with a contact website or URL."
},
"411": {
"description": "{var1} may have an outdated URL: {var2} returned HTTP status code {var3}."
},
"412": {
"description": "{var1} may have an outdated URL: {var2} contained suspicious text \"{var3}\"."
},
"413": {
"description": "{var1} may have an outdated URL: {var2} did not contain keywords \"{var3}\"."
}
}
}
},
"streetside": {
"tooltip": "Streetside photos from Microsoft",
"title": "Photo Overlay (Bing Streetside)",
@@ -992,6 +1316,14 @@
"tracing": "The GPS track isn't sent to OpenStreetMap - the best way to use it is to draw on the map, using it as a guide for the new features that you add.",
"upload": "You can also [upload your GPS data to OpenStreetMap](https://www.openstreetmap.org/trace/create) for other users to use."
},
"qa": {
"title": "Quality Assurance",
"intro": "*Quality Assurance* (Q/A) 3rd party tools help lead to better quality of OSM data. They list automatically deteted bugs, conflics, and issues with the data, which mappers can then go and fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer.",
"tools_h": "Tools",
"tools": "The following tools are currently supported: [KeepRight](https://www.keepright.at/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/), [ImproveOSM](https://improveosm.org/en/), and more Q/A tools in the future.",
"issues_h": "Handling Issues",
"issues": "Handling Q/A issues is similar to handling notes. Clicking an existing Q/A issue populates the sidebar with details on the issue type and related features. Each tool has its own capabilities, but generally you can comment and/or close an issue. Expect iD to support a 'fix me' button to automatically fix simple issues in the future."
},
"field": {
"restrictions": {
"title": "Turn Restrictions Help",
@@ -1448,7 +1780,8 @@
},
"with_selected": {
"title": "With feature selected",
"edit_menu": "Toggle edit menu"
"edit_menu": "Toggle edit menu",
"zoom_to": "Zoom to selected feature"
},
"vertex_selected": {
"title": "With node selected",
@@ -3777,8 +4110,8 @@
"terms": "interactive kiosk,ekiosk,atm,bill pay,tax pay,phone pay,finance,cash,money transfer,card"
},
"amenity/pharmacy": {
"name": "Pharmacy",
"terms": "apothecary,drug*,med*,prescription"
"name": "Pharmacy Counter",
"terms": "apothecary,drug store,drugstore,med*,prescription"
},
"amenity/place_of_worship": {
"name": "Place of Worship",
@@ -3789,7 +4122,7 @@
"terms": "stupa,vihara,monastery,temple,pagoda,zendo,dojo"
},
"amenity/place_of_worship/christian": {
"name": "Church",
"name": "Christian Church",
"terms": "christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple"
},
"amenity/place_of_worship/hindu": {
@@ -3797,11 +4130,11 @@
"terms": "kovil,devasthana,mandir,kshetram,alayam,shrine,temple"
},
"amenity/place_of_worship/jewish": {
"name": "Synagogue",
"name": "Jewish Synagogue",
"terms": "jewish"
},
"amenity/place_of_worship/muslim": {
"name": "Mosque",
"name": "Muslim Mosque",
"terms": "muslim"
},
"amenity/place_of_worship/shinto": {
@@ -5620,10 +5953,6 @@
"name": "Resort",
"terms": ""
},
"leisure/running_track": {
"name": "Racetrack (Running)",
"terms": "race*,running,sprint,track"
},
"leisure/sauna": {
"name": "Sauna",
"terms": ""
@@ -5652,6 +5981,14 @@
"name": "Racetrack (Non-Motorsport)",
"terms": "cycle,dog,greyhound,horse,race*,track"
},
"leisure/track/horse_racing": {
"name": "Racetrack (Horse Racing)",
"terms": "race*,horse,track"
},
"leisure/track/running": {
"name": "Racetrack (Running)",
"terms": "race*,running,sprint,track"
},
"leisure/water_park": {
"name": "Water Park",
"terms": "swim,pool,dive"
@@ -6124,6 +6461,10 @@
"name": "Downhill Piste/Ski Run",
"terms": "ski,alpine,snowboard,downhill,piste"
},
"piste/downhill/halfpipe": {
"name": "Halfpipe",
"terms": "ski,alpine,halfpipe,half pipe,snowboard,downhill,piste"
},
"piste/hike": {
"name": "Snowshoeing or Winter Hiking Trail",
"terms": "hike,winter hiking,snowshoe,snowshoeing,piste,ski"
@@ -6732,6 +7073,10 @@
"name": "Car Dealership",
"terms": "auto"
},
"shop/caravan": {
"name": "RV Dealership",
"terms": "auto"
},
"shop/carpet": {
"name": "Carpet Store",
"terms": "rug"
@@ -6750,7 +7095,7 @@
},
"shop/chemist": {
"name": "Drugstore",
"terms": "apothecary,med*,drug*,gift"
"terms": "apothecary,beauty,drug store,drugstore,gift,hair,med*,pharmacy,prescription,tooth"
},
"shop/chocolate": {
"name": "Chocolate Store",
@@ -6840,6 +7185,14 @@
"name": "Fashion Store",
"terms": ""
},
"shop/fireplace": {
"name": "Fireplace Store",
"terms": "fireplace,stove,masonry heater"
},
"shop/fishing": {
"name": "Fishing Shop",
"terms": ""
},
"shop/florist": {
"name": "Florist",
"terms": "flower"
@@ -6848,6 +7201,10 @@
"name": "Framing Shop",
"terms": "art*,paint*,photo*,frame"
},
"shop/fuel": {
"name": "Fuel Shop",
"terms": ""
},
"shop/funeral_directors": {
"name": "Funeral Home",
"terms": "undertaker,memorial home"
@@ -6904,6 +7261,10 @@
"name": "Houseware Store",
"terms": "home,household"
},
"shop/hunting": {
"name": "Hunting Shop",
"terms": ""
},
"shop/interior_decoration": {
"name": "Interior Decoration Store",
"terms": ""
@@ -7136,6 +7497,10 @@
"name": "Watersport/Swim Shop",
"terms": ""
},
"shop/water": {
"name": "Drinking Water Shop",
"terms": ""
},
"shop/weapons": {
"name": "Weapon Shop",
"terms": "ammo,gun,knife,knives"
+2 -2
View File
@@ -160,8 +160,8 @@ export function behaviorDrag() {
for (; target && target !== root; target = target.parentNode) {
var datum = target.__data__;
var entity = datum instanceof osmNote ?
datum : datum && datum.properties && datum.properties.entity;
var entity = datum instanceof osmNote ? datum
: datum && datum.properties && datum.properties.entity;
if (entity && target[matchesSelector](_selector)) {
return dragstart.call(target, entity);
+5 -1
View File
@@ -5,7 +5,7 @@ import {
select as d3_select
} from 'd3-selection';
import { osmEntity, osmNote } from '../osm';
import { osmEntity, osmNote, krError } from '../osm';
import { utilKeybinding, utilRebind } from '../util';
@@ -112,6 +112,10 @@ export function behaviorHover(context) {
entity = datum;
selector = '.data' + datum.__featurehash__;
} else if (datum instanceof krError) {
entity = datum;
selector = '.kr_error-' + datum.id;
} else if (datum instanceof osmNote) {
entity = datum;
selector = '.note-' + datum.id;
+10 -3
View File
@@ -12,12 +12,14 @@ import {
modeBrowse,
modeSelect,
modeSelectData,
modeSelectNote
modeSelectNote,
modeSelectError
} from '../modes';
import {
osmEntity,
osmNote
osmNote,
krError
} from '../osm';
@@ -130,6 +132,7 @@ export function behaviorSelect(context) {
if (datum instanceof osmEntity) { // clicked an entity..
var selectedIDs = context.selectedIDs();
context.selectedNoteID(null);
context.selectedErrorID(null);
if (!isMultiselect) {
if (selectedIDs.length > 1 && (!suppressMenu && !isShowAlways)) {
@@ -167,9 +170,13 @@ export function behaviorSelect(context) {
context
.selectedNoteID(datum.id)
.enter(modeSelectNote(context, datum.id));
} else if (datum instanceof krError & !isMultiselect) { // clicked a krError error
context
.selectedErrorID(datum.id)
.enter(modeSelectError(context, datum.id));
} else { // clicked nothing..
context.selectedNoteID(null);
context.selectedErrorID(null);
if (!isMultiselect && mode.id !== 'browse') {
context.enter(modeBrowse(context));
}
+10 -1
View File
@@ -146,7 +146,9 @@ export function coreContext() {
this.loadEntity(entityID, function(err, result) {
if (err) return;
var entity = _find(result.data, function(e) { return e.id === entityID; });
if (entity) { map.zoomTo(entity); }
if (entity) {
map.zoomTo(entity);
}
});
}
@@ -264,6 +266,13 @@ export function coreContext() {
return context;
};
var _selectedErrorID;
context.selectedErrorID = function(errorID) {
if (!arguments.length) return _selectedErrorID;
_selectedErrorID = errorID;
return context;
};
/* Behaviors */
context.install = function(behavior) {
+15 -6
View File
@@ -52,9 +52,6 @@ export function coreHistory(context) {
annotation = actions.pop();
}
_stack[_index].transform = context.projection.transform();
_stack[_index].selectedIDs = context.selectedIDs();
var graph = _stack[_index].graph;
for (var i = 0; i < actions.length; i++) {
graph = actions[i](graph, t);
@@ -63,7 +60,9 @@ export function coreHistory(context) {
return {
graph: graph,
annotation: annotation,
imageryUsed: _imageryUsed
imageryUsed: _imageryUsed,
transform: context.projection.transform(),
selectedIDs: context.selectedIDs()
};
}
@@ -410,7 +409,8 @@ export function coreHistory(context) {
var base = _stack[0];
var s = _stack.map(function(i) {
var modified = [], deleted = [];
var modified = [];
var deleted = [];
_forEach(i.graph.entities, function(entity, id) {
if (entity) {
@@ -440,6 +440,8 @@ export function coreHistory(context) {
if (deleted.length) x.deleted = deleted;
if (i.imageryUsed) x.imageryUsed = i.imageryUsed;
if (i.annotation) x.annotation = i.annotation;
if (i.transform) x.transform = i.transform;
if (i.selectedIDs) x.selectedIDs = i.selectedIDs;
return x;
});
@@ -537,7 +539,9 @@ export function coreHistory(context) {
return {
graph: coreGraph(_stack[0].graph).load(entities),
annotation: d.annotation,
imageryUsed: d.imageryUsed
imageryUsed: d.imageryUsed,
transform: d.transform,
selectedIDs: d.selectedIDs
};
});
@@ -555,6 +559,11 @@ export function coreHistory(context) {
});
}
var transform = _stack[_index].transform;
if (transform) {
context.map().transformEase(transform, 0); // 0 = immediate, no easing
}
if (loadComplete) {
dispatch.call('change');
}
+23 -14
View File
@@ -20,13 +20,14 @@ export function modeDragNote(context) {
var _nudgeInterval;
var _lastLoc;
var _note; // most current note.. dragged note may have stale datum.
function startNudge(note, nudge) {
function startNudge(nudge) {
if (_nudgeInterval) window.clearInterval(_nudgeInterval);
_nudgeInterval = window.setInterval(function() {
context.pan(nudge);
doMove(note, nudge);
doMove(nudge);
}, 50);
}
@@ -45,58 +46,66 @@ export function modeDragNote(context) {
function start(note) {
context.surface().selectAll('.note-' + note.id)
_note = note;
var osm = services.osm;
if (osm) {
// Get latest note from cache.. The marker may have a stale datum bound to it
// and dragging it around can sometimes delete the users note comment.
_note = osm.getNote(_note.id);
}
context.surface().selectAll('.note-' + _note.id)
.classed('active', true);
context.perform(actionNoop());
context.enter(mode);
context.selectedNoteID(note.id);
context.selectedNoteID(_note.id);
}
function move(note) {
function move() {
d3_event.sourceEvent.stopPropagation();
_lastLoc = context.projection.invert(d3_event.point);
doMove(note);
doMove();
var nudge = geoViewportEdge(d3_event.point, context.map().dimensions());
if (nudge) {
startNudge(note, nudge);
startNudge(nudge);
} else {
stopNudge();
}
}
function doMove(note, nudge) {
function doMove(nudge) {
nudge = nudge || [0, 0];
var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc);
var currMouse = geoVecSubtract(currPoint, nudge);
var loc = context.projection.invert(currMouse);
note = note.move(loc);
_note = _note.move(loc);
var osm = services.osm;
if (osm) {
osm.replaceNote(note); // update note cache
osm.replaceNote(_note); // update note cache
}
context.replace(actionNoop()); // trigger redraw
}
function end(note) {
function end() {
context.replace(actionNoop()); // trigger redraw
context
.selectedNoteID(note.id)
.enter(modeSelectNote(context, note.id));
.selectedNoteID(_note.id)
.enter(modeSelectNote(context, _note.id));
}
var drag = behaviorDrag()
.selector('.layer-notes .new')
.selector('.layer-touch.markers .target.note.new')
.surface(d3_select('#map').node())
.origin(origin)
.on('start', start)
+1
View File
@@ -12,4 +12,5 @@ export { modeRotate } from './rotate';
export { modeSave } from './save';
export { modeSelect } from './select';
export { modeSelectData } from './select_data';
export { modeSelectError} from './select_error';
export { modeSelectNote } from './select_note';
+93 -85
View File
@@ -192,6 +192,14 @@ export function modeSelect(context, selectedIDs) {
};
mode.zoomToSelected = function() {
var entity = singular();
if (entity) {
context.map().zoomTo(entity);
}
};
mode.reselect = function() {
if (!checkSelectedIDs()) return;
@@ -229,6 +237,91 @@ export function modeSelect(context, selectedIDs) {
mode.enter = function() {
if (!checkSelectedIDs()) return;
var operations = _without(_values(Operations), Operations.operationDelete)
.map(function(o) { return o(selectedIDs, context); })
.filter(function(o) { return o.available(); });
// deprecation warning - Radial Menu to be removed in iD v3
var isRadialMenu = context.storage('edit-menu-style') === 'radial';
if (isRadialMenu) {
operations = operations.slice(0,7);
operations.unshift(Operations.operationDelete(selectedIDs, context));
} else {
operations.push(Operations.operationDelete(selectedIDs, context));
}
operations.forEach(function(operation) {
if (operation.behavior) {
behaviors.push(operation.behavior);
}
});
behaviors.forEach(context.install);
keybinding
.on(t('inspector.zoom_to.key'), mode.zoomToSelected)
.on(['[', 'pgup'], previousVertex)
.on([']', 'pgdown'], nextVertex)
.on(['{', uiCmd('⌘['), 'home'], firstVertex)
.on(['}', uiCmd('⌘]'), 'end'], lastVertex)
.on(['\\', 'pause'], nextParent)
.on('⎋', esc, true)
.on('space', toggleMenu);
d3_select(document)
.call(keybinding);
// deprecation warning - Radial Menu to be removed in iD v3
editMenu = isRadialMenu
? uiRadialMenu(context, operations)
: uiEditMenu(context, operations);
context.ui().sidebar
.select(singular() ? singular().id : null, newFeature);
context.history()
.on('undone.select', update)
.on('redone.select', update);
context.map()
.on('move.select', closeMenu)
.on('drawn.select', selectElements);
context.surface()
.on('dblclick.select', dblclick);
selectElements();
if (selectedIDs.length > 1) {
var entities = uiSelectionList(context, selectedIDs);
context.ui().sidebar.show(entities);
}
if (follow) {
var extent = geoExtent();
var graph = context.graph();
selectedIDs.forEach(function(id) {
var entity = context.entity(id);
extent._extend(entity.extent(graph));
});
var loc = extent.center();
context.map().centerEase(loc);
} else if (singular() && singular().type === 'way') {
context.map().pan([0,0]); // full redraw, to adjust z-sorting #2914
}
timeout = window.setTimeout(function() {
positionMenu();
if (!suppressMenu) {
showMenu();
}
}, 270); /* after any centerEase completes */
function update() {
closeMenu();
@@ -419,91 +512,6 @@ export function modeSelect(context, selectedIDs) {
.classed('related', true);
}
}
if (!checkSelectedIDs()) return;
var operations = _without(_values(Operations), Operations.operationDelete)
.map(function(o) { return o(selectedIDs, context); })
.filter(function(o) { return o.available(); });
// deprecation warning - Radial Menu to be removed in iD v3
var isRadialMenu = context.storage('edit-menu-style') === 'radial';
if (isRadialMenu) {
operations = operations.slice(0,7);
operations.unshift(Operations.operationDelete(selectedIDs, context));
} else {
operations.push(Operations.operationDelete(selectedIDs, context));
}
operations.forEach(function(operation) {
if (operation.behavior) {
behaviors.push(operation.behavior);
}
});
behaviors.forEach(context.install);
keybinding
.on(['[', 'pgup'], previousVertex)
.on([']', 'pgdown'], nextVertex)
.on(['{', uiCmd('⌘['), 'home'], firstVertex)
.on(['}', uiCmd('⌘]'), 'end'], lastVertex)
.on(['\\', 'pause'], nextParent)
.on('⎋', esc, true)
.on('space', toggleMenu);
d3_select(document)
.call(keybinding);
// deprecation warning - Radial Menu to be removed in iD v3
editMenu = isRadialMenu
? uiRadialMenu(context, operations)
: uiEditMenu(context, operations);
context.ui().sidebar
.select(singular() ? singular().id : null, newFeature);
context.history()
.on('undone.select', update)
.on('redone.select', update);
context.map()
.on('move.select', closeMenu)
.on('drawn.select', selectElements);
context.surface()
.on('dblclick.select', dblclick);
selectElements();
if (selectedIDs.length > 1) {
var entities = uiSelectionList(context, selectedIDs);
context.ui().sidebar.show(entities);
}
if (follow) {
var extent = geoExtent();
var graph = context.graph();
selectedIDs.forEach(function(id) {
var entity = context.entity(id);
extent._extend(entity.extent(graph));
});
var loc = extent.center();
context.map().centerEase(loc);
} else if (singular() && singular().type === 'way') {
context.map().pan([0,0]); // full redraw, to adjust z-sorting #2914
}
timeout = window.setTimeout(function() {
positionMenu();
if (!suppressMenu) {
showMenu();
}
}, 270); /* after any centerEase completes */
};
+12 -2
View File
@@ -1,4 +1,3 @@
import { geoBounds as d3_geoBounds } from 'd3-geo';
import {
@@ -13,6 +12,8 @@ import {
behaviorSelect
} from '../behavior';
import { t } from '../util/locale';
import { geoExtent } from '../geo';
import { modeBrowse, modeDragNode, modeDragNote } from '../modes';
import { uiDataEditor } from '../ui';
@@ -61,9 +62,18 @@ export function modeSelectData(context, selectedDatum) {
}
mode.zoomToSelected = function() {
var extent = geoExtent(d3_geoBounds(selectedDatum));
context.map().centerZoom(extent.center(), context.map().trimmedExtentZoom(extent));
};
mode.enter = function() {
behaviors.forEach(context.install);
keybinding.on('⎋', esc, true);
keybinding
.on(t('inspector.zoom_to.key'), mode.zoomToSelected)
.on('⎋', esc, true);
d3_select(document)
.call(keybinding);
+138
View File
@@ -0,0 +1,138 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import {
behaviorBreathe,
behaviorHover,
behaviorLasso,
behaviorSelect
} from '../behavior';
import { t } from '../util/locale';
import { services } from '../services';
import { modeBrowse, modeDragNode, modeDragNote } from '../modes';
import { uiKeepRightEditor } from '../ui';
import { utilKeybinding } from '../util';
export function modeSelectError(context, selectedErrorID) {
var mode = {
id: 'select-error',
button: 'browse'
};
var keepRight = services.keepRight;
var keybinding = utilKeybinding('select-error');
var keepRightEditor = uiKeepRightEditor(context)
.on('change', function() {
context.map().pan([0,0]); // trigger a redraw
var error = checkSelectedID();
if (!error) return;
context.ui().sidebar
.show(keepRightEditor.error(error));
});
var behaviors = [
behaviorBreathe(context),
behaviorHover(context),
behaviorSelect(context),
behaviorLasso(context),
modeDragNode(context).behavior,
modeDragNote(context).behavior
];
function checkSelectedID() {
if (!keepRight) return;
var error = keepRight.getError(selectedErrorID);
if (!error) {
context.enter(modeBrowse(context));
}
return error;
}
mode.zoomToSelected = function() {
if (!keepRight) return;
var error = keepRight.getError(selectedErrorID);
if (error) {
context.map().centerZoom(error.loc, 20);
}
};
mode.enter = function() {
var error = checkSelectedID();
if (!error) return;
behaviors.forEach(context.install);
keybinding
.on(t('inspector.zoom_to.key'), mode.zoomToSelected)
.on('⎋', esc, true);
d3_select(document)
.call(keybinding);
selectError();
var sidebar = context.ui().sidebar;
sidebar.show(keepRightEditor.error(error));
context.map()
.on('drawn.select-error', selectError);
// class the error as selected, or return to browse mode if the error is gone
function selectError(drawn) {
if (!checkSelectedID()) return;
var selection = context.surface()
.selectAll('.kr_error-' + selectedErrorID);
if (selection.empty()) {
// Return to browse mode if selected DOM elements have
// disappeared because the user moved them out of view..
var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent;
if (drawn && source && (source.type === 'mousemove' || source.type === 'touchmove')) {
context.enter(modeBrowse(context));
}
} else {
selection
.classed('selected', true);
context.selectedErrorID(selectedErrorID);
}
}
function esc() {
if (d3_select('.combobox').size()) return;
context.enter(modeBrowse(context));
}
};
mode.exit = function() {
behaviors.forEach(context.uninstall);
d3_select(document)
.call(keybinding.unbind);
context.surface()
.selectAll('.kr_error.selected')
.classed('selected hover', false);
context.map()
.on('drawn.select-error', null);
context.ui().sidebar
.hide();
context.selectedErrorID(null);
};
return mode;
}
+18 -3
View File
@@ -10,6 +10,8 @@ import {
behaviorSelect
} from '../behavior';
import { t } from '../util/locale';
import { modeBrowse, modeDragNode, modeDragNote } from '../modes';
import { services } from '../services';
import { uiNoteEditor } from '../ui';
@@ -72,6 +74,7 @@ export function modeSelectNote(context, selectedNoteID) {
} else {
selection
.classed('selected', true);
context.selectedNoteID(selectedNoteID);
}
}
@@ -83,9 +86,18 @@ export function modeSelectNote(context, selectedNoteID) {
}
mode.newFeature = function(_) {
mode.zoomToSelected = function() {
if (!osm) return;
var note = osm.getNote(selectedNoteID);
if (note) {
context.map().centerZoom(note.loc, 20);
}
};
mode.newFeature = function(val) {
if (!arguments.length) return newFeature;
newFeature = _;
newFeature = val;
return mode;
};
@@ -95,7 +107,10 @@ export function modeSelectNote(context, selectedNoteID) {
if (!note) return;
behaviors.forEach(context.install);
keybinding.on('⎋', esc, true);
keybinding
.on(t('inspector.zoom_to.key'), mode.zoomToSelected)
.on('⎋', esc, true);
d3_select(document)
.call(keybinding);
+1
View File
@@ -1,5 +1,6 @@
export { osmChangeset } from './changeset';
export { osmEntity } from './entity';
export { krError } from './keepRight';
export { osmNode } from './node';
export { osmNote } from './note';
export { osmRelation } from './relation';
+49
View File
@@ -0,0 +1,49 @@
import _extend from 'lodash-es/extend';
export function krError() {
if (!(this instanceof krError)) {
return (new krError()).initialize(arguments);
} else if (arguments.length) {
this.initialize(arguments);
}
}
krError.id = function() {
return krError.id.next--;
};
krError.id.next = -1;
_extend(krError.prototype, {
type: 'krError',
initialize: function(sources) {
for (var i = 0; i < sources.length; ++i) {
var source = sources[i];
for (var prop in source) {
if (Object.prototype.hasOwnProperty.call(source, prop)) {
if (source[prop] === undefined) {
delete this[prop];
} else {
this[prop] = source[prop];
}
}
}
}
if (!this.id) {
this.id = krError.id() + ''; // as string
}
return this;
},
update: function(attrs) {
return krError(this, attrs); // {v: 1 + (this.v || 0)}
}
});
+4 -3
View File
@@ -351,10 +351,11 @@ export function rendererMap(context) {
function editOff() {
context.features().resetStats();
surface.selectAll('.layer-osm *').remove();
surface.selectAll('.layer-touch *').remove();
surface.selectAll('.layer-touch:not(.markers) *').remove();
var mode = context.mode();
if (mode && mode.id !== 'save' && mode.id !== 'select-note' && mode.id !== 'select-data') {
if (mode && mode.id !== 'save' && mode.id !== 'select-note' &&
mode.id !== 'select-data' && mode.id !== 'select-error') {
context.enter(modeBrowse(context));
}
@@ -857,7 +858,7 @@ export function rendererMap(context) {
if (!isFinite(extent.area())) return;
var z2 = map.trimmedExtentZoom(extent);
zoomLimits = zoomLimits || [context.minEditableZoom(), 19];
zoomLimits = zoomLimits || [context.minEditableZoom(), 20];
map.centerZoom(extent.center(), Math.min(Math.max(z2, zoomLimits[0]), zoomLimits[1]));
};
+6
View File
@@ -1,8 +1,10 @@
import serviceKeepRight from './keepRight';
import serviceMapillary from './mapillary';
import serviceMapRules from './maprules';
import serviceNominatim from './nominatim';
import serviceOpenstreetcam from './openstreetcam';
import serviceOsm from './osm';
import serviceOsmWikibase from './osm_wikibase';
import serviceStreetside from './streetside';
import serviceTaginfo from './taginfo';
import serviceVectorTile from './vector_tile';
@@ -12,9 +14,11 @@ import serviceWikipedia from './wikipedia';
export var services = {
geocoder: serviceNominatim,
keepRight: serviceKeepRight,
mapillary: serviceMapillary,
openstreetcam: serviceOpenstreetcam,
osm: serviceOsm,
osmWikibase: serviceOsmWikibase,
maprules: serviceMapRules,
streetside: serviceStreetside,
taginfo: serviceTaginfo,
@@ -24,11 +28,13 @@ export var services = {
};
export {
serviceKeepRight,
serviceMapillary,
serviceMapRules,
serviceNominatim,
serviceOpenstreetcam,
serviceOsm,
serviceOsmWikibase,
serviceStreetside,
serviceTaginfo,
serviceVectorTile,
+498
View File
@@ -0,0 +1,498 @@
import _extend from 'lodash-es/extend';
import _find from 'lodash-es/find';
import _forEach from 'lodash-es/forEach';
import rbush from 'rbush';
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { json as d3_json } from 'd3-request';
import { request as d3_request } from 'd3-request';
import { geoExtent, geoVecAdd } from '../geo';
import { krError } from '../osm';
import { t } from '../util/locale';
import { utilRebind, utilTiler, utilQsString } from '../util';
import { errorTypes, localizeStrings } from '../../data/keepRight.json';
var tiler = utilTiler();
var dispatch = d3_dispatch('loaded');
var _krCache;
var _krZoom = 14;
var _krUrlRoot = 'https://www.keepright.at/';
var _krRuleset = [
// no 20 - multiple node on same spot - these are mostly boundaries overlapping roads
30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180,
190, 191, 192, 193, 194, 195, 196, 197, 198,
200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220,
230, 231, 232, 270, 280, 281, 282, 283, 284, 285,
290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313,
320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413
];
function abortRequest(i) {
if (i) {
i.abort();
}
}
function abortUnwantedRequests(cache, tiles) {
_forEach(cache.inflight, function(v, k) {
var wanted = _find(tiles, function(tile) {
return k === tile.id;
});
if (!wanted) {
abortRequest(v);
delete cache.inflight[k];
}
});
}
function encodeErrorRtree(d) {
return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d };
}
// replace or remove error from rtree
function updateRtree(item, replace) {
_krCache.rtree.remove(item, function isEql(a, b) {
return a.data.id === b.data.id;
});
if (replace) {
_krCache.rtree.insert(item);
}
}
function tokenReplacements(d) {
if (!(d instanceof krError)) return;
var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/);
var replacements = {};
var errorTemplate = errorTypes[d.which_type];
if (!errorTemplate) {
/* eslint-disable no-console */
console.log('No Template: ', d.which_type);
console.log(' ', d.description);
/* eslint-enable no-console */
return;
}
// some descriptions are just fixed text
if (!errorTemplate.regex) return;
// regex pattern should match description with variable details captured
var errorRegex = new RegExp(errorTemplate.regex, 'i');
var errorMatch = errorRegex.exec(d.description);
if (!errorMatch) {
/* eslint-disable no-console */
console.log('Unmatched: ', d.which_type);
console.log(' ', d.description);
console.log(' ', errorRegex);
/* eslint-enable no-console */
return;
}
for (var i = 1; i < errorMatch.length; i++) { // skip first
var capture = errorMatch[i];
var idType;
idType = 'IDs' in errorTemplate ? errorTemplate.IDs[i-1] : '';
if (idType && capture) { // link IDs if present in the capture
capture = parseError(capture, idType);
} else if (htmlRegex.test(capture)) { // escape any html in non-IDs
capture = '\\' + capture + '\\';
} else {
var compare = capture.toLowerCase();
if (localizeStrings[compare]) { // some replacement strings can be localized
capture = t('QA.keepRight.error_parts.' + localizeStrings[compare]);
}
}
replacements['var' + i] = capture;
}
return replacements;
}
function parseError(capture, idType) {
var compare = capture.toLowerCase();
if (localizeStrings[compare]) { // some replacement strings can be localized
capture = t('QA.keepRight.error_parts.' + localizeStrings[compare]);
}
switch (idType) {
// link a string like "this node"
case 'this':
capture = linkErrorObject(capture);
break;
case 'url':
capture = linkURL(capture);
break;
// link an entity ID
case 'n':
case 'w':
case 'r':
capture = linkEntity(idType + capture);
break;
// some errors have more complex ID lists/variance
case '20':
capture = parse20(capture);
break;
case '211':
capture = parse211(capture);
break;
case '231':
capture = parse231(capture);
break;
case '294':
capture = parse294(capture);
break;
case '370':
capture = parse370(capture);
break;
}
return capture;
function linkErrorObject(d) {
return '<a class="kr_error_object_link">' + d + '</a>';
}
function linkEntity(d) {
return '<a class="kr_error_entity_link">' + d + '</a>';
}
function linkURL(d) {
return '<a class="kr_external_link" target="_blank" href="' + d + '">' + d + '</a>';
}
// arbitrary node list of form: #ID, #ID, #ID...
function parse211(capture) {
var newList = [];
var items = capture.split(', ');
items.forEach(function(item) {
// ID has # at the front
var id = linkEntity('n' + item.slice(1));
newList.push(id);
});
return newList.join(', ');
}
// arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)...
function parse231(capture) {
var newList = [];
// unfortunately 'layer' can itself contain commas, so we split on '),'
var items = capture.split('),');
items.forEach(function(item) {
var match = item.match(/\#(\d+)\((.+)\)?/);
if (match !== null && match.length > 2) {
newList.push(linkEntity('w' + match[1]) + ' ' +
t('QA.keepRight.errorTypes.231.layer', { layer: match[2] })
);
}
});
return newList.join(', ');
}
// arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID...
function parse294(capture) {
var newList = [];
var items = capture.split(',');
items.forEach(function(item) {
var role;
var idType;
var id;
// item of form "from/to node/relation #ID"
item = item.split(' ');
// to/from role is more clear in quotes
role = '"' + item[0] + '"';
// first letter of node/relation provides the type
idType = item[1].slice(0,1);
// ID has # at the front
id = item[2].slice(1);
id = linkEntity(idType + id);
item = [role, item[1], id].join(' ');
newList.push(item);
});
return newList.join(', ');
}
// may or may not include the string "(including the name 'name')"
function parse370(capture) {
if (!capture) return '';
var match = capture.match(/\(including the name (\'.+\')\)/);
if (match !== null && match.length) {
return t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] });
}
return '';
}
// arbitrary node list of form: #ID,#ID,#ID...
function parse20(capture) {
var newList = [];
var items = capture.split(',');
items.forEach(function(item) {
// ID has # at the front
var id = linkEntity('n' + item.slice(1));
newList.push(id);
});
return newList.join(', ');
}
}
export default {
init: function() {
if (!_krCache) {
this.reset();
}
this.event = utilRebind(this, dispatch, 'on');
},
reset: function() {
if (_krCache) {
_forEach(_krCache.inflight, abortRequest);
}
_krCache = {
data: {},
loaded: {},
inflight: {},
closed: {},
rtree: rbush()
};
},
// KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php
loadErrors: function(projection) {
var options = { format: 'geojson' };
var rules = _krRuleset.join();
// determine the needed tiles to cover the view
var tiles = tiler
.zoomExtent([_krZoom, _krZoom])
.getTiles(projection);
// abort inflight requests that are no longer needed
abortUnwantedRequests(_krCache, tiles);
// issue new requests..
tiles.forEach(function(tile) {
if (_krCache.loaded[tile.id] || _krCache.inflight[tile.id]) return;
var rect = tile.extent.rectangle();
var params = _extend({}, options, { left: rect[0], bottom: rect[3], right: rect[2], top: rect[1] });
var url = _krUrlRoot + 'export.php?' + utilQsString(params) + '&ch=' + rules;
_krCache.inflight[tile.id] = d3_json(url,
function(err, data) {
delete _krCache.inflight[tile.id];
if (err) return;
_krCache.loaded[tile.id] = true;
if (!data.features || !data.features.length) return;
data.features.forEach(function(feature) {
var loc = feature.geometry.coordinates;
var props = feature.properties;
// if there is a parent, save its error type e.g.:
// Error 191 = "highway-highway"
// Error 190 = "intersections without junctions" (parent)
var errorType = props.error_type;
var errorTemplate = errorTypes[errorType];
var parentErrorType = (Math.floor(errorType / 10) * 10).toString();
// try to handle error type directly, fallback to parent error type.
var whichType = errorTemplate ? errorType : parentErrorType;
var whichTemplate = errorTypes[whichType];
// Rewrite a few of the errors at this point..
// This is done to make them easier to linkify and translate.
switch (whichType) {
case '170':
props.description = 'This feature has a FIXME tag: ' + props.description;
break;
case '292':
case '293':
props.description = props.description.replace('A turn-', 'This turn-');
break;
case '294':
case '295':
case '296':
case '297':
case '298':
props.description = 'This turn-restriction~' + props.description;
break;
case '300':
props.description = 'This highway is missing a maxspeed tag';
break;
case '411':
case '412':
case '413':
props.description = 'This feature~' + props.description;
break;
}
// - move markers slightly so it doesn't obscure the geometry,
// - then move markers away from other coincident markers
var coincident = false;
do {
// first time, move marker up. after that, move marker right.
var delta = coincident ? [0.00001, 0] : [0, 0.00001];
loc = geoVecAdd(loc, delta);
var bbox = geoExtent(loc).bbox();
coincident = _krCache.rtree.search(bbox).length;
} while (coincident);
var d = new krError({
loc: loc,
id: props.error_id,
comment: props.comment || null,
description: props.description || '',
error_id: props.error_id,
which_type: whichType,
error_type: errorType,
parent_error_type: parentErrorType,
severity: whichTemplate.severity || 'error',
object_id: props.object_id,
object_type: props.object_type,
schema: props.schema,
title: props.title
});
d.replacements = tokenReplacements(d);
_krCache.data[d.id] = d;
_krCache.rtree.insert(encodeErrorRtree(d));
});
dispatch.call('loaded');
}
);
});
},
postKeepRightUpdate: function(d, callback) {
if (_krCache.inflight[d.id]) {
return callback({ message: 'Error update already inflight', status: -2 }, d);
}
var that = this;
var params = { schema: d.schema, id: d.error_id };
if (d.state) {
params.st = d.state;
}
if (d.newComment !== undefined) {
params.co = d.newComment;
}
// NOTE: This throws a CORS err, but it seems successful.
// We don't care too much about the response, so this is fine.
var url = _krUrlRoot + 'comment.php?' + utilQsString(params);
_krCache.inflight[d.id] = d3_request(url)
.post(function(err) {
delete _krCache.inflight[d.id];
if (d.state === 'ignore') { // ignore permanently (false positive)
that.removeError(d);
} else if (d.state === 'ignore_t') { // ignore temporarily (error fixed)
that.removeError(d);
_krCache.closed[d.schema + ':' + d.error_id] = true;
} else {
d = that.replaceError(d.update({
comment: d.newComment,
newComment: undefined,
state: undefined
}));
}
return callback(err, d);
});
},
// get all cached errors covering the viewport
getErrors: function(projection) {
var viewport = projection.clipExtent();
var min = [viewport[0][0], viewport[1][1]];
var max = [viewport[1][0], viewport[0][1]];
var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
return _krCache.rtree.search(bbox).map(function(d) {
return d.data;
});
},
// get a single error from the cache
getError: function(id) {
return _krCache.data[id];
},
// replace a single error in the cache
replaceError: function(error) {
if (!(error instanceof krError) || !error.id) return;
_krCache.data[error.id] = error;
updateRtree(encodeErrorRtree(error), true); // true = replace
return error;
},
// remove a single error from the cache
removeError: function(error) {
if (!(error instanceof krError) || !error.id) return;
delete _krCache.data[error.id];
updateRtree(encodeErrorRtree(error), false); // false = remove
},
errorURL: function(error) {
return _krUrlRoot + 'report_map.php?schema=' + error.schema + '&error=' + error.id;
},
// Get an array of errors closed during this session.
// Used to populate `closed:keepright` changeset tag
getClosedIDs: function() {
return Object.keys(_krCache.closed).sort();
}
};
+21 -2
View File
@@ -47,7 +47,7 @@ var oauth = osmAuth({
var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*'];
var _tileCache = { loaded: {}, inflight: {}, seen: {} };
var _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, rtree: rbush() };
var _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() };
var _userCache = { toLoad: {}, user: {} };
var _changeset = {};
@@ -385,7 +385,7 @@ export default {
if (_changeset.inflight) abortRequest(_changeset.inflight);
_tileCache = { loaded: {}, inflight: {}, seen: {} };
_noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, rtree: rbush() };
_noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() };
_userCache = { toLoad: {}, user: {} };
_changeset = {};
@@ -432,6 +432,11 @@ export default {
},
noteReportURL: function(note) {
return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id;
},
// Generic method to load data from the OSM API
// Can handle either auth or unauth calls.
loadFromAPI: function(path, callback, options) {
@@ -951,6 +956,13 @@ export default {
// we get the updated note back, remove from caches and reparse..
this.removeNote(note);
// update closed note cache - used to populate `closed:note` changeset tag
if (action === 'close') {
_noteCache.closed[note.id] = true;
} else if (action === 'reopen') {
delete _noteCache.closed[note.id];
}
var options = { skipSeen: false };
return parseXML(xml, function(err, results) {
if (err) {
@@ -1113,6 +1125,13 @@ export default {
_noteCache.note[note.id] = note;
updateRtree(encodeNoteRtree(note), true); // true = replace
return note;
},
// Get an array of note IDs closed during this session.
// Used to populate `closed:note` changeset tag
getClosedIDs: function() {
return Object.keys(_noteCache.closed).sort();
}
};
+201
View File
@@ -0,0 +1,201 @@
import _debounce from 'lodash-es/debounce';
import _forEach from 'lodash-es/forEach';
import { json as d3_json } from 'd3-request';
import { utilQsString } from '../util';
var apibase = 'https://wiki.openstreetmap.org/w/api.php';
var _inflight = {};
var _wikibaseCache = {};
var _localeIDs = { en: false };
var debouncedRequest = _debounce(request, 500, { leading: false });
function request(url, callback) {
if (_inflight[url]) return;
_inflight[url] = d3_json(url, function (err, data) {
delete _inflight[url];
callback(err, data);
});
}
/**
* Get the best string value from the descriptions/labels result
* Note that if mediawiki doesn't recognize language code, it will return all values.
* In that case, fallback to use English.
* @param values object - either descriptions or labels
* @param langCode String
* @returns localized string
*/
function localizedToString(values, langCode) {
if (values) {
values = values[langCode] || values.en;
}
return values ? values.value : '';
}
export default {
init: function() {
_inflight = {};
_wikibaseCache = {};
_localeIDs = {};
},
reset: function() {
_forEach(_inflight, function(req) { req.abort(); });
_inflight = {};
},
/**
* Get the best value for the property, or undefined if not found
* @param entity object from wikibase
* @param property string e.g. 'P4' for image
* @param langCode string e.g. 'fr' for French
*/
claimToValue: function(entity, property, langCode) {
if (!entity.claims[property]) return undefined;
var locale = _localeIDs[langCode];
var preferredPick, localePick;
_forEach(entity.claims[property], function(stmt) {
// If exists, use value limited to the needed language (has a qualifier P26 = locale)
// Or if not found, use the first value with the "preferred" rank
if (!preferredPick && stmt.rank === 'preferred') {
preferredPick = stmt;
}
if (locale && stmt.qualifiers && stmt.qualifiers.P26 &&
stmt.qualifiers.P26[0].datavalue.value.id === locale
) {
localePick = stmt;
}
});
var result = localePick || preferredPick;
if (result) {
var datavalue = result.mainsnak.datavalue;
return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value;
} else {
return undefined;
}
},
toSitelink: function(key, value) {
var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key;
return result.replace(/_/g, ' ').trim();
},
getEntity: function(params, callback) {
var doRequest = params.debounce ? debouncedRequest : request;
var self = this;
var titles = [];
var result = {};
var keySitelink = this.toSitelink(params.key);
var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false;
var localeSitelink;
if (params.langCode && _localeIDs[params.langCode] === undefined) {
// If this is the first time we are asking about this locale,
// fetch corresponding entity (if it exists), and cache it.
// If there is no such entry, cache `false` value to avoid re-requesting it.
localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim();
titles.push(localeSitelink);
}
if (_wikibaseCache[keySitelink]) {
result.key = _wikibaseCache[keySitelink];
} else {
titles.push(keySitelink);
}
if (tagSitelink) {
if (_wikibaseCache[tagSitelink]) {
result.tag = _wikibaseCache[tagSitelink];
} else {
titles.push(tagSitelink);
}
}
if (!titles.length) {
// Nothing to do, we already had everything in the cache
return callback(null, result);
}
// Requesting just the user language code
// If backend recognizes the code, it will perform proper fallbacks,
// and the result will contain the requested code. If not, all values are returned:
// {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"}
// {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}}
var obj = {
action: 'wbgetentities',
sites: 'wiki',
titles: titles.join('|'),
languages: params.langCode,
languagefallback: 1,
origin: '*',
format: 'json',
// There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069
// We shouldn't use v1 until it gets fixed, but should switch to it afterwards
// formatversion: 2,
};
var url = apibase + '?' + utilQsString(obj);
doRequest(url, function(err, d) {
if (err) {
callback(err);
} else if (!d.success || d.error) {
callback(d.error.messages.map(function(v) { return v.html['*']; }).join('<br>'));
} else {
var localeID = false;
_forEach(d.entities, function(res) {
if (res.missing !== '') {
var title = res.sitelinks.wiki.title;
// Simplify access to the localized values
res.description = localizedToString(res.descriptions, params.langCode);
res.label = localizedToString(res.labels, params.langCode);
if (title === keySitelink) {
_wikibaseCache[keySitelink] = res;
result.key = res;
} else if (title === tagSitelink) {
_wikibaseCache[tagSitelink] = res;
result.tag = res;
} else if (title === localeSitelink) {
localeID = res.id;
} else {
console.log('Unexpected title ' + title); // eslint-disable-line no-console
}
}
});
if (localeSitelink) {
// If locale ID is not found, store false to prevent repeated queries
self.addLocale(params.langCode, localeID);
}
callback(null, result);
}
});
},
addLocale: function(langCode, qid) {
// Makes it easier to unit test
_localeIDs[langCode] = qid;
},
apibase: function(val) {
if (!arguments.length) return apibase;
apibase = val;
return this;
}
};
+2 -2
View File
@@ -2,9 +2,9 @@ import { select as d3_select } from 'd3-selection';
import { svgPointTransform } from './helpers';
import { geoMetersToLat } from '../geo';
import _throttle from 'lodash-es/throttle';
export function svgGeolocate(projection, context, dispatch) {
export function svgGeolocate(projection) {
var layer = d3_select(null);
var _position;
+1
View File
@@ -2,6 +2,7 @@ export { svgAreas } from './areas.js';
export { svgData } from './data.js';
export { svgDebug } from './debug.js';
export { svgDefs } from './defs.js';
export { svgKeepRight } from './keepRight';
export { svgIcon } from './icon.js';
export { svgGeolocate } from './geolocate';
export { svgLabels } from './labels.js';
+245
View File
@@ -0,0 +1,245 @@
import _throttle from 'lodash-es/throttle';
import { select as d3_select } from 'd3-selection';
import { modeBrowse } from '../modes';
import { svgPointTransform } from './index';
import { services } from '../services';
var _keepRightEnabled = false;
var _keepRightService;
export function svgKeepRight(projection, context, dispatch) {
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
var minZoom = 12;
var touchLayer = d3_select(null);
var drawLayer = d3_select(null);
var _keepRightVisible = false;
function markerPath(selection, klass) {
selection
.attr('class', klass)
.attr('transform', 'translate(-4, -24)')
.attr('d', 'M11.6,6.2H7.1l1.4-5.1C8.6,0.6,8.1,0,7.5,0H2.2C1.7,0,1.3,0.3,1.3,0.8L0,10.2c-0.1,0.6,0.4,1.1,0.9,1.1h4.6l-1.8,7.6C3.6,19.4,4.1,20,4.7,20c0.3,0,0.6-0.2,0.8-0.5l6.9-11.9C12.7,7,12.3,6.2,11.6,6.2z');
}
// Loosely-coupled keepRight service for fetching errors.
function getService() {
if (services.keepRight && !_keepRightService) {
_keepRightService = services.keepRight;
_keepRightService.on('loaded', throttledRedraw);
} else if (!services.keepRight && _keepRightService) {
_keepRightService = null;
}
return _keepRightService;
}
// Show the errors
function editOn() {
if (!_keepRightVisible) {
_keepRightVisible = true;
drawLayer
.style('display', 'block');
}
}
// Immediately remove the errors and their touch targets
function editOff() {
if (_keepRightVisible) {
_keepRightVisible = false;
drawLayer
.style('display', 'none');
drawLayer.selectAll('.kr_error')
.remove();
touchLayer.selectAll('.kr_error')
.remove();
}
}
// Enable the layer. This shows the errors and transitions them to visible.
function layerOn() {
editOn();
drawLayer
.style('opacity', 0)
.transition()
.duration(250)
.style('opacity', 1)
.on('end interrupt', function () {
dispatch.call('change');
});
}
// Disable the layer. This transitions the layer invisible and then hides the errors.
function layerOff() {
throttledRedraw.cancel();
drawLayer.interrupt();
touchLayer.selectAll('.kr_error')
.remove();
drawLayer
.transition()
.duration(250)
.style('opacity', 0)
.on('end interrupt', function () {
editOff();
dispatch.call('change');
});
}
// Update the error markers
function updateMarkers() {
if (!_keepRightVisible || !_keepRightEnabled) return;
var service = getService();
var selectedID = context.selectedErrorID();
var data = (service ? service.getErrors(projection) : []);
var getTransform = svgPointTransform(projection);
// Draw markers..
var markers = drawLayer.selectAll('.kr_error')
.data(data, function(d) { return d.id; });
// exit
markers.exit()
.remove();
// enter
var markersEnter = markers.enter()
.append('g')
.attr('class', function(d) {
return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.parent_error_type; }
);
markersEnter
.append('ellipse')
.attr('cx', 0.5)
.attr('cy', 1)
.attr('rx', 6.5)
.attr('ry', 3)
.attr('class', 'stroke');
markersEnter
.append('path')
.call(markerPath, 'shadow');
markersEnter
.append('use')
.attr('class', 'kr_error-fill')
.attr('width', '20px')
.attr('height', '20px')
.attr('x', '-8px')
.attr('y', '-22px')
.attr('xlink:href', '#iD-icon-bolt');
// update
markers
.merge(markersEnter)
.sort(sortY)
.classed('selected', function(d) { return d.id === selectedID; })
.attr('transform', getTransform);
// Draw targets..
if (touchLayer.empty()) return;
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var targets = touchLayer.selectAll('.kr_error')
.data(data, function(d) { return d.id; });
// exit
targets.exit()
.remove();
// enter/update
targets.enter()
.append('rect')
.attr('width', '20px')
.attr('height', '20px')
.attr('x', '-8px')
.attr('y', '-22px')
.merge(targets)
.sort(sortY)
.attr('class', function(d) {
return 'kr_error target kr_error-' + d.id + ' ' + fillClass;
})
.attr('transform', getTransform);
function sortY(a, b) {
return (a.id === selectedID) ? 1
: (b.id === selectedID) ? -1
: (a.severity === 'error' && b.severity !== 'error') ? 1
: (b.severity === 'error' && a.severity !== 'error') ? -1
: b.loc[1] - a.loc[1];
}
}
// Draw the keepRight layer and schedule loading errors and updating markers.
function drawKeepRight(selection) {
var service = getService();
var surface = context.surface();
if (surface && !surface.empty()) {
touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
}
drawLayer = selection.selectAll('.layer-keepRight')
.data(service ? [0] : []);
drawLayer.exit()
.remove();
drawLayer = drawLayer.enter()
.append('g')
.attr('class', 'layer-keepRight')
.style('display', _keepRightEnabled ? 'block' : 'none')
.merge(drawLayer);
if (_keepRightEnabled) {
if (service && ~~context.map().zoom() >= minZoom) {
editOn();
service.loadErrors(projection);
updateMarkers();
} else {
editOff();
}
}
}
// Toggles the layer on and off
drawKeepRight.enabled = function(val) {
if (!arguments.length) return _keepRightEnabled;
_keepRightEnabled = val;
if (_keepRightEnabled) {
layerOn();
} else {
layerOff();
if (context.selectedErrorID()) {
context.enter(modeBrowse(context));
}
}
dispatch.call('change');
return this;
};
drawKeepRight.supported = function() {
return !!getService();
};
return drawKeepRight;
}
+2
View File
@@ -10,6 +10,7 @@ import { select as d3_select } from 'd3-selection';
import { svgData } from './data';
import { svgDebug } from './debug';
import { svgGeolocate } from './geolocate';
import { svgKeepRight } from './keepRight';
import { svgStreetside } from './streetside';
import { svgMapillaryImages } from './mapillary_images';
import { svgMapillarySigns } from './mapillary_signs';
@@ -28,6 +29,7 @@ export function svgLayers(projection, context) {
{ id: 'osm', layer: svgOsm(projection, context, dispatch) },
{ id: 'notes', layer: svgNotes(projection, context, dispatch) },
{ id: 'data', layer: svgData(projection, context, dispatch) },
{ id: 'keepRight', layer: svgKeepRight(projection, context, dispatch) },
{ id: 'streetside', layer: svgStreetside(projection, context, dispatch)},
{ id: 'mapillary-images', layer: svgMapillaryImages(projection, context, dispatch) },
{ id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch) },
+123 -68
View File
@@ -8,12 +8,18 @@ import { svgPointTransform } from './index';
import { services } from '../services';
var _notesEnabled = false;
var _osmService;
export function svgNotes(projection, context, dispatch) {
if (!dispatch) { dispatch = d3_dispatch('change'); }
var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000);
var minZoom = 12;
var layer = d3_select(null);
var _notes;
var touchLayer = d3_select(null);
var drawLayer = d3_select(null);
var _notesVisible = false;
function markerPath(selection, klass) {
selection
@@ -22,40 +28,49 @@ export function svgNotes(projection, context, dispatch) {
.attr('d', 'm17.5,0l-15,0c-1.37,0 -2.5,1.12 -2.5,2.5l0,11.25c0,1.37 1.12,2.5 2.5,2.5l3.75,0l0,3.28c0,0.38 0.43,0.6 0.75,0.37l4.87,-3.65l5.62,0c1.37,0 2.5,-1.12 2.5,-2.5l0,-11.25c0,-1.37 -1.12,-2.5 -2.5,-2.5z');
}
function init() {
if (svgNotes.initialized) return; // run once
svgNotes.enabled = false;
svgNotes.initialized = true;
}
function editOn() {
layer.style('display', 'block');
}
function editOff() {
layer.selectAll('.note').remove();
layer.style('display', 'none');
}
// Loosely-coupled osm service for fetching notes.
function getService() {
if (services.osm && !_notes) {
_notes = services.osm;
_notes.on('loadedNotes', throttledRedraw);
} else if (!services.osm && _notes) {
_notes = null;
if (services.osm && !_osmService) {
_osmService = services.osm;
_osmService.on('loadedNotes', throttledRedraw);
} else if (!services.osm && _osmService) {
_osmService = null;
}
return _notes;
return _osmService;
}
function showLayer() {
// Show the notes
function editOn() {
if (!_notesVisible) {
_notesVisible = true;
drawLayer
.style('display', 'block');
}
}
// Immediately remove the notes and their touch targets
function editOff() {
if (_notesVisible) {
_notesVisible = false;
drawLayer
.style('display', 'none');
drawLayer.selectAll('.note')
.remove();
touchLayer.selectAll('.note')
.remove();
}
}
// Enable the layer. This shows the notes and transitions them to visible.
function layerOn() {
editOn();
layer
.classed('disabled', false)
drawLayer
.style('opacity', 0)
.transition()
.duration(250)
@@ -66,30 +81,35 @@ export function svgNotes(projection, context, dispatch) {
}
function hideLayer() {
editOff();
// Disable the layer. This transitions the layer invisible and then hides the notes.
function layerOff() {
throttledRedraw.cancel();
layer.interrupt();
drawLayer.interrupt();
touchLayer.selectAll('.note')
.remove();
layer
drawLayer
.transition()
.duration(250)
.style('opacity', 0)
.on('end interrupt', function () {
layer.classed('disabled', true);
editOff();
dispatch.call('change');
});
}
function update() {
// Update the note markers
function updateMarkers() {
if (!_notesVisible || !_notesEnabled) return;
var service = getService();
var selectedID = context.selectedNoteID();
var data = (service ? service.notes(projection) : []);
var transform = svgPointTransform(projection);
var notes = layer.selectAll('.note')
var getTransform = svgPointTransform(projection);
// Draw markers..
var notes = drawLayer.selectAll('.note')
.data(data, function(d) { return d.status + d.id; });
// exit
@@ -139,55 +159,90 @@ export function svgNotes(projection, context, dispatch) {
// update
notes
.merge(notesEnter)
.sort(function(a, b) {
return (a.id === selectedID) ? 1
: (b.id === selectedID) ? -1
: b.loc[1] - a.loc[1]; // sort Y
.sort(sortY)
.classed('selected', function(d) {
var mode = context.mode();
var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging
return !isMoving && d.id === selectedID;
})
.classed('selected', function(d) { return d.id === selectedID; })
.attr('transform', transform);
.attr('transform', getTransform);
// Draw targets..
if (touchLayer.empty()) return;
var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
var targets = touchLayer.selectAll('.note')
.data(data, function(d) { return d.id; });
// exit
targets.exit()
.remove();
// enter/update
targets.enter()
.append('rect')
.attr('width', '20px')
.attr('height', '20px')
.attr('x', '-8px')
.attr('y', '-22px')
.merge(targets)
.sort(sortY)
.attr('class', function(d) {
var newClass = (d.id < 0 ? 'new' : '');
return 'note target note-' + d.id + ' ' + fillClass + newClass;
})
.attr('transform', getTransform);
function sortY(a, b) {
return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
}
}
// Draw the notes layer and schedule loading notes and updating markers.
function drawNotes(selection) {
var enabled = svgNotes.enabled;
var service = getService();
layer = selection.selectAll('.layer-notes')
.data(service ? [0] : []);
layer.exit()
.remove();
layer.enter()
.append('g')
.attr('class', 'layer-notes')
.style('display', enabled ? 'block' : 'none')
.merge(layer);
function dimensions() {
return [window.innerWidth, window.innerHeight];
var surface = context.surface();
if (surface && !surface.empty()) {
touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
}
if (enabled) {
drawLayer = selection.selectAll('.layer-notes')
.data(service ? [0] : []);
drawLayer.exit()
.remove();
drawLayer = drawLayer.enter()
.append('g')
.attr('class', 'layer-notes')
.style('display', _notesEnabled ? 'block' : 'none')
.merge(drawLayer);
if (_notesEnabled) {
if (service && ~~context.map().zoom() >= minZoom) {
editOn();
service.loadNotes(projection, dimensions());
update();
service.loadNotes(projection);
updateMarkers();
} else {
editOff();
}
}
}
drawNotes.enabled = function(val) {
if (!arguments.length) return svgNotes.enabled;
svgNotes.enabled = val;
if (svgNotes.enabled) {
showLayer();
// Toggles the layer on and off
drawNotes.enabled = function(val) {
if (!arguments.length) return _notesEnabled;
_notesEnabled = val;
if (_notesEnabled) {
layerOn();
} else {
hideLayer();
layerOff();
if (context.selectedNoteID()) {
context.enter(modeBrowse(context));
}
@@ -197,6 +252,6 @@ export function svgNotes(projection, context, dispatch) {
return this;
};
init();
return drawNotes;
}
+1 -1
View File
@@ -2,7 +2,7 @@ export function svgTouch() {
function drawTouch(selection) {
selection.selectAll('.layer-touch')
.data(['areas', 'lines', 'points', 'turns', 'notes'])
.data(['areas', 'lines', 'points', 'turns', 'markers'])
.enter()
.append('g')
.attr('class', function(d) { return 'layer-touch ' + d; });
+5 -8
View File
@@ -56,15 +56,12 @@ export function uiCombobox(context, klass) {
var parent = this.parentNode;
var sibling = this.nextSibling;
var caret = d3_select(parent).selectAll('.combobox-caret')
d3_select(parent).selectAll('.combobox-caret')
.filter(function(d) { return d === input.node(); })
.data([input.node()]);
caret = caret.enter()
.data([input.node()])
.enter()
.insert('div', function() { return sibling; })
.attr('class', 'combobox-caret')
.merge(caret);
.attr('class', 'combobox-caret');
}
@@ -341,7 +338,7 @@ export function uiCombobox(context, klass) {
// Dispatches an 'accept' event if an option has been chosen.
// Then hides the combobox.
function accept(d) {
d = d || _choice;
d = d || _choice || value();
if (d) {
utilGetSetValue(input, d.value);
utilTriggerEvent(input, 'change');
+21 -8
View File
@@ -8,6 +8,7 @@ import { select as d3_select } from 'd3-selection';
import { t } from '../util/locale';
import { osmChangeset } from '../osm';
import { services } from '../services';
import { uiChangesetEditor } from './changeset_editor';
import { uiCommitChanges } from './commit_changes';
import { uiCommitWarnings } from './commit_warnings';
@@ -63,6 +64,8 @@ export function uiCommit(context) {
}
var tags;
// Initialize changeset if one does not exist yet.
// Also pull values from local storage.
if (!_changeset) {
var detected = utilDetect();
tags = {
@@ -81,13 +84,9 @@ export function uiCommit(context) {
tags.hashtags = hashtags;
}
// iD 2.8.1 could write a literal 'undefined' here.. see #5021
// (old source values expire after 2 days, so 'undefined' checks can go away in v2.9)
var source = context.storage('source');
if (source && source !== 'undefined') {
if (source) {
tags.source = source;
} else if (source === 'undefined') {
context.storage('source', null);
}
_changeset = new osmChangeset({ tags: tags });
@@ -95,8 +94,22 @@ export function uiCommit(context) {
tags = _clone(_changeset.tags);
// assign tags for imagery used
var imageryUsed = context.history().imageryUsed().join(';').substr(0, 255);
tags.imagery_used = imageryUsed || 'None';
// assign tags for closed issues and notes
var osmClosed = osm.getClosedIDs();
if (osmClosed.length) {
tags['closed:note'] = osmClosed.join(';').substr(0, 255);
}
if (services.keepRight) {
var krClosed = services.keepRight.getClosedIDs();
if (krClosed.length) {
tags['closed:keepright'] = krClosed.join(';').substr(0, 255);
}
}
_changeset = _changeset.update({ tags: tags });
var header = selection.selectAll('.header')
@@ -109,17 +122,17 @@ export function uiCommit(context) {
headerTitle
.append('div')
.attr('class', 'header-block header-block-outer');
headerTitle
.append('div')
.attr('class', 'header-block')
.append('h3')
.text(t('commit.title'));
headerTitle
.append('div')
.attr('class', 'header-block header-block-outer header-block-close')
.append('button')
.append('button')
.attr('class', 'close')
.on('click', function() { context.enter(modeBrowse(context)); })
.call(svgIcon('#iD-icon-close'));
+3 -1
View File
@@ -26,7 +26,9 @@ export function uiCommitWarnings(context) {
}, {});
_forEach(issues, function(instances, severity) {
instances = _uniqBy(instances, function(val) { return val.id + '_' + val.message.replace(/\s+/g,''); });
instances = _uniqBy(instances, function(val) {
return val.entity || (val.id + '_' + val.message.replace(/\s+/g,''));
});
var section = severity + '-section';
var instanceItem = severity + '-item';
+20 -3
View File
@@ -4,17 +4,33 @@ import { svgIcon } from '../svg';
import {
uiDataHeader,
uiRawTagEditor
uiQuickLinks,
uiRawTagEditor,
uiTooltipHtml
} from './index';
export function uiDataEditor(context) {
var dataHeader = uiDataHeader();
var quickLinks = uiQuickLinks();
var rawTagEditor = uiRawTagEditor(context);
var _datum;
function dataEditor(selection) {
// quick links
var choices = [{
id: 'zoom_to',
label: 'inspector.zoom_to.title',
tooltip: function() {
return uiTooltipHtml(t('inspector.zoom_to.tooltip_data'), t('inspector.zoom_to.key'));
},
click: function zoomTo() {
context.mode().zoomToSelected();
}
}];
var header = selection.selectAll('.header')
.data([0]);
@@ -50,14 +66,15 @@ export function uiDataEditor(context) {
.append('div')
.attr('class', 'modal-section data-editor')
.merge(editor)
.call(dataHeader.datum(_datum));
.call(dataHeader.datum(_datum))
.call(quickLinks.choices(choices));
var rte = body.selectAll('.raw-tag-editor')
.data([0]);
rte.enter()
.append('div')
.attr('class', 'inspector-border raw-tag-editor inspector-inner data-editor')
.attr('class', 'raw-tag-editor inspector-inner data-editor')
.merge(rte)
.call(rawTagEditor
.expanded(true)
+1 -1
View File
@@ -104,7 +104,7 @@ export function uiEditMenu(context, operations) {
.attr('transform', function () { return 'translate(' + [2 * p, 5] + ')'; })
.attr('xlink:href', function (d) { return '#iD-operation-' + d.id; });
tooltip = d3_select(document.body)
tooltip = d3_select('#id-container')
.append('div')
.attr('class', 'tooltip-inner edit-menu-tooltip');
+52 -28
View File
@@ -15,12 +15,14 @@ import { actionChangeTags } from '../actions';
import { modeBrowse } from '../modes';
import { svgIcon } from '../svg';
import { uiPresetIcon } from './preset_icon';
import { uiQuickLinks } from './quick_links';
import { uiRawMemberEditor } from './raw_member_editor';
import { uiRawMembershipEditor } from './raw_membership_editor';
import { uiRawTagEditor } from './raw_tag_editor';
import { uiTagReference } from './tag_reference';
import { uiPresetEditor } from './preset_editor';
import { uiEntityIssues } from './entity_issues';
import { uiTooltipHtml } from './tooltipHtml';
import { utilCleanTags, utilRebind } from '../util';
@@ -35,6 +37,7 @@ export function uiEntityEditor(context) {
var _tagReference;
var entityIssues = uiEntityIssues(context);
var quickLinks = uiQuickLinks();
var presetEditor = uiPresetEditor(context).on('change', changeTags);
var rawTagEditor = uiRawTagEditor(context).on('change', changeTags);
var rawMemberEditor = uiRawMemberEditor(context);
@@ -49,28 +52,28 @@ export function uiEntityEditor(context) {
.data([0]);
// Enter
var enter = header.enter()
var headerEnter = header.enter()
.append('div')
.attr('class', 'header fillL cf');
enter
headerEnter
.append('button')
.attr('class', 'fl preset-reset preset-choose')
.call(svgIcon((textDirection === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
enter
headerEnter
.append('button')
.attr('class', 'fr preset-close')
.on('click', function() { context.enter(modeBrowse(context)); })
.call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
enter
headerEnter
.append('h3')
.text(t('inspector.edit'));
// Update
header = header
.merge(enter);
.merge(headerEnter);
header.selectAll('.preset-reset')
.on('click', function() {
@@ -83,11 +86,11 @@ export function uiEntityEditor(context) {
.data([0]);
// Enter
enter = body.enter()
var bodyEnter = body.enter()
.append('div')
.attr('class', 'inspector-body');
enter
bodyEnter
.append('div')
.attr('class', 'preset-list-item inspector-inner')
.append('div')
@@ -100,27 +103,31 @@ export function uiEntityEditor(context) {
.append('div')
.attr('class', 'label-inner');
enter
bodyEnter
.append('div')
.attr('class', 'inspector-border entity-issues');
.attr('class', 'preset-quick-links');
enter
bodyEnter
.append('div')
.attr('class', 'inspector-border preset-editor');
.attr('class', 'entity-issues');
enter
bodyEnter
.append('div')
.attr('class', 'inspector-border raw-tag-editor inspector-inner');
.attr('class', 'preset-editor');
enter
bodyEnter
.append('div')
.attr('class', 'inspector-border raw-member-editor inspector-inner');
.attr('class', 'raw-tag-editor inspector-inner');
enter
bodyEnter
.append('div')
.attr('class', 'raw-member-editor inspector-inner');
bodyEnter
.append('div')
.attr('class', 'raw-membership-editor inspector-inner');
enter
bodyEnter
.append('input')
.attr('type', 'text')
.attr('class', 'key-trap');
@@ -128,8 +135,9 @@ export function uiEntityEditor(context) {
// Update
body = body
.merge(enter);
.merge(bodyEnter);
// update header
if (_tagReference) {
body.selectAll('.preset-list-button-wrap')
.call(_tagReference.button);
@@ -149,7 +157,6 @@ export function uiEntityEditor(context) {
.preset(_activePreset)
);
var label = body.select('.label-inner');
var nameparts = label.selectAll('.namepart')
.data(_activePreset.name().split(' - '), function(d) { return d; });
@@ -168,6 +175,23 @@ export function uiEntityEditor(context) {
.entityID(_entityID)
);
// update quick links
var choices = [{
id: 'zoom_to',
label: 'inspector.zoom_to.title',
tooltip: function() {
return uiTooltipHtml(t('inspector.zoom_to.tooltip_feature'), t('inspector.zoom_to.key'));
},
click: function zoomTo() {
context.mode().zoomToSelected();
}
}];
body.select('.preset-quick-links')
.call(quickLinks.choices(choices));
// update editor sections
body.select('.preset-editor')
.call(presetEditor
.preset(_activePreset)
@@ -274,25 +298,25 @@ export function uiEntityEditor(context) {
}
entityEditor.modified = function(_) {
entityEditor.modified = function(val) {
if (!arguments.length) return _modified;
_modified = _;
_modified = val;
d3_selectAll('button.preset-close use')
.attr('xlink:href', (_modified ? '#iD-icon-apply' : '#iD-icon-close'));
return entityEditor;
};
entityEditor.state = function(_) {
entityEditor.state = function(val) {
if (!arguments.length) return _state;
_state = _;
_state = val;
return entityEditor;
};
entityEditor.entityID = function(_) {
entityEditor.entityID = function(val) {
if (!arguments.length) return _entityID;
_entityID = _;
_entityID = val;
_base = context.graph();
_coalesceChanges = false;
@@ -310,10 +334,10 @@ export function uiEntityEditor(context) {
};
entityEditor.preset = function(_) {
entityEditor.preset = function(val) {
if (!arguments.length) return _activePreset;
if (_ !== _activePreset) {
_activePreset = _;
if (val !== _activePreset) {
_activePreset = val;
_tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context)
.showing(false);
}
+17 -17
View File
@@ -231,38 +231,38 @@ export function uiField(context, presetField, entity, options) {
}
};
// A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
field.isShown = function() {
return _show || isPresent();
};
// An allowed field can appear in the UI or in the 'Add field' dropdown.
// A non-allowed field is hidden from the user altogether
field.isAllowed = function() {
if (!entity || isPresent()) return true; // a field with a value should always display
if (isPresent()) {
// always allow a field with a value to display
return true;
}
var latest = context.hasEntity(entity.id); // check the most current copy of the entity
if (!latest) return true;
var prerequisiteTag = field.prerequisiteTag;
if (prerequisiteTag && prerequisiteTag.key && field.entityID && context.hasEntity(field.entityID)) {
var value = context.entity(field.entityID).tags[prerequisiteTag.key];
if (value) {
if (prerequisiteTag.valueNot) {
return prerequisiteTag.valueNot !== value;
}
if (prerequisiteTag.value) {
return prerequisiteTag.value === value;
}
return true;
} else {
return false;
var require = field.prerequisiteTag;
if (require && require.key) {
var value = latest.tags[require.key];
if (!value) return false;
if (require.valueNot) {
return require.valueNot !== value;
}
if (require.value) {
return require.value === value;
}
return true;
}
return true;
};
field.focus = function() {
if (field.impl) {
field.impl.focus();
+2 -2
View File
@@ -4,7 +4,7 @@ import {
} from 'd3-selection';
import marked from 'marked';
import { t } from '../util/locale';
import { t, textDirection } from '../util/locale';
import { svgIcon } from '../svg';
import { icon } from './intro/helper';
@@ -197,7 +197,7 @@ export function uiFieldHelp(context, fieldName) {
titleEnter
.append('h2')
.attr('class', 'fl')
.attr('class', ((textDirection === 'rtl') ? 'fr' : 'fl'))
.text(t('help.field.' + fieldName + '.title'));
titleEnter
+12 -18
View File
@@ -7,25 +7,13 @@ import { utilGetSetValue, utilNoAuto } from '../util';
export function uiFormFields(context) {
var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
var _fieldsArr = [];
var _state = '';
var _fieldsArr;
var _klass = '';
function formFields(selection, klass) {
render(selection, klass);
}
formFields.tagsChanged = function() {};
function render(selection, klass) {
formFields.tagsChanged = function() {
render(selection, klass);
};
function formFields(selection) {
var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
var shown = allowedFields.filter(function(field) { return field.isShown(); });
var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
@@ -34,7 +22,7 @@ export function uiFormFields(context) {
container = container.enter()
.append('div')
.attr('class', 'form-fields-container ' + (klass || ''))
.attr('class', 'form-fields-container ' + (_klass || ''))
.merge(container);
@@ -111,7 +99,7 @@ export function uiFormFields(context) {
.on('accept', function (d) {
var field = d.field;
field.show();
render(selection);
selection.call(formFields); // rerender
if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
field.focus();
}
@@ -122,7 +110,7 @@ export function uiFormFields(context) {
formFields.fieldsArr = function(val) {
if (!arguments.length) return _fieldsArr;
_fieldsArr = val;
_fieldsArr = val || [];
return formFields;
};
@@ -132,6 +120,12 @@ export function uiFormFields(context) {
return formFields;
};
formFields.klass = function(val) {
if (!arguments.length) return _klass;
_klass = val;
return formFields;
};
return formFields;
}
+9
View File
@@ -181,6 +181,13 @@ export function uiHelp(context) {
'using',
'tracing',
'upload'
]],
['qa', [
'intro',
'tools_h',
'tools',
'issues_h',
'issues'
]]
];
@@ -228,6 +235,8 @@ export function uiHelp(context) {
'help.imagery.offsets_h': 3,
'help.streetlevel.using_h': 3,
'help.gps.using_h': 3,
'help.qa.tools_h': 3,
'help.qa.issues_h': 3
};
var replacements = {
+5
View File
@@ -30,6 +30,9 @@ export { uiGeolocate } from './geolocate';
export { uiHelp } from './help';
export { uiInfo } from './info';
export { uiInspector } from './inspector';
export { uiKeepRightDetails } from './keepRight_details';
export { uiKeepRightEditor } from './keepRight_editor';
export { uiKeepRightHeader } from './keepRight_header';
export { uiLasso } from './lasso';
export { uiLoading } from './loading';
export { uiMapData } from './map_data';
@@ -44,6 +47,7 @@ export { uiNoteReport } from './note_report';
export { uiPresetEditor } from './preset_editor';
export { uiPresetIcon } from './preset_icon';
export { uiPresetList } from './preset_list';
export { uiQuickLinks } from './quick_links';
export { uiRadialMenu } from './radial_menu';
export { uiRawMemberEditor } from './raw_member_editor';
export { uiRawMembershipEditor } from './raw_membership_editor';
@@ -64,4 +68,5 @@ export { uiTooltipHtml } from './tooltipHtml';
export { uiUndoRedo } from './undo_redo';
export { uiVersion } from './version';
export { uiViewOnOSM } from './view_on_osm';
export { uiViewOnKeepRight } from './view_on_keepRight';
export { uiZoom } from './zoom';
+1 -1
View File
@@ -306,7 +306,7 @@ export function uiInit(context) {
var panPixels = 80;
context.keybinding()
.on('⌫', function() { d3_event.preventDefault(); })
.on(t('sidebar.key'), ui.sidebar.toggle)
.on([t('sidebar.key'), '`', '²'], ui.sidebar.toggle) // #5663 - common QWERTY, AZERTY
.on('←', pan([panPixels, 0]))
.on('↑', pan([0, panPixels]))
.on('→', pan([-panPixels, 0]))
+6 -6
View File
@@ -92,9 +92,9 @@ export function uiInspector(context) {
inspector.showList = function() {};
inspector.setPreset = function() {};
inspector.state = function(_) {
inspector.state = function(val) {
if (!arguments.length) return _state;
_state = _;
_state = val;
entityEditor.state(_state);
// remove any old field help overlay that might have gotten attached to the inspector
@@ -104,16 +104,16 @@ export function uiInspector(context) {
};
inspector.entityID = function(_) {
inspector.entityID = function(val) {
if (!arguments.length) return _entityID;
_entityID = _;
_entityID = val;
return inspector;
};
inspector.newFeature = function(_) {
inspector.newFeature = function(val) {
if (!arguments.length) return _newFeature;
_newFeature = _;
_newFeature = val;
return inspector;
};
+132
View File
@@ -0,0 +1,132 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { dataEn } from '../../data';
import { modeSelect } from '../modes';
import { t } from '../util/locale';
import { utilDisplayName, utilEntityOrMemberSelector, utilEntityRoot } from '../util';
export function uiKeepRightDetails(context) {
var _error;
function errorDetail(d) {
var unknown = t('inspector.unknown');
if (!d) return unknown;
var errorType = d.error_type;
var parentErrorType = d.parent_error_type;
var et = dataEn.QA.keepRight.errorTypes[errorType];
var pt = dataEn.QA.keepRight.errorTypes[parentErrorType];
var detail;
if (et && et.description) {
detail = t('QA.keepRight.errorTypes.' + errorType + '.description', d.replacements);
} else if (pt && pt.description) {
detail = t('QA.keepRight.errorTypes.' + parentErrorType + '.description', d.replacements);
} else {
detail = unknown;
}
return detail;
}
function keepRightDetails(selection) {
var details = selection.selectAll('.kr_error-details')
.data(
(_error ? [_error] : []),
function(d) { return d.id + '-' + (d.status || 0); }
);
details.exit()
.remove();
var detailsEnter = details.enter()
.append('div')
.attr('class', 'kr_error-details kr_error-details-container');
// description
var descriptionEnter = detailsEnter
.append('div')
.attr('class', 'kr_error-details-description');
descriptionEnter
.append('h4')
.text(function() { return t('QA.keepRight.detail_description'); });
descriptionEnter
.append('div')
.attr('class', 'kr_error-details-description-text')
.html(errorDetail);
// If there are entity links in the error message..
descriptionEnter.selectAll('.kr_error_entity_link, .kr_error_object_link')
.each(function() {
var link = d3_select(this);
var isObjectLink = link.classed('kr_error_object_link');
var entityID = isObjectLink ?
(utilEntityRoot(_error.object_type) + _error.object_id)
: this.textContent;
var entity = context.hasEntity(entityID);
// Add click handler
link
.on('mouseover', function() {
context.surface().selectAll(utilEntityOrMemberSelector([entityID], context.graph()))
.classed('hover', true);
})
.on('mouseout', function() {
context.surface().selectAll('.hover')
.classed('hover', false);
})
.on('click', function() {
d3_event.preventDefault();
var osmlayer = context.layers().layer('osm');
if (!osmlayer.enabled()) {
osmlayer.enabled(true);
}
context.map().centerZoom(_error.loc, 20);
if (entity) {
context.enter(modeSelect(context, [entityID]));
} else {
context.loadEntity(entityID, function() {
context.enter(modeSelect(context, [entityID]));
});
}
});
// Replace with friendly name if possible
// (The entity may not yet be loaded into the graph)
if (entity) {
var name = utilDisplayName(entity); // try to use common name
if (!name && !isObjectLink) {
var preset = context.presets().match(entity, context.graph());
name = preset && !preset.isFallback() && preset.name(); // fallback to preset name
}
if (name) {
this.innerText = name;
}
}
});
}
keepRightDetails.error = function(val) {
if (!arguments.length) return _error;
_error = val;
return keepRightDetails;
};
return keepRightDetails;
}
+244
View File
@@ -0,0 +1,244 @@
import { dispatch as d3_dispatch } from 'd3-dispatch';
import { select as d3_select } from 'd3-selection';
import { t } from '../util/locale';
import { services } from '../services';
import { modeBrowse } from '../modes';
import { svgIcon } from '../svg';
import {
uiKeepRightDetails,
uiKeepRightHeader,
uiQuickLinks,
uiTooltipHtml,
uiViewOnKeepRight
} from './index';
import { utilNoAuto, utilRebind } from '../util';
export function uiKeepRightEditor(context) {
var dispatch = d3_dispatch('change');
var keepRightDetails = uiKeepRightDetails(context);
var keepRightHeader = uiKeepRightHeader(context);
var quickLinks = uiQuickLinks();
var _error;
function keepRightEditor(selection) {
// quick links
var choices = [{
id: 'zoom_to',
label: 'inspector.zoom_to.title',
tooltip: function() {
return uiTooltipHtml(t('inspector.zoom_to.tooltip_issue'), t('inspector.zoom_to.key'));
},
click: function zoomTo() {
context.mode().zoomToSelected();
}
}];
var header = selection.selectAll('.header')
.data([0]);
var headerEnter = header.enter()
.append('div')
.attr('class', 'header fillL');
headerEnter
.append('button')
.attr('class', 'fr keepRight-editor-close')
.on('click', function() {
context.enter(modeBrowse(context));
})
.call(svgIcon('#iD-icon-close'));
headerEnter
.append('h3')
.text(t('QA.keepRight.title'));
var body = selection.selectAll('.body')
.data([0]);
body = body.enter()
.append('div')
.attr('class', 'body')
.merge(body);
var editor = body.selectAll('.keepRight-editor')
.data([0]);
editor.enter()
.append('div')
.attr('class', 'modal-section keepRight-editor')
.merge(editor)
.call(keepRightHeader.error(_error))
.call(quickLinks.choices(choices))
.call(keepRightDetails.error(_error))
.call(keepRightSaveSection);
var footer = selection.selectAll('.footer')
.data([0]);
footer.enter()
.append('div')
.attr('class', 'footer')
.merge(footer)
.call(uiViewOnKeepRight(context).what(_error));
}
function keepRightSaveSection(selection) {
var isSelected = (_error && _error.id === context.selectedErrorID());
var isShown = (_error && (isSelected || _error.newComment || _error.comment));
var saveSection = selection.selectAll('.error-save')
.data(
(isShown ? [_error] : []),
function(d) { return d.id + '-' + (d.status || 0); }
);
// exit
saveSection.exit()
.remove();
// enter
var saveSectionEnter = saveSection.enter()
.append('div')
.attr('class', 'keepRight-save save-section cf');
saveSectionEnter
.append('h4')
.attr('class', '.error-save-header')
.text(t('QA.keepRight.comment'));
saveSectionEnter
.append('textarea')
.attr('class', 'new-comment-input')
.attr('placeholder', t('QA.keepRight.comment_placeholder'))
.attr('maxlength', 1000)
.property('value', function(d) { return d.newComment || d.comment; })
.call(utilNoAuto)
.on('input', changeInput)
.on('blur', changeInput);
// update
saveSection = saveSectionEnter
.merge(saveSection)
.call(keepRightSaveButtons);
function changeInput() {
var input = d3_select(this);
var val = input.property('value').trim();
if (val === _error.comment) {
val = undefined;
}
// store the unsaved comment with the error itself
_error = _error.update({ newComment: val });
var keepRight = services.keepRight;
if (keepRight) {
keepRight.replaceError(_error); // update keepright cache
}
saveSection
.call(keepRightSaveButtons);
}
}
function keepRightSaveButtons(selection) {
var isSelected = (_error && _error.id === context.selectedErrorID());
var buttonSection = selection.selectAll('.buttons')
.data((isSelected ? [_error] : []), function(d) { return d.status + d.id; });
// exit
buttonSection.exit()
.remove();
// enter
var buttonEnter = buttonSection.enter()
.append('div')
.attr('class', 'buttons');
buttonEnter
.append('button')
.attr('class', 'button comment-button action')
.text(t('QA.keepRight.save_comment'));
buttonEnter
.append('button')
.attr('class', 'button close-button action');
buttonEnter
.append('button')
.attr('class', 'button ignore-button action');
// update
buttonSection = buttonSection
.merge(buttonEnter);
buttonSection.select('.comment-button') // select and propagate data
.attr('disabled', function(d) {
return d.newComment === undefined ? true : null;
})
.on('click.comment', function(d) {
this.blur(); // avoid keeping focus on the button - #4641
var keepRight = services.keepRight;
if (keepRight) {
keepRight.postKeepRightUpdate(d, function(err, error) {
dispatch.call('change', error);
});
}
});
buttonSection.select('.close-button') // select and propagate data
.text(function(d) {
var andComment = (d.newComment !== undefined ? '_comment' : '');
return t('QA.keepRight.close' + andComment);
})
.on('click.close', function(d) {
this.blur(); // avoid keeping focus on the button - #4641
var keepRight = services.keepRight;
if (keepRight) {
d.state = 'ignore_t'; // ignore temporarily (error fixed)
keepRight.postKeepRightUpdate(d, function(err, error) {
dispatch.call('change', error);
});
}
});
buttonSection.select('.ignore-button') // select and propagate data
.text(function(d) {
var andComment = (d.newComment !== undefined ? '_comment' : '');
return t('QA.keepRight.ignore' + andComment);
})
.on('click.ignore', function(d) {
this.blur(); // avoid keeping focus on the button - #4641
var keepRight = services.keepRight;
if (keepRight) {
d.state = 'ignore'; // ignore permanently (false positive)
keepRight.postKeepRightUpdate(d, function(err, error) {
dispatch.call('change', error);
});
}
});
}
keepRightEditor.error = function(val) {
if (!arguments.length) return _error;
_error = val;
return keepRightEditor;
};
return utilRebind(keepRightEditor, dispatch, 'on');
}
+71
View File
@@ -0,0 +1,71 @@
import { dataEn } from '../../data';
import { svgIcon } from '../svg';
import { t } from '../util/locale';
export function uiKeepRightHeader() {
var _error;
function errorTitle(d) {
var unknown = t('inspector.unknown');
if (!d) return unknown;
var errorType = d.error_type;
var parentErrorType = d.parent_error_type;
var et = dataEn.QA.keepRight.errorTypes[errorType];
var pt = dataEn.QA.keepRight.errorTypes[parentErrorType];
if (et && et.title) {
return t('QA.keepRight.errorTypes.' + errorType + '.title');
} else if (pt && pt.title) {
return t('QA.keepRight.errorTypes.' + parentErrorType + '.title');
} else {
return unknown;
}
}
function keepRightHeader(selection) {
var header = selection.selectAll('.kr_error-header')
.data(
(_error ? [_error] : []),
function(d) { return d.id + '-' + (d.status || 0); }
);
header.exit()
.remove();
var headerEnter = header.enter()
.append('div')
.attr('class', 'kr_error-header');
var iconEnter = headerEnter
.append('div')
.attr('class', 'kr_error-header-icon')
.classed('new', function(d) { return d.id < 0; });
iconEnter
.append('div')
.attr('class', function(d) {
return 'preset-icon-28 kr_error kr_error-' + d.id + ' kr_error_type_' + d.parent_error_type;
})
.call(svgIcon('#iD-icon-bolt', 'kr_error-fill'));
headerEnter
.append('div')
.attr('class', 'kr_error-header-label')
.text(errorTitle);
}
keepRightHeader.error = function(val) {
if (!arguments.length) return _error;
_error = val;
return keepRightHeader;
};
return keepRightHeader;
}
+79 -5
View File
@@ -30,6 +30,7 @@ export function uiMapData(context) {
var _dataLayerContainer = d3_select(null);
var _fillList = d3_select(null);
var _featureList = d3_select(null);
var _QAList = d3_select(null);
function showsFeature(d) {
@@ -38,6 +39,7 @@ export function uiMapData(context) {
function autoHiddenFeature(d) {
if (d.type === 'kr_error') return context.errors().autoHidden(d);
return context.features().autoHidden(d);
}
@@ -48,6 +50,22 @@ export function uiMapData(context) {
}
function showsQA(d) {
var QAKeys = [d];
var QALayers = layers.all().filter(function(obj) { return QAKeys.indexOf(obj.id) !== -1; });
var data = QALayers.filter(function(obj) { return obj.layer.supported(); });
function layerSupported(d) {
return d.layer && d.layer.supported();
}
function layerEnabled(d) {
return layerSupported(d) && d.layer.enabled();
}
return layerEnabled(data[0]);
}
function showsFill(d) {
return _fillSelected === d;
}
@@ -207,6 +225,58 @@ export function uiMapData(context) {
}
function drawQAItems(selection) {
var qaKeys = ['keepRight'];
var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
var ul = selection
.selectAll('.layer-list-qa')
.data([0]);
ul = ul.enter()
.append('ul')
.attr('class', 'layer-list layer-list-qa')
.merge(ul);
var li = ul.selectAll('.list-item')
.data(qaLayers);
li.exit()
.remove();
var liEnter = li.enter()
.append('li')
.attr('class', function(d) { return 'list-item list-item-' + d.id; });
var labelEnter = liEnter
.append('label')
.each(function(d) {
d3_select(this)
.call(tooltip()
.title(t('map_data.layers.' + d.id + '.tooltip'))
.placement('bottom')
);
});
labelEnter
.append('input')
.attr('type', 'checkbox')
.on('change', function(d) { toggleLayer(d.id); });
labelEnter
.append('span')
.text(function(d) { return t('map_data.layers.' + d.id + '.title'); });
// Update
li
.merge(liEnter)
.classed('active', function (d) { return d.layer.enabled(); })
.selectAll('input')
.property('checked', function (d) { return d.layer.enabled(); });
}
// Beta feature - sample vector layers to support Detroit Mapping Challenge
// https://github.com/osmus/detroit-mapping-challenge
function drawVectorItems(selection) {
@@ -427,10 +497,9 @@ export function uiMapData(context) {
.call(tooltip()
.html(true)
.title(function(d) {
var tip = t(name + '.' + d + '.tooltip'),
key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
if (name === 'feature' && autoHiddenFeature(d)) {
var tip = t(name + '.' + d + '.tooltip');
var key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null);
if ((name === 'feature' || name === 'keepRight') && autoHiddenFeature(d)) {
var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden');
tip += '<div>' + msg + '</div>';
}
@@ -461,7 +530,7 @@ export function uiMapData(context) {
.selectAll('input')
.property('checked', active)
.property('indeterminate', function(d) {
return (name === 'feature' && autoHiddenFeature(d));
return ((name === 'feature' || name === 'keepRight') && autoHiddenFeature(d));
});
}
@@ -502,6 +571,7 @@ export function uiMapData(context) {
function update() {
_dataLayerContainer
.call(drawOsmItems)
.call(drawQAItems)
.call(drawPhotoItems)
.call(drawCustomDataItems)
.call(drawVectorItems); // Beta - Detroit mapping challenge
@@ -511,6 +581,9 @@ export function uiMapData(context) {
_featureList
.call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature);
_QAList
.call(drawListItems, ['keep-right'], 'checkbox', 'QA', function(d) { toggleLayer(d); }, showsQA);
}
@@ -611,6 +684,7 @@ export function uiMapData(context) {
.append('div')
.attr('class', 'pane-content');
// data layers
content
.append('div')
+2 -2
View File
@@ -110,9 +110,9 @@ export function uiNoteComments() {
}
noteComments.note = function(_) {
noteComments.note = function(val) {
if (!arguments.length) return _note;
_note = _;
_note = val;
return noteComments;
};
+20 -3
View File
@@ -16,6 +16,8 @@ import {
uiNoteComments,
uiNoteHeader,
uiNoteReport,
uiQuickLinks,
uiTooltipHtml,
uiViewOnOSM,
} from './index';
@@ -27,6 +29,7 @@ import {
export function uiNoteEditor(context) {
var dispatch = d3_dispatch('change');
var quickLinks = uiQuickLinks();
var noteComments = uiNoteComments();
var noteHeader = uiNoteHeader();
@@ -37,6 +40,19 @@ export function uiNoteEditor(context) {
function noteEditor(selection) {
// quick links
var choices = [{
id: 'zoom_to',
label: 'inspector.zoom_to.title',
tooltip: function() {
return uiTooltipHtml(t('inspector.zoom_to.tooltip_note'), t('inspector.zoom_to.key'));
},
click: function zoomTo() {
context.mode().zoomToSelected();
}
}];
var header = selection.selectAll('.header')
.data([0]);
@@ -73,6 +89,7 @@ export function uiNoteEditor(context) {
.attr('class', 'modal-section note-editor')
.merge(editor)
.call(noteHeader.note(_note))
.call(quickLinks.choices(choices))
.call(noteComments.note(_note))
.call(noteSaveSection);
@@ -155,7 +172,7 @@ export function uiNoteEditor(context) {
noteSaveEnter
.append('textarea')
.attr('id', 'new-comment-input')
.attr('class', 'new-comment-input')
.attr('placeholder', t('note.inputPlaceholder'))
.attr('maxlength', 1000)
.property('value', function(d) { return d.newComment; })
@@ -425,9 +442,9 @@ export function uiNoteEditor(context) {
}
noteEditor.note = function(_) {
noteEditor.note = function(val) {
if (!arguments.length) return _note;
_note = _;
_note = val;
return noteEditor;
};
+2 -2
View File
@@ -49,9 +49,9 @@ export function uiNoteHeader() {
}
noteHeader.note = function(_) {
noteHeader.note = function(val) {
if (!arguments.length) return _note;
_note = _;
_note = val;
return noteHeader;
};
+10 -13
View File
@@ -1,23 +1,20 @@
import { t } from '../util/locale';
import { osmNote } from '../osm';
import { services } from '../services';
import { svgIcon } from '../svg';
import {
osmNote
} from '../osm';
export function uiNoteReport() {
var _note;
var url = 'https://www.openstreetmap.org/reports/new?reportable_id=';
function noteReport(selection) {
var url;
if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) {
url = services.osm.noteReportURL(_note);
}
if (!(_note instanceof osmNote)) return;
url += _note.id + '&reportable_type=Note';
var data = ((!_note || _note.isNew()) ? [] : [_note]);
var link = selection.selectAll('.note-report')
.data(data, function(d) { return d.id; });
.data(url ? [url] : []);
// exit
link.exit()
@@ -28,7 +25,7 @@ export function uiNoteReport() {
.append('a')
.attr('class', 'note-report')
.attr('target', '_blank')
.attr('href', url)
.attr('href', function(d) { return d; })
.call(svgIcon('#iD-icon-out-link', 'inline'));
linkEnter
@@ -37,9 +34,9 @@ export function uiNoteReport() {
}
noteReport.note = function(_) {
noteReport.note = function(val) {
if (!arguments.length) return _note;
_note = _;
_note = val;
return noteReport;
};
+3 -3
View File
@@ -87,8 +87,9 @@ export function uiPresetEditor(context) {
selection
.call(formFields
.fieldsArr(_fieldsArr)
.state(_state),
'inspector-inner fillL3');
.state(_state)
.klass('inspector-inner fillL3')
);
selection.selectAll('.wrap-form-field input')
@@ -120,7 +121,6 @@ export function uiPresetEditor(context) {
presetEditor.tags = function(val) {
if (!arguments.length) return _tags;
_tags = val;
formFields.tagsChanged();
// Don't reset _fieldsArr here.
return presetEditor;
};
+62
View File
@@ -0,0 +1,62 @@
import {
event as d3_event,
select as d3_select
} from 'd3-selection';
import { t } from '../util/locale';
import { tooltip } from '../util/tooltip';
export function uiQuickLinks() {
var _choices = [];
function quickLinks(selection) {
var container = selection.selectAll('.quick-links')
.data([0]);
container = container.enter()
.append('div')
.attr('class', 'quick-links')
.merge(container);
var items = container.selectAll('.quick-link')
.data(_choices, function(d) { return d.id; });
items.exit()
.remove();
items.enter()
.append('a')
.attr('class', function(d) { return 'quick-link quick-link-' + d.id; })
.attr('href', '#')
.text(function(d) { return t(d.label); })
.each(function(d) {
if (typeof d.tooltip !== 'function') return;
d3_select(this)
.call(tooltip().html(true).title(d.tooltip).placement('bottom'));
})
.on('click', function(d) {
if (typeof d.click !== 'function') return;
d3_event.preventDefault();
d.click(d);
});
}
// val should be an array of choices like:
// [{
// id: 'link-id',
// label: 'translation.key',
// tooltip: function(d),
// click: function(d)
// }, ..]
quickLinks.choices = function(val) {
if (!arguments.length) return _choices;
_choices = val;
return quickLinks;
};
return quickLinks;
}
+11 -7
View File
@@ -1,3 +1,5 @@
import _uniq from 'lodash-es/uniq';
import {
select as d3_select,
selectAll as d3_selectAll
@@ -19,7 +21,7 @@ export function uiShortcuts(context) {
context.keybinding()
.on(t('shortcuts.toggle.key'), function () {
.on([t('shortcuts.toggle.key'), '?'], function () {
if (d3_selectAll('.modal-shortcuts').size()) { // already showing
if (_modalSelection) {
_modalSelection.close();
@@ -179,7 +181,12 @@ export function uiShortcuts(context) {
arr = ['F11'];
}
return arr.map(function(s) {
// replace translations
arr = arr.map(function(s) {
return uiCmd.display(s.indexOf('.') !== -1 ? t(s) : s);
});
return _uniq(arr).map(function(s) {
return {
shortcut: s,
separator: d.separator
@@ -191,17 +198,14 @@ export function uiShortcuts(context) {
var selection = d3_select(this);
var click = d.shortcut.toLowerCase().match(/(.*).click/);
if (click && click[1]) {
if (click && click[1]) { // replace "left_click", "right_click" with mouse icon
selection
.call(svgIcon('#iD-walkthrough-mouse', 'mouseclick', click[1]));
} else {
selection
.append('kbd')
.attr('class', 'shortcut')
.text(function (d) {
var key = d.shortcut;
return key.indexOf('.') !== -1 ? uiCmd.display(t(key)) : uiCmd.display(key);
});
.text(function (d) { return d.shortcut; });
}
if (i < nodes.length - 1) {
+30 -13
View File
@@ -9,18 +9,9 @@ import {
selectAll as d3_selectAll
} from 'd3-selection';
import {
osmEntity,
osmNote
} from '../osm';
import {
uiDataEditor,
uiFeatureList,
uiInspector,
uiNoteEditor
} from './index';
import { osmEntity, osmNote, krError } from '../osm';
import { services } from '../services';
import { uiDataEditor, uiFeatureList, uiInspector, uiNoteEditor, uiKeepRightEditor } from './index';
import { textDirection } from '../util/locale';
@@ -28,9 +19,11 @@ export function uiSidebar(context) {
var inspector = uiInspector(context);
var dataEditor = uiDataEditor(context);
var noteEditor = uiNoteEditor(context);
var keepRightEditor = uiKeepRightEditor(context);
var _current;
var _wasData = false;
var _wasNote = false;
var _wasKRError = false;
function sidebar(selection) {
@@ -127,12 +120,34 @@ export function uiSidebar(context) {
if (context.mode().id === 'drag-note') return;
_wasNote = true;
var osm = services.osm;
if (osm) {
datum = osm.getNote(datum.id); // marker may contain stale data - get latest
}
sidebar
.show(noteEditor.note(datum));
selection.selectAll('.sidebar-component')
.classed('inspector-hover', true);
} else if (datum instanceof krError) {
_wasKRError = true;
var keepRight = services.keepRight;
if (keepRight) {
datum = keepRight.getError(datum.id); // marker may contain stale data - get latest
}
d3_selectAll('.kr_error')
.classed('hover', function(d) { return d.id === datum.id; });
sidebar
.show(keepRightEditor.error(datum));
selection.selectAll('.sidebar-component')
.classed('inspector-hover', true);
} else if (!_current && (datum instanceof osmEntity)) {
featureListWrap
.classed('inspector-hidden', true);
@@ -158,10 +173,12 @@ export function uiSidebar(context) {
inspector
.state('hide');
} else if (_wasData || _wasNote) {
} else if (_wasData || _wasNote || _wasKRError) {
_wasNote = false;
_wasData = false;
_wasKRError = false;
d3_selectAll('.note').classed('hover', false);
d3_selectAll('.kr_error').classed('hover', false);
sidebar.hide();
}
}
+49 -41
View File
@@ -1,6 +1,3 @@
import _find from 'lodash-es/find';
import _omit from 'lodash-es/omit';
import {
event as d3_event,
select as d3_select
@@ -10,10 +7,11 @@ import { t } from '../util/locale';
import { utilDetect } from '../util/detect';
import { services } from '../services';
import { svgIcon } from '../svg';
import { utilQsString } from '../util';
export function uiTagReference(tag) {
var taginfo = services.taginfo;
var wikibase = services.osmWikibase;
var tagReference = {};
var _button = d3_select(null);
@@ -21,42 +19,49 @@ export function uiTagReference(tag) {
var _loaded;
var _showing;
/**
* @returns {{itemTitle: String, description: String, image: String|null}|null}
**/
function findLocal(data) {
var locale = utilDetect().locale.toLowerCase();
var localized;
var entity = data.tag || data.key;
if (!entity) return null;
if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br'
localized = _find(data, function(d) {
return d.lang.toLowerCase() === locale;
});
if (localized) return localized;
var result = {
title: entity.title,
description: entity.description,
};
if (entity.claims) {
var langCode = utilDetect().locale.toLowerCase();
var url;
var image = wikibase.claimToValue(entity, 'P4', langCode);
if (image) {
url = 'https://commons.wikimedia.org/w/index.php';
} else {
image = wikibase.claimToValue(entity, 'P28', langCode);
if (image) {
url = 'https://wiki.openstreetmap.org/w/index.php';
}
}
if (image) {
result.image = {
url: url,
title: 'Special:Redirect/file/' + image
};
}
}
// try the non-regional version of a language, like
// 'en' if the language is 'en-US'
if (locale.indexOf('-') !== -1) {
var first = locale.split('-')[0];
localized = _find(data, function(d) {
return d.lang.toLowerCase() === first;
});
if (localized) return localized;
}
// finally fall back to english
return _find(data, function(d) {
return d.lang.toLowerCase() === 'en';
});
return result;
}
function load(param) {
if (!taginfo) return;
if (!wikibase) return;
_button
.classed('tag-reference-loading', true);
taginfo.docs(param, function show(err, data) {
wikibase.getEntity(param, function show(err, data) {
var docs;
if (!err && data) {
docs = findLocal(data);
@@ -65,23 +70,25 @@ export function uiTagReference(tag) {
_body.html('');
if (!docs || !docs.title) {
if (param.hasOwnProperty('value')) {
load(_omit(param, 'value')); // retry with key only
} else {
_body
.append('p')
.attr('class', 'tag-reference-description')
.text(t('inspector.no_documentation_key'));
done();
}
_body
.append('p')
.attr('class', 'tag-reference-description')
.text(t('inspector.no_documentation_key'));
done();
return;
}
if (docs.image && docs.image.thumb_url_prefix) {
if (docs.image) {
var imageUrl = docs.image.url + '?' + utilQsString({
title: docs.image.title,
width: 100,
height: 100,
});
_body
.append('img')
.attr('class', 'tag-reference-wiki-image')
.attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix)
.attr('src', imageUrl)
.on('load', function() { done(); })
.on('error', function() { d3_select(this).remove(); done(); });
} else {
@@ -91,7 +98,7 @@ export function uiTagReference(tag) {
_body
.append('p')
.attr('class', 'tag-reference-description')
.text(docs.description || t('inspector.documentation_redirect'));
.text(docs.description || t('inspector.no_documentation_key'));
_body
.append('a')
@@ -101,7 +108,7 @@ export function uiTagReference(tag) {
.attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title)
.call(svgIcon('#iD-icon-out-link', 'inline'))
.append('span')
.text(t('inspector.reference'));
.text(t('inspector.edit_reference'));
// Add link to info about "good changeset comments" - #2923
if (param.key === 'comment') {
@@ -171,6 +178,7 @@ export function uiTagReference(tag) {
} else if (_loaded) {
done();
} else {
tag.langCode = utilDetect().locale.toLowerCase();
load(tag);
}
});
+45
View File
@@ -0,0 +1,45 @@
import { t } from '../util/locale';
import { services } from '../services';
import { svgIcon } from '../svg';
import { krError } from '../osm';
export function uiViewOnKeepRight() {
var _error; // a keepright error
function viewOnKeepRight(selection) {
var url;
if (services.keepRight && (_error instanceof krError)) {
url = services.keepRight.errorURL(_error);
}
var link = selection.selectAll('.view-on-keepRight')
.data(url ? [url] : []);
// exit
link.exit()
.remove();
// enter
var linkEnter = link.enter()
.append('a')
.attr('class', 'view-on-keepRight')
.attr('target', '_blank')
.attr('href', function(d) { return d; })
.call(svgIcon('#iD-icon-out-link', 'inline'));
linkEnter
.append('span')
.text(t('inspector.view_on_keepRight'));
}
viewOnKeepRight.what = function(val) {
if (!arguments.length) return _error;
_error = val;
return viewOnKeepRight;
};
return viewOnKeepRight;
}
+1 -4
View File
@@ -1,9 +1,6 @@
import { t } from '../util/locale';
import { osmEntity, osmNote } from '../osm';
import { svgIcon } from '../svg';
import {
osmEntity,
osmNote
} from '../osm';
export function uiViewOnOSM(context) {
+22 -16
View File
@@ -9,7 +9,8 @@ export function utilDetect(force) {
detected = {};
var ua = navigator.userAgent,
m = null;
m = null,
q = utilStringQs(window.location.hash.substring(1));
m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge
if (m !== null) {
@@ -59,24 +60,30 @@ export function utilDetect(force) {
// Added due to incomplete svg style support. See #715
detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 );
detected.locale = (navigator.language || navigator.userLanguage || 'en-US');
detected.language = detected.locale.split('-')[0];
// Set locale based on url param (format 'en-US') or browser lang (default)
if (q.hasOwnProperty('locale')) {
detected.locale = q.locale;
detected.language = q.locale.split('-')[0];
} else {
detected.locale = (navigator.language || navigator.userLanguage || 'en-US');
detected.language = detected.locale.split('-')[0];
// Search `navigator.languages` for a better locale.. Prefer the first language,
// unless the second language is a culture-specific version of the first one, see #3842
if (navigator.languages && navigator.languages.length > 0) {
var code0 = navigator.languages[0],
parts0 = code0.split('-');
// Search `navigator.languages` for a better locale. Prefer the first language,
// unless the second language is a culture-specific version of the first one, see #3842
if (navigator.languages && navigator.languages.length > 0) {
var code0 = navigator.languages[0],
parts0 = code0.split('-');
detected.locale = code0;
detected.language = parts0[0];
detected.locale = code0;
detected.language = parts0[0];
if (navigator.languages.length > 1 && parts0.length === 1) {
var code1 = navigator.languages[1],
parts1 = code1.split('-');
if (navigator.languages.length > 1 && parts0.length === 1) {
var code1 = navigator.languages[1],
parts1 = code1.split('-');
if (parts1[0] === parts0[0]) {
detected.locale = code1;
if (parts1[0] === parts0[0]) {
detected.locale = code1;
}
}
}
}
@@ -90,7 +97,6 @@ export function utilDetect(force) {
}
// detect text direction
var q = utilStringQs(window.location.hash.substring(1));
var lang = dataLocales[detected.locale];
if ((lang && lang.rtl) || (q.rtl === 'true')) {
detected.textDirection = 'rtl';
+1 -1
View File
@@ -5,6 +5,7 @@ export { utilDisplayName } from './util';
export { utilDisplayNameForPath } from './util';
export { utilDisplayType } from './util';
export { utilDisplayLabel } from './util';
export { utilEntityRoot } from './util';
export { utilEditDistance } from './util';
export { utilEntitySelector } from './util';
export { utilEntityOrMemberSelector } from './util';
@@ -28,7 +29,6 @@ export { utilRebind } from './rebind';
export { utilSetTransform } from './util';
export { utilSessionMutex } from './session_mutex';
export { utilStringQs } from './util';
// export { utilSuggestNames } from './suggest_names';
export { utilTagText } from './util';
export { utilTiler } from './tiler';
export { utilTriggerEvent } from './trigger_event';
+3 -2
View File
@@ -1,4 +1,5 @@
import _isFunction from 'lodash-es/isFunction';
import _uniq from 'lodash-es/uniq';
import {
event as d3_event,
@@ -125,7 +126,7 @@ export function utilKeybinding(namespace) {
// Remove one or more keycode bindings.
keybinding.off = function(codes, capture) {
var arr = [].concat(codes);
var arr = _uniq([].concat(codes));
for (var i = 0; i < arr.length; i++) {
var id = arr[i] + (capture ? '-capture' : '-bubble');
@@ -141,7 +142,7 @@ export function utilKeybinding(namespace) {
return keybinding.off(codes, capture);
}
var arr = [].concat(codes);
var arr = _uniq([].concat(codes));
for (var i = 0; i < arr.length; i++) {
var id = arr[i] + (capture ? '-capture' : '-bubble');
+3 -1
View File
@@ -42,7 +42,9 @@ export function t(s, o, loc) {
if (rep !== undefined) {
if (o) {
for (var k in o) {
rep = rep.replace('{' + k + '}', o[k]);
var variable = '{' + k + '}';
var re = new RegExp(variable, 'g'); // check globally for variables
rep = rep.replace(re, o[k]);
}
}
return rep;
+50 -32
View File
@@ -37,6 +37,7 @@ export function utilEntityOrMemberSelector(ids, graph) {
export function utilEntityOrDeepMemberSelector(ids, graph) {
var seen = {};
var allIDs = [];
function addEntityAndMembersIfNotYetSeen(id) {
// avoid infinite recursion for circular relations by skipping seen entities
if (seen[id]) return;
@@ -53,6 +54,7 @@ export function utilEntityOrDeepMemberSelector(ids, graph) {
}
}
}
ids.forEach(function(id) {
addEntityAndMembersIfNotYetSeen(id);
});
@@ -85,9 +87,9 @@ export function utilGetAllNodes(ids, graph) {
export function utilDisplayName(entity) {
var localizedNameKey = 'name:' + utilDetect().locale.toLowerCase().split('-')[0],
name = entity.tags[localizedNameKey] || entity.tags.name || '',
network = entity.tags.cycle_network || entity.tags.network;
var localizedNameKey = 'name:' + utilDetect().locale.toLowerCase().split('-')[0];
var name = entity.tags[localizedNameKey] || entity.tags.name || '';
var network = entity.tags.cycle_network || entity.tags.network;
if (!name && entity.tags.ref) {
name = entity.tags.ref;
@@ -137,6 +139,15 @@ export function utilDisplayLabel(entity, context) {
}
export function utilEntityRoot(entityType) {
return {
node: 'n',
way: 'w',
relation: 'r'
}[entityType];
}
export function utilStringQs(str) {
return str.split('&').reduce(function(obj, pair){
var parts = pair.split('=');
@@ -152,11 +163,12 @@ export function utilStringQs(str) {
export function utilQsString(obj, noencode) {
// encode everything except special characters used in certain hash parameters:
// "/" in map states, ":", ",", {" and "}" in background
function softEncode(s) {
// encode everything except special characters used in certain hash parameters:
// "/" in map states, ":", ",", {" and "}" in background
return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent);
}
return Object.keys(obj).sort().map(function(key) {
return encodeURIComponent(key) + '=' + (
noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key]));
@@ -165,36 +177,41 @@ export function utilQsString(obj, noencode) {
export function utilPrefixDOMProperty(property) {
var prefixes = ['webkit', 'ms', 'moz', 'o'],
i = -1,
n = prefixes.length,
s = document.body;
var prefixes = ['webkit', 'ms', 'moz', 'o'];
var i = -1;
var n = prefixes.length;
var s = document.body;
if (property in s)
return property;
property = property.substr(0, 1).toUpperCase() + property.substr(1);
while (++i < n)
if (prefixes[i] + property in s)
while (++i < n) {
if (prefixes[i] + property in s) {
return prefixes[i] + property;
}
}
return false;
}
export function utilPrefixCSSProperty(property) {
var prefixes = ['webkit', 'ms', 'Moz', 'O'],
i = -1,
n = prefixes.length,
s = document.body.style;
var prefixes = ['webkit', 'ms', 'Moz', 'O'];
var i = -1;
var n = prefixes.length;
var s = document.body.style;
if (property.toLowerCase() in s)
if (property.toLowerCase() in s) {
return property.toLowerCase();
}
while (++i < n)
if (prefixes[i] + property in s)
while (++i < n) {
if (prefixes[i] + property in s) {
return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase();
}
}
return false;
}
@@ -202,10 +219,9 @@ export function utilPrefixCSSProperty(property) {
var transformProperty;
export function utilSetTransform(el, x, y, scale) {
var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'),
translate = utilDetect().opera ?
'translate(' + x + 'px,' + y + 'px)' :
'translate3d(' + x + 'px,' + y + 'px,0)';
var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform');
var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)'
: 'translate3d(' + x + 'px,' + y + 'px,0)';
return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : ''));
}
@@ -240,11 +256,12 @@ export function utilEditDistance(a, b) {
// 1. Only works on HTML elements, not SVG
// 2. Does not cause style recalculation
export function utilFastMouse(container) {
var rect = container.getBoundingClientRect(),
rectLeft = rect.left,
rectTop = rect.top,
clientLeft = +container.clientLeft,
clientTop = +container.clientTop;
var rect = container.getBoundingClientRect();
var rectLeft = rect.left;
var rectTop = rect.top;
var clientLeft = +container.clientLeft;
var clientTop = +container.clientTop;
if (textDirection === 'rtl') {
rectLeft = 0;
}
@@ -262,9 +279,9 @@ export var utilGetPrototypeOf = Object.getPrototypeOf || function(obj) { return
export function utilAsyncMap(inputs, func, callback) {
var remaining = inputs.length,
results = [],
errors = [];
var remaining = inputs.length;
var results = [];
var errors = [];
inputs.forEach(function(d, i) {
func(d, function done(err, data) {
@@ -279,8 +296,9 @@ export function utilAsyncMap(inputs, func, callback) {
// wraps an index to an interval [0..length-1]
export function utilWrap(index, length) {
if (index < 0)
if (index < 0) {
index += Math.ceil(-index/length)*length;
}
return index % length;
}

Some files were not shown because too many files have changed in this diff Show More