From 92ca6a5a59f1d0a80499040de3c124a01c4229a1 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 9 Sep 2018 18:34:45 -0700 Subject: [PATCH 01/51] Adds navigation of the preset list using the arrow keys: Up and down arrows move focus up and down Right and left arrows open and close category items --- modules/ui/preset_list.js | 133 ++++++++++++++++++++++++++++++++++---- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 3360aeb8c..47dd22cbd 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -61,7 +61,7 @@ export function uiPresetList(context) { .call(svgIcon('#iD-icon-close')); } - function keydown() { + function initialKeydown() { // hack to let delete shortcut work when search is autofocused if (search.property('value').length === 0 && (d3_event.keyCode === d3_keybinding.keyCodes['⌫'] || @@ -69,6 +69,8 @@ export function uiPresetList(context) { d3_event.preventDefault(); d3_event.stopPropagation(); operationDelete([id], context)(); + + // hack to let undo work when search is autofocused } else if (search.property('value').length === 0 && (d3_event.ctrlKey || d3_event.metaKey) && d3_event.keyCode === d3_keybinding.keyCodes.z) { @@ -76,7 +78,21 @@ export function uiPresetList(context) { d3_event.stopPropagation(); context.undo(); } else if (!d3_event.ctrlKey && !d3_event.metaKey) { - d3_select(this).on('keydown', null); + // don't check for delete/undo hack on future keydown events + d3_select(this).on('keydown', keydown); + keydown.call(this); + } + } + + function keydown() { + // down arrow + if (d3_event.keyCode === d3_keybinding.keyCodes['↓'] && + // if insertion point is at the end of the string + search.node().selectionStart === search.property('value').length) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + // move focus to the first item in the preset list + list.select('.preset-list-button').node().focus(); } } @@ -114,7 +130,7 @@ export function uiPresetList(context) { .attr('placeholder', t('inspector.search')) .attr('type', 'search') .call(utilNoAuto) - .on('keydown', keydown) + .on('keydown', initialKeydown) .on('keypress', keypress) .on('input', inputevent); @@ -159,6 +175,73 @@ export function uiPresetList(context) { .style('opacity', 1); } + function itemKeydown(){ + // the actively focused item + var item = d3_select(this.closest('.preset-list-item')); + var parentItem = d3_select(item.node().parentElement.closest('.preset-list-item')); + + // arrow down, move focus to the next, lower item + if (d3_event.keyCode === d3_keybinding.keyCodes['↓']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + // the next item in the list at the same level + var nextItem = d3_select(item.node().nextElementSibling); + // if there is no next item in this list + if (nextItem.empty()) { + // if there is a parent item + if (!parentItem.empty()) { + // the item is the last item of a sublist, + // select the next item at the parent level + nextItem = d3_select(parentItem.node().nextElementSibling); + } + // if the focused item is expanded + } else if (d3_select(this).classed('expanded')) { + // select the first subitem instead + nextItem = item.select('.subgrid .preset-list-item:first-child'); + } + if (!nextItem.empty()) { + // focus on the next item + nextItem.select('.preset-list-button').node().focus(); + } + + // arrow up, move focus to the previous, higher item + } else if (d3_event.keyCode === d3_keybinding.keyCodes['↑']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + // the previous item in the list at the same level + var previousItem = d3_select(item.node().previousElementSibling); + + // if there is no previous item in this list + if (previousItem.empty()) { + // if there is a parent item + if (!parentItem.empty()) { + // the item is the first subitem of a sublist, + // select the parent item + previousItem = parentItem; + } + // if the previous item is expanded + } else if (previousItem.select('.preset-list-button').classed('expanded')) { + // select the last subitem of the sublist of the previous item + previousItem = previousItem.select('.subgrid .preset-list-item:last-child'); + } + if (!previousItem.empty()) { + // focus on the previous item + previousItem.select('.preset-list-button').node().focus(); + } + else { + // the focus is at the top of the list, move focus back to the search field + var search = d3_select(this.closest('.preset-list-pane')).select('.preset-search-input'); + search.node().focus(); + } + } + else if (d3_event.keyCode === d3_keybinding.keyCodes['→'] || + d3_event.keyCode === d3_keybinding.keyCodes['←']) { + // for consistency, don't propagate any arrow keys + d3_event.preventDefault(); + d3_event.stopPropagation(); + } + } + function CategoryItem(preset) { var box, sublist, shown = false; @@ -167,6 +250,17 @@ export function uiPresetList(context) { var wrap = selection.append('div') .attr('class', 'preset-list-button-wrap category col12'); + function click() { + var isExpanded = d3_select(this).classed('expanded'); + var iconName = isExpanded ? + (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down'; + d3_select(this) + .classed('expanded', !isExpanded); + d3_select(this).selectAll('div.label svg.icon use') + .attr('href', iconName); + item.choose(); + } + var button = wrap .append('button') .attr('class', 'preset-list-button') @@ -174,15 +268,29 @@ export function uiPresetList(context) { .call(uiPresetIcon() .geometry(context.geometry(id)) .preset(preset)) - .on('click', function() { - var isExpanded = d3_select(this).classed('expanded'); - var iconName = isExpanded ? - (textDirection === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down'; - d3_select(this) - .classed('expanded', !isExpanded); - d3_select(this).selectAll('div.label svg.icon use') - .attr('href', iconName); - item.choose(); + .on('click', click) + .on('keydown', function() { + // right arrow, expand the focused item + if (d3_event.keyCode === d3_keybinding.keyCodes['→']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + // if the item isn't expanded + if (!d3_select(this).classed('expanded')) { + // toggle expansion (expand the item) + click.call(this); + } + // left arrow, collapse the focused item + } else if (d3_event.keyCode === d3_keybinding.keyCodes['←']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + // if the item is expanded + if (d3_select(this).classed('expanded')) { + // toggle expansion (collapse the item) + click.call(this); + } + } else { + itemKeydown.call(this); + } }); var label = button @@ -245,6 +353,7 @@ export function uiPresetList(context) { .geometry(context.geometry(id)) .preset(preset)) .on('click', item.choose) + .on('keydown', itemKeydown) .append('div') .attr('class', 'label') .text(preset.name()); From 031b1d02fa3f8dc57b1153e8edeeb321eee46d70 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 29 Sep 2018 11:01:16 -0700 Subject: [PATCH 02/51] Right arrow keydown on focused preset list item now chooses the preset Left arrow keydown on focused preset list item now moves the focus to the category, if there is one --- modules/ui/preset_list.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 47dd22cbd..2d83c045e 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -234,11 +234,22 @@ export function uiPresetList(context) { search.node().focus(); } } - else if (d3_event.keyCode === d3_keybinding.keyCodes['→'] || - d3_event.keyCode === d3_keybinding.keyCodes['←']) { - // for consistency, don't propagate any arrow keys + // arrow left, move focus to the parent item if there is one + else if (d3_event.keyCode === d3_keybinding.keyCodes['←']) { d3_event.preventDefault(); d3_event.stopPropagation(); + + // if there is a parent item + if (!parentItem.empty()) { + // focus on the parent item + parentItem.select('.preset-list-button').node().focus(); + } + } + // arrow right, choose this item + else if (d3_event.keyCode === d3_keybinding.keyCodes['→']) { + d3_event.preventDefault(); + d3_event.stopPropagation(); + item.datum().choose(); } } From fc76e2c8c658b81a14fa996e99b910431276ee14 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 6 Oct 2018 09:41:39 -0700 Subject: [PATCH 03/51] Adds the Solar Panel preset Changes the Power Output field type from "text" to "typeCombo" Renames the source_nuclear and source_wind presets to source/nuclear and source/wind Adds point geometry to the power/tower preset --- data/presets.yaml | 9 +++- data/presets/fields.json | 2 +- .../fields/generator/output/electricity.json | 2 +- data/presets/presets.json | 7 +-- .../power/generator/method/photovoltaic.json | 43 +++++++++++++++++++ .../nuclear.json} | 0 .../{source_wind.json => source/wind.json} | 0 data/presets/presets/power/tower.json | 1 + data/taginfo.json | 7 +++ dist/locales/en.json | 8 +++- svg/fontawesome/fas-solar-panel.svg | 1 + 11 files changed, 71 insertions(+), 9 deletions(-) create mode 100644 data/presets/presets/power/generator/method/photovoltaic.json rename data/presets/presets/power/generator/{source_nuclear.json => source/nuclear.json} (100%) rename data/presets/presets/power/generator/{source_wind.json => source/wind.json} (100%) create mode 100644 svg/fontawesome/fas-solar-panel.svg diff --git a/data/presets.yaml b/data/presets.yaml index c975f7959..a22496d31 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -5027,12 +5027,17 @@ en: name: Power Generator # 'terms: hydro,solar,turbine,wind' terms: '' - power/generator/source_nuclear: + power/generator/method/photovoltaic: + # 'power=generator, generator:source=solar, generator:method=photovoltaic, generator:type=solar_photovoltaic_panel' + name: Solar Panel + # 'terms: photovoltaic module,PV module,sunlight' + terms: '' + power/generator/source/nuclear: # 'power=generator, generator:source=nuclear, generator:method=fission' name: Nuclear Reactor # 'terms: fission,generator,nuclear,nuke,reactor' terms: '' - power/generator/source_wind: + power/generator/source/wind: # 'power=generator, generator:source=wind, generator:method=wind_turbine' name: Wind Turbine # 'terms: generator,turbine,windmill,wind' diff --git a/data/presets/fields.json b/data/presets/fields.json index b0ec1b069..0addd4de0 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -113,7 +113,7 @@ "gauge": {"key": "gauge", "type": "combo", "label": "Gauge"}, "gender": {"type": "radio", "keys": ["male", "female", "unisex"], "label": "Gender", "placeholder": "Unknown", "strings": {"options": {"male": "Male", "female": "Female", "unisex": "Unisex"}}}, "generator/method": {"key": "generator:method", "type": "combo", "label": "Method"}, - "generator/output/electricity": {"key": "generator:output:electricity", "type": "text", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..."}, + "generator/output/electricity": {"key": "generator:output:electricity", "type": "typeCombo", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..."}, "generator/source": {"key": "generator:source", "type": "combo", "label": "Source"}, "generator/type": {"key": "generator:type", "type": "combo", "label": "Type"}, "government": {"key": "government", "type": "typeCombo", "label": "Type"}, diff --git a/data/presets/fields/generator/output/electricity.json b/data/presets/fields/generator/output/electricity.json index 50bc4cfd2..ae078a86e 100644 --- a/data/presets/fields/generator/output/electricity.json +++ b/data/presets/fields/generator/output/electricity.json @@ -1,6 +1,6 @@ { "key": "generator:output:electricity", - "type": "text", + "type": "typeCombo", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..." } diff --git a/data/presets/presets.json b/data/presets/presets.json index 4d1f7eb42..decbea42b 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -691,15 +691,16 @@ "point": {"fields": ["name"], "geometry": ["point"], "tags": {}, "name": "Point", "matchScore": 0.1}, "power/sub_station": {"icon": "temaki-power", "fields": ["substation", "operator", "building", "ref"], "geometry": ["point", "area"], "tags": {"power": "sub_station"}, "reference": {"key": "power", "value": "substation"}, "name": "Substation", "searchable": false}, "power/generator": {"icon": "temaki-power", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["hydro", "solar", "turbine", "wind"], "tags": {"power": "generator"}, "name": "Power Generator"}, - "power/generator/source_nuclear": {"icon": "temaki-radiation", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["fission", "generator", "nuclear", "nuke", "reactor"], "tags": {"power": "generator", "generator:source": "nuclear", "generator:method": "fission"}, "reference": {"key": "generator:source", "value": "nuclear"}, "name": "Nuclear Reactor"}, - "power/generator/source_wind": {"icon": "temaki-wind_turbine", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "height", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["generator", "turbine", "windmill", "wind"], "tags": {"power": "generator", "generator:source": "wind", "generator:method": "wind_turbine"}, "reference": {"key": "generator:source", "value": "wind"}, "name": "Wind Turbine"}, + "power/generator/method/photovoltaic": {"icon": "fas-solar-panel", "fields": ["operator", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["photovoltaic module", "PV module", "sunlight"], "tags": {"power": "generator", "generator:source": "solar", "generator:method": "photovoltaic", "generator:type": "solar_photovoltaic_panel"}, "addTags": {"power": "generator", "generator:source": "solar", "generator:method": "photovoltaic", "generator:type": "solar_photovoltaic_panel", "generator:output:electricity": "yes"}, "removeTags": {"power": "generator", "generator:source": "solar", "generator:method": "photovoltaic", "generator:type": "solar_photovoltaic_panel", "generator:output:electricity": "yes"}, "reference": {"key": "generator:method", "value": "photovoltaic"}, "name": "Solar Panel"}, + "power/generator/source/nuclear": {"icon": "temaki-radiation", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["fission", "generator", "nuclear", "nuke", "reactor"], "tags": {"power": "generator", "generator:source": "nuclear", "generator:method": "fission"}, "reference": {"key": "generator:source", "value": "nuclear"}, "name": "Nuclear Reactor"}, + "power/generator/source/wind": {"icon": "temaki-wind_turbine", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "height", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["generator", "turbine", "windmill", "wind"], "tags": {"power": "generator", "generator:source": "wind", "generator:method": "wind_turbine"}, "reference": {"key": "generator:source", "value": "wind"}, "name": "Wind Turbine"}, "power/line": {"icon": "iD-power-line", "fields": ["name", "operator", "voltage", "ref", "layer"], "geometry": ["line"], "tags": {"power": "line"}, "name": "Power Line"}, "power/minor_line": {"icon": "iD-power-line", "fields": ["name", "operator", "voltage", "ref", "layer"], "geometry": ["line"], "tags": {"power": "minor_line"}, "name": "Minor Power Line"}, "power/plant": {"icon": "maki-industry", "fields": ["name", "operator", "address", "plant/output/electricity", "start_date"], "geometry": ["area"], "tags": {"power": "plant"}, "addTags": {"power": "plant", "landuse": "industrial"}, "removeTags": {"power": "plant", "landuse": "industrial"}, "terms": ["coal", "gas", "generat*", "hydro", "nuclear", "power", "station"], "name": "Power Station Grounds"}, "power/pole": {"fields": ["ref"], "geometry": ["point", "vertex"], "tags": {"power": "pole"}, "name": "Power Pole"}, "power/substation": {"icon": "temaki-power", "fields": ["substation", "operator", "building", "ref"], "geometry": ["point", "area"], "tags": {"power": "substation"}, "name": "Substation"}, "power/switch": {"icon": "temaki-power", "fields": ["switch", "operator", "location", "cables", "voltage", "ref"], "geometry": ["point", "vertex", "area"], "tags": {"power": "switch"}, "name": "Power Switch"}, - "power/tower": {"fields": ["design", "ref"], "geometry": ["vertex"], "terms": ["power"], "tags": {"power": "tower"}, "name": "High-Voltage Tower"}, + "power/tower": {"fields": ["design", "ref"], "geometry": ["point", "vertex"], "terms": ["power"], "tags": {"power": "tower"}, "name": "High-Voltage Tower"}, "power/transformer": {"icon": "temaki-power", "fields": ["transformer", "operator", "location", "rating", "devices", "phases", "frequency", "voltage/primary", "voltage/secondary", "voltage/tertiary", "windings", "windings/configuration", "ref"], "geometry": ["point", "vertex", "area"], "tags": {"power": "transformer"}, "name": "Transformer"}, "public_transport/linear_platform_aerialway": {"icon": "iD-highway-footway", "fields": ["name", "ref_platform", "network", "operator", "surface", "lit", "bench", "shelter"], "geometry": ["line"], "tags": {"public_transport": "platform", "aerialway": "yes"}, "reference": {"key": "public_transport", "value": "platform"}, "terms": ["aerialway", "cable car", "platform", "public transit", "public transportation", "transit", "transportation"], "name": "Aerialway Stop / Platform"}, "public_transport/linear_platform_bus": {"icon": "iD-highway-footway", "fields": ["name", "ref_platform", "network", "operator", "surface", "lit", "bench", "shelter", "passenger_information_display"], "geometry": ["line"], "tags": {"public_transport": "platform", "bus": "yes"}, "addTags": {"public_transport": "platform", "bus": "yes", "highway": "bus_stop"}, "removeTags": {"public_transport": "platform", "bus": "yes", "highway": "bus_stop"}, "reference": {"key": "public_transport", "value": "platform"}, "terms": ["bus", "platform", "public transit", "public transportation", "transit", "transportation"], "name": "Bus Stop / Platform"}, diff --git a/data/presets/presets/power/generator/method/photovoltaic.json b/data/presets/presets/power/generator/method/photovoltaic.json new file mode 100644 index 000000000..eaecdb820 --- /dev/null +++ b/data/presets/presets/power/generator/method/photovoltaic.json @@ -0,0 +1,43 @@ +{ + "icon": "fas-solar-panel", + "fields": [ + "operator", + "generator/output/electricity", + "ref" + ], + "geometry": [ + "point", + "vertex", + "area" + ], + "terms": [ + "photovoltaic module", + "PV module", + "sunlight" + ], + "tags": { + "power": "generator", + "generator:source": "solar", + "generator:method": "photovoltaic", + "generator:type": "solar_photovoltaic_panel" + }, + "addTags": { + "power": "generator", + "generator:source": "solar", + "generator:method": "photovoltaic", + "generator:type": "solar_photovoltaic_panel", + "generator:output:electricity": "yes" + }, + "removeTags": { + "power": "generator", + "generator:source": "solar", + "generator:method": "photovoltaic", + "generator:type": "solar_photovoltaic_panel", + "generator:output:electricity": "yes" + }, + "reference": { + "key": "generator:method", + "value": "photovoltaic" + }, + "name": "Solar Panel" +} diff --git a/data/presets/presets/power/generator/source_nuclear.json b/data/presets/presets/power/generator/source/nuclear.json similarity index 100% rename from data/presets/presets/power/generator/source_nuclear.json rename to data/presets/presets/power/generator/source/nuclear.json diff --git a/data/presets/presets/power/generator/source_wind.json b/data/presets/presets/power/generator/source/wind.json similarity index 100% rename from data/presets/presets/power/generator/source_wind.json rename to data/presets/presets/power/generator/source/wind.json diff --git a/data/presets/presets/power/tower.json b/data/presets/presets/power/tower.json index 77c5277fe..eddacdbfd 100644 --- a/data/presets/presets/power/tower.json +++ b/data/presets/presets/power/tower.json @@ -4,6 +4,7 @@ "ref" ], "geometry": [ + "point", "vertex" ], "terms": [ diff --git a/data/taginfo.json b/data/taginfo.json index 244690dee..337b438d4 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -4627,6 +4627,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/power.svg?sanitize=true" }, + { + "key": "generator:type", + "value": "solar_photovoltaic_panel", + "description": "Solar Panel", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-solar-panel.svg?sanitize=true" + }, { "key": "generator:method", "value": "fission", diff --git a/dist/locales/en.json b/dist/locales/en.json index cac517781..34ef50c3a 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -5842,11 +5842,15 @@ "name": "Power Generator", "terms": "hydro,solar,turbine,wind" }, - "power/generator/source_nuclear": { + "power/generator/method/photovoltaic": { + "name": "Solar Panel", + "terms": "photovoltaic module,PV module,sunlight" + }, + "power/generator/source/nuclear": { "name": "Nuclear Reactor", "terms": "fission,generator,nuclear,nuke,reactor" }, - "power/generator/source_wind": { + "power/generator/source/wind": { "name": "Wind Turbine", "terms": "generator,turbine,windmill,wind" }, diff --git a/svg/fontawesome/fas-solar-panel.svg b/svg/fontawesome/fas-solar-panel.svg new file mode 100644 index 000000000..6c637615d --- /dev/null +++ b/svg/fontawesome/fas-solar-panel.svg @@ -0,0 +1 @@ + \ No newline at end of file From 805491caedd788b18f1bd1e44ea7ebbad7bbc558 Mon Sep 17 00:00:00 2001 From: yvecai Date: Sun, 7 Oct 2018 09:37:41 +0200 Subject: [PATCH 04/51] Fix nordic type field --- data/presets/fields/piste/type_nordic.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/presets/fields/piste/type_nordic.json b/data/presets/fields/piste/type_nordic.json index 2065421f4..dba168b2f 100644 --- a/data/presets/fields/piste/type_nordic.json +++ b/data/presets/fields/piste/type_nordic.json @@ -4,7 +4,7 @@ "label": "Type", "strings": { "options": { - "downhill": "Nordic", + "nordic": "Nordic", "playground": "Playground", "connection": "Connection between pistes" } From bfc3bc3c3c5023176492a7099bdd4980e3f037e8 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 09:01:01 -0700 Subject: [PATCH 05/51] Changes the type of the generator/output/electricity field back to `text` --- data/presets/fields.json | 2 +- data/presets/fields/generator/output/electricity.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/presets/fields.json b/data/presets/fields.json index 0addd4de0..b0ec1b069 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -113,7 +113,7 @@ "gauge": {"key": "gauge", "type": "combo", "label": "Gauge"}, "gender": {"type": "radio", "keys": ["male", "female", "unisex"], "label": "Gender", "placeholder": "Unknown", "strings": {"options": {"male": "Male", "female": "Female", "unisex": "Unisex"}}}, "generator/method": {"key": "generator:method", "type": "combo", "label": "Method"}, - "generator/output/electricity": {"key": "generator:output:electricity", "type": "typeCombo", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..."}, + "generator/output/electricity": {"key": "generator:output:electricity", "type": "text", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..."}, "generator/source": {"key": "generator:source", "type": "combo", "label": "Source"}, "generator/type": {"key": "generator:type", "type": "combo", "label": "Type"}, "government": {"key": "government", "type": "typeCombo", "label": "Type"}, diff --git a/data/presets/fields/generator/output/electricity.json b/data/presets/fields/generator/output/electricity.json index ae078a86e..50bc4cfd2 100644 --- a/data/presets/fields/generator/output/electricity.json +++ b/data/presets/fields/generator/output/electricity.json @@ -1,6 +1,6 @@ { "key": "generator:output:electricity", - "type": "typeCombo", + "type": "text", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..." } From 6fca79fc23819d88cc8c6275e297cdeebd064403 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 11:02:30 -0700 Subject: [PATCH 06/51] Adds the snowflake icon referenced by some piste presets added in #5368 --- svg/fontawesome/fas-snowflake.svg | 1 + 1 file changed, 1 insertion(+) create mode 100644 svg/fontawesome/fas-snowflake.svg diff --git a/svg/fontawesome/fas-snowflake.svg b/svg/fontawesome/fas-snowflake.svg new file mode 100644 index 000000000..a162d4179 --- /dev/null +++ b/svg/fontawesome/fas-snowflake.svg @@ -0,0 +1 @@ + \ No newline at end of file From 72842ff0f5c101f4cf5ba1eda11b27b397969f07 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 11:05:31 -0700 Subject: [PATCH 07/51] Adds the Underground Power Cable Preset (closes #5380) Adds the Utility Features preset category for line geometry Adds terms to the Pipeline preset --- data/presets.yaml | 7 +++++++ data/presets/categories.json | 11 +++++++++++ data/presets/categories/utility.json | 11 +++++++++++ data/presets/defaults.json | 2 +- data/presets/presets.json | 3 ++- data/presets/presets/man_made/pipeline.json | 7 +++++++ .../presets/power/cable/underground.json | 17 +++++++++++++++++ data/taginfo.json | 6 ++++++ dist/locales/en.json | 9 ++++++++- svg/iD-sprite/presets/category-utility.svg | 8 ++++++++ 10 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 data/presets/categories/utility.json create mode 100644 data/presets/presets/power/cable/underground.json create mode 100644 svg/iD-sprite/presets/category-utility.svg diff --git a/data/presets.yaml b/data/presets.yaml index f04b94426..17cce17aa 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -25,6 +25,8 @@ en: name: Road Features category-route: name: Route Features + category-utility: + name: Utility Features category-water-area: name: Water Features category-water-line: @@ -4603,6 +4605,7 @@ en: man_made/pipeline: # man_made=pipeline name: Pipeline + # 'terms: oil,natural gas,water,sewer,sewage' terms: '' man_made/pumping_station: # man_made=pumping_station @@ -5170,6 +5173,10 @@ en: power: # power=* name: Power + power/cable/underground: + # 'power=cable, location=underground' + name: Underground Power Cable + terms: '' power/generator: # power=generator name: Power Generator diff --git a/data/presets/categories.json b/data/presets/categories.json index af19ba230..60309de77 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -186,6 +186,17 @@ "type/route" ] }, + "category-utility": { + "icon": "iD-category-utility", + "geometry": "line", + "name": "Utility Features", + "members": [ + "power/line", + "power/minor_line", + "man_made/pipeline", + "power/cable/underground" + ] + }, "category-water-area": { "icon": "maki-water", "geometry": "area", diff --git a/data/presets/categories/utility.json b/data/presets/categories/utility.json new file mode 100644 index 000000000..63aae92d0 --- /dev/null +++ b/data/presets/categories/utility.json @@ -0,0 +1,11 @@ +{ + "icon": "iD-category-utility", + "geometry": "line", + "name": "Utility Features", + "members": [ + "power/line", + "power/minor_line", + "man_made/pipeline", + "power/cable/underground" + ] +} diff --git a/data/presets/defaults.json b/data/presets/defaults.json index b0c9e52ef..a6db43d57 100644 --- a/data/presets/defaults.json +++ b/data/presets/defaults.json @@ -19,7 +19,7 @@ "category-water-line", "category-barrier", "category-natural-line", - "power/line", + "category-utility", "line" ], "point": [ diff --git a/data/presets/presets.json b/data/presets/presets.json index 814adcd7b..6b499878d 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -571,7 +571,7 @@ "man_made/observatory": {"geometry": ["point", "area"], "terms": ["astronomical", "meteorological"], "tags": {"man_made": "observatory"}, "name": "Observatory"}, "man_made/petroleum_well": {"icon": "temaki-storage_tank", "geometry": ["point"], "terms": ["drilling rig", "oil derrick", "oil drill", "oil horse", "oil rig", "oil pump", "petroleum well", "pumpjack"], "tags": {"man_made": "petroleum_well"}, "name": "Oil Well"}, "man_made/pier": {"icon": "iD-highway-footway", "fields": ["name", "surface", "lit", "width", "access"], "geometry": ["line", "area"], "terms": ["dock", "jetty"], "tags": {"man_made": "pier"}, "name": "Pier"}, - "man_made/pipeline": {"icon": "iD-pipeline-line", "fields": ["location", "operator", "substance", "layer"], "geometry": ["line"], "tags": {"man_made": "pipeline"}, "name": "Pipeline"}, + "man_made/pipeline": {"icon": "iD-pipeline-line", "fields": ["location", "operator", "substance", "layer"], "geometry": ["line"], "terms": ["oil", "natural gas", "water", "sewer", "sewage"], "tags": {"man_made": "pipeline"}, "name": "Pipeline"}, "man_made/pumping_station": {"icon": "maki-water", "geometry": ["point", "area"], "tags": {"man_made": "pumping_station"}, "name": "Pumping Station"}, "man_made/silo": {"icon": "temaki-silo", "fields": ["crop", "building_area"], "geometry": ["point", "area"], "terms": ["grain", "corn", "wheat"], "tags": {"man_made": "silo"}, "name": "Silo"}, "man_made/storage_tank": {"icon": "temaki-storage_tank", "fields": ["content", "building_area"], "geometry": ["point", "area"], "terms": ["water", "oil", "gas", "petrol"], "tags": {"man_made": "storage_tank"}, "name": "Storage Tank"}, @@ -697,6 +697,7 @@ "playground/zipwire": {"icon": "maki-playground", "geometry": ["point", "line"], "tags": {"playground": "zipwire"}, "name": "Zip Wire"}, "point": {"fields": ["name"], "geometry": ["point"], "tags": {}, "name": "Point", "matchScore": 0.1}, "power/sub_station": {"icon": "temaki-power", "fields": ["substation", "operator", "building", "ref"], "geometry": ["point", "area"], "tags": {"power": "sub_station"}, "reference": {"key": "power", "value": "substation"}, "name": "Substation", "searchable": false}, + "power/cable/underground": {"fields": ["name", "operator", "voltage", "ref", "layer"], "geometry": ["line"], "tags": {"power": "cable", "location": "underground"}, "name": "Underground Power Cable"}, "power/generator": {"icon": "temaki-power", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["hydro", "solar", "turbine", "wind"], "tags": {"power": "generator"}, "name": "Power Generator"}, "power/generator/source_nuclear": {"icon": "temaki-radiation", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["fission", "generator", "nuclear", "nuke", "reactor"], "tags": {"power": "generator", "generator:source": "nuclear", "generator:method": "fission"}, "reference": {"key": "generator:source", "value": "nuclear"}, "name": "Nuclear Reactor"}, "power/generator/source_wind": {"icon": "temaki-wind_turbine", "fields": ["operator", "generator/source", "generator/method", "generator/type", "generator/output/electricity", "height", "ref"], "geometry": ["point", "vertex", "area"], "terms": ["generator", "turbine", "windmill", "wind"], "tags": {"power": "generator", "generator:source": "wind", "generator:method": "wind_turbine"}, "reference": {"key": "generator:source", "value": "wind"}, "name": "Wind Turbine"}, diff --git a/data/presets/presets/man_made/pipeline.json b/data/presets/presets/man_made/pipeline.json index 192855517..d198ffc10 100644 --- a/data/presets/presets/man_made/pipeline.json +++ b/data/presets/presets/man_made/pipeline.json @@ -9,6 +9,13 @@ "geometry": [ "line" ], + "terms": [ + "oil", + "natural gas", + "water", + "sewer", + "sewage" + ], "tags": { "man_made": "pipeline" }, diff --git a/data/presets/presets/power/cable/underground.json b/data/presets/presets/power/cable/underground.json new file mode 100644 index 000000000..0d80be672 --- /dev/null +++ b/data/presets/presets/power/cable/underground.json @@ -0,0 +1,17 @@ +{ + "fields": [ + "name", + "operator", + "voltage", + "ref", + "layer" + ], + "geometry": [ + "line" + ], + "tags": { + "power": "cable", + "location": "underground" + }, + "name": "Underground Power Cable" +} diff --git a/data/taginfo.json b/data/taginfo.json index 299bcf6ee..fbd88f0eb 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -4669,6 +4669,12 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/bhousel/temaki/master/icons/power.svg?sanitize=true" }, + { + "key": "location", + "value": "underground", + "description": "Underground Power Cable", + "object_types": ["way"] + }, { "key": "power", "value": "generator", diff --git a/dist/locales/en.json b/dist/locales/en.json index 431da2fe2..d8cb61df6 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1455,6 +1455,9 @@ "category-route": { "name": "Route Features" }, + "category-utility": { + "name": "Utility Features" + }, "category-water-area": { "name": "Water Features" }, @@ -5446,7 +5449,7 @@ }, "man_made/pipeline": { "name": "Pipeline", - "terms": "" + "terms": "oil,natural gas,water,sewer,sewage" }, "man_made/pumping_station": { "name": "Pumping Station", @@ -5948,6 +5951,10 @@ "name": "Substation", "terms": "" }, + "power/cable/underground": { + "name": "Underground Power Cable", + "terms": "" + }, "power/generator": { "name": "Power Generator", "terms": "hydro,solar,turbine,wind" diff --git a/svg/iD-sprite/presets/category-utility.svg b/svg/iD-sprite/presets/category-utility.svg new file mode 100644 index 000000000..e819114fc --- /dev/null +++ b/svg/iD-sprite/presets/category-utility.svg @@ -0,0 +1,8 @@ + + + + + + + + From b7555b4f0b29af2bb12b6af98ca480d370820ac2 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 11:09:42 -0700 Subject: [PATCH 08/51] Adds the generated changes for #5376 --- data/presets.yaml | 4 ++-- data/presets/fields.json | 2 +- dist/locales/en.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index f04b94426..b292d1c24 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1317,8 +1317,8 @@ en: options: # 'piste:type=connection' connection: Connection between pistes - # 'piste:type=downhill' - downhill: Nordic + # 'piste:type=nordic' + nordic: Nordic # 'piste:type=playground' playground: Playground place: diff --git a/data/presets/fields.json b/data/presets/fields.json index 801eb4d74..1d8fc8bf7 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -211,7 +211,7 @@ "piste/grooming_nordic": {"key": "piste:grooming", "type": "combo", "label": "Grooming", "strings": {"options": {"classic": "Classic", "backcountry": "Backcountry, no grooming", "classic+skating": "Classic and Skating", "scooter": "Scooter/Snowmobile", "skating": "Skating"}}}, "piste/grooming": {"key": "piste:grooming", "type": "combo", "label": "Grooming", "strings": {"options": {"classic": "Classic", "mogul": "Mogul", "backcountry": "Backcountry", "classic+skating": "Classic and Skating", "scooter": "Scooter/Snowmobile", "skating": "Skating"}}}, "piste/type_downhill": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"downhill": "Downhill", "snow_park": "Snow Park", "playground": "Playground", "connection": "Connection between pistes"}}}, - "piste/type_nordic": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"downhill": "Nordic", "playground": "Playground", "connection": "Connection between pistes"}}}, + "piste/type_nordic": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"nordic": "Nordic", "playground": "Playground", "connection": "Connection between pistes"}}}, "piste/type": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"downhill": "Downhill", "nordic": "Nordic", "skitour": "Skitour", "sled": "Sled", "hike": "Hike", "sleigh": "Sleigh", "ice_skate": "Ice Skate", "snow_park": "Snow Park", "playground": "Playground", "connection": "Connection"}}}, "place": {"key": "place", "type": "typeCombo", "label": "Type"}, "plant": {"key": "plant", "type": "combo", "label": "Plant"}, diff --git a/dist/locales/en.json b/dist/locales/en.json index 431da2fe2..0d4eb105b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2528,7 +2528,7 @@ "piste/type_nordic": { "label": "Type", "options": { - "downhill": "Nordic", + "nordic": "Nordic", "playground": "Playground", "connection": "Connection between pistes" } From fa15acc4bb16ad2a9cd9fadb035ecdf379dc2590 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 7 Oct 2018 14:15:40 -0400 Subject: [PATCH 09/51] Use 'en-us' "Color" --- data/presets.yaml | 2 +- data/presets/fields.json | 2 +- data/presets/fields/colour.json | 2 +- data/taginfo.json | 2 +- dist/locales/en.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index b292d1c24..f81d2a5cf 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -323,7 +323,7 @@ en: label: Collection Times colour: # colour=* - label: Colour + label: Color comment: # comment=* label: Changeset Comment diff --git a/data/presets/fields.json b/data/presets/fields.json index 1d8fc8bf7..4212d95e1 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -53,7 +53,7 @@ "clothes": {"key": "clothes", "type": "semiCombo", "label": "Clothes"}, "club": {"key": "club", "type": "typeCombo", "label": "Type"}, "collection_times": {"key": "collection_times", "type": "text", "label": "Collection Times"}, - "colour": {"key": "colour", "type": "text", "label": "Colour"}, + "colour": {"key": "colour", "type": "text", "label": "Color"}, "comment": {"key": "comment", "type": "textarea", "label": "Changeset Comment", "placeholder": "Brief description of your contributions (required)"}, "communication_multi": {"key": "communication:", "type": "multiCombo", "label": "Communication Types"}, "construction": {"key": "construction", "type": "combo", "label": "Type"}, diff --git a/data/presets/fields/colour.json b/data/presets/fields/colour.json index 6ab9de9af..d53e2a950 100644 --- a/data/presets/fields/colour.json +++ b/data/presets/fields/colour.json @@ -1,5 +1,5 @@ { "key": "colour", "type": "text", - "label": "Colour" + "label": "Color" } diff --git a/data/taginfo.json b/data/taginfo.json index 299bcf6ee..4af7e5ca2 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -6689,7 +6689,7 @@ {"key": "castle_type", "description": "Type"}, {"key": "clothes", "description": "Clothes"}, {"key": "collection_times", "description": "Collection Times"}, - {"key": "colour", "description": "Colour"}, + {"key": "colour", "description": "Color"}, {"key": "comment", "description": "Changeset Comment"}, {"key": "communication:", "description": "Communication Types"}, {"key": "construction", "description": "Type"}, diff --git a/dist/locales/en.json b/dist/locales/en.json index 0d4eb105b..19ffe1642 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1731,7 +1731,7 @@ "label": "Collection Times" }, "colour": { - "label": "Colour" + "label": "Color" }, "comment": { "label": "Changeset Comment", From 62d16d8b5a8df334784b7ed14a7a24888a3ec18a Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 12:26:28 -0700 Subject: [PATCH 10/51] Corrects the names of the curb presets and seamark color fields to use US English spellings --- data/presets.yaml | 12 ++++----- data/presets/fields.json | 4 +-- .../fields/seamark/beacon_lateral/colour.json | 2 +- .../fields/seamark/buoy_lateral/colour.json | 2 +- data/presets/presets.json | 4 +-- data/presets/presets/barrier/kerb.json | 2 +- .../presets/presets/barrier/kerb/lowered.json | 2 +- data/taginfo.json | 26 +++++++++---------- dist/locales/en.json | 8 +++--- 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index f81d2a5cf..cf7299c1e 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1491,7 +1491,7 @@ en: waterway_right: Waterway Right seamark/beacon_lateral/colour: # 'seamark:beacon_lateral:colour=*' - label: Colour + label: Color options: # 'seamark:beacon_lateral:colour=green' green: Green @@ -1540,7 +1540,7 @@ en: waterway_right: Waterway Right seamark/buoy_lateral/colour: # 'seamark:buoy_lateral:colour=*' - label: Colour + label: Color options: # 'seamark:buoy_lateral:colour=green' green: Green @@ -2961,13 +2961,13 @@ en: terms: '' barrier/kerb: # barrier=kerb - name: Kerb - terms: '' + name: Curb + terms: '' barrier/kerb/lowered: # 'barrier=kerb, kerb=lowered' - name: Lowered Kerb + name: Lowered Curb # 'terms: curb cut,curb ramp,kerb ramp,dropped kerb,pram ramp' - terms: '' + terms: '' barrier/kissing_gate: # barrier=kissing_gate name: Kissing Gate diff --git a/data/presets/fields.json b/data/presets/fields.json index 4212d95e1..fc1b11195 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -252,11 +252,11 @@ "sanitary_dump_station": {"key": "sanitary_dump_station", "type": "check", "label": "Toilet Disposal"}, "seamark/beacon_isolated_danger/shape": {"key": "seamark:beacon_isolated_danger:shape", "type": "combo", "label": "Shape"}, "seamark/beacon_lateral/category": {"key": "seamark:beacon_lateral:category", "type": "combo", "label": "Category", "strings": {"options": {"port": "Port", "starboard": "Starboard", "waterway_left": "Waterway Left", "waterway_right": "Waterway Right", "danger_left": "Danger Left", "danger_right": "Danger Right"}}}, - "seamark/beacon_lateral/colour": {"key": "seamark:beacon_lateral:colour", "type": "combo", "label": "Colour", "strings": {"options": {"red": "Red", "green": "Green", "grey": "Grey"}}}, + "seamark/beacon_lateral/colour": {"key": "seamark:beacon_lateral:colour", "type": "combo", "label": "Color", "strings": {"options": {"red": "Red", "green": "Green", "grey": "Grey"}}}, "seamark/beacon_lateral/shape": {"key": "seamark:beacon_lateral:shape", "type": "combo", "label": "Shape"}, "seamark/beacon_lateral/system": {"key": "seamark:beacon_lateral:system", "type": "combo", "label": "System", "strings": {"options": {"iala-a": "IALA A", "iala-b": "IALA B", "cevni": "CEVNI", "other": "Other"}}}, "seamark/buoy_lateral/category": {"key": "seamark:buoy_lateral:category", "type": "combo", "label": "Category", "strings": {"options": {"port": "Port", "starboard": "Starboard", "channel_left": "Channel Left", "channel_right": "Channel Right", "waterway_left": "Waterway Left", "waterway_right": "Waterway Right", "danger_left": "Danger Left", "danger_right": "Danger Right", "preferred_channel_port": "Preferred Channel Port", "preferred_channel_starboard": "Preferred Channel Starboard"}}}, - "seamark/buoy_lateral/colour": {"key": "seamark:buoy_lateral:colour", "type": "combo", "label": "Colour", "strings": {"options": {"red": "Red", "green": "Green", "red;white;red;white": "Red-White-Red-White", "green;white;green;white": "Green-White-Green-White", "red;green;red": "Red-Green-Red", "green;red;green": "Green-Red-Green", "white": "White", "yellow": "Yellow"}}}, + "seamark/buoy_lateral/colour": {"key": "seamark:buoy_lateral:colour", "type": "combo", "label": "Color", "strings": {"options": {"red": "Red", "green": "Green", "red;white;red;white": "Red-White-Red-White", "green;white;green;white": "Green-White-Green-White", "red;green;red": "Red-Green-Red", "green;red;green": "Green-Red-Green", "white": "White", "yellow": "Yellow"}}}, "seamark/buoy_lateral/shape": {"key": "seamark:buoy_lateral:shape", "type": "combo", "label": "Shape"}, "seamark/buoy_lateral/system": {"key": "seamark:buoy_lateral:system", "type": "combo", "label": "System", "strings": {"options": {"iala-a": "IALA A", "iala-b": "IALA B", "cevni": "CEVNI", "other": "Other"}}}, "seamark/mooring/category": {"key": "seamark:mooring:category", "type": "combo", "label": "Category"}, diff --git a/data/presets/fields/seamark/beacon_lateral/colour.json b/data/presets/fields/seamark/beacon_lateral/colour.json index 2af53b30e..94c35483c 100644 --- a/data/presets/fields/seamark/beacon_lateral/colour.json +++ b/data/presets/fields/seamark/beacon_lateral/colour.json @@ -1,7 +1,7 @@ { "key": "seamark:beacon_lateral:colour", "type": "combo", - "label": "Colour", + "label": "Color", "strings": { "options": { "red": "Red", diff --git a/data/presets/fields/seamark/buoy_lateral/colour.json b/data/presets/fields/seamark/buoy_lateral/colour.json index abe96b049..e95c4363e 100644 --- a/data/presets/fields/seamark/buoy_lateral/colour.json +++ b/data/presets/fields/seamark/buoy_lateral/colour.json @@ -1,7 +1,7 @@ { "key": "seamark:buoy_lateral:colour", "type": "combo", - "label": "Colour", + "label": "Color", "strings": { "options": { "red": "Red", diff --git a/data/presets/presets.json b/data/presets/presets.json index 814adcd7b..e8d5feadb 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -205,8 +205,8 @@ "barrier/fence": {"icon": "maki-fence", "fields": ["fence_type", "height"], "geometry": ["line"], "tags": {"barrier": "fence"}, "name": "Fence", "matchScore": 0.25}, "barrier/gate": {"icon": "maki-barrier", "fields": ["access"], "geometry": ["vertex", "line"], "tags": {"barrier": "gate"}, "name": "Gate"}, "barrier/hedge": {"fields": ["height"], "geometry": ["line", "area"], "tags": {"barrier": "hedge"}, "name": "Hedge", "matchScore": 0.25}, - "barrier/kerb": {"icon": "maki-wheelchair", "fields": ["kerb", "tactile_paving"], "geometry": ["vertex", "line"], "tags": {"barrier": "kerb"}, "name": "Kerb"}, - "barrier/kerb/lowered": {"icon": "maki-wheelchair", "fields": ["kerb", "tactile_paving"], "geometry": ["vertex", "line"], "tags": {"barrier": "kerb", "kerb": "lowered"}, "reference": {"key": "kerb", "value": "lowered"}, "terms": ["curb cut", "curb ramp", "kerb ramp", "dropped kerb", "pram ramp"], "name": "Lowered Kerb"}, + "barrier/kerb": {"icon": "maki-wheelchair", "fields": ["kerb", "tactile_paving"], "geometry": ["vertex", "line"], "tags": {"barrier": "kerb"}, "name": "Curb"}, + "barrier/kerb/lowered": {"icon": "maki-wheelchair", "fields": ["kerb", "tactile_paving"], "geometry": ["vertex", "line"], "tags": {"barrier": "kerb", "kerb": "lowered"}, "reference": {"key": "kerb", "value": "lowered"}, "terms": ["curb cut", "curb ramp", "kerb ramp", "dropped kerb", "pram ramp"], "name": "Lowered Curb"}, "barrier/kissing_gate": {"icon": "maki-barrier", "fields": ["access"], "geometry": ["vertex"], "tags": {"barrier": "kissing_gate"}, "name": "Kissing Gate"}, "barrier/lift_gate": {"icon": "maki-roadblock", "fields": ["access"], "geometry": ["vertex", "line"], "tags": {"barrier": "lift_gate"}, "name": "Lift Gate"}, "barrier/retaining_wall": {"fields": ["height"], "geometry": ["line", "area"], "tags": {"barrier": "retaining_wall"}, "name": "Retaining Wall"}, diff --git a/data/presets/presets/barrier/kerb.json b/data/presets/presets/barrier/kerb.json index aff132c00..21fdd6985 100644 --- a/data/presets/presets/barrier/kerb.json +++ b/data/presets/presets/barrier/kerb.json @@ -11,5 +11,5 @@ "tags": { "barrier": "kerb" }, - "name": "Kerb" + "name": "Curb" } diff --git a/data/presets/presets/barrier/kerb/lowered.json b/data/presets/presets/barrier/kerb/lowered.json index 98a3b9a56..9054ddc4d 100644 --- a/data/presets/presets/barrier/kerb/lowered.json +++ b/data/presets/presets/barrier/kerb/lowered.json @@ -23,5 +23,5 @@ "dropped kerb", "pram ramp" ], - "name": "Lowered Kerb" + "name": "Lowered Curb" } diff --git a/data/taginfo.json b/data/taginfo.json index 4af7e5ca2..ecf5e471c 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -1398,14 +1398,14 @@ { "key": "barrier", "value": "kerb", - "description": "Kerb", + "description": "Curb", "object_types": ["node", "way"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/wheelchair-15.svg?sanitize=true" }, { "key": "kerb", "value": "lowered", - "description": "Lowered Kerb", + "description": "Lowered Curb", "object_types": ["node", "way"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/wheelchair-15.svg?sanitize=true" }, @@ -5024,13 +5024,13 @@ { "key": "seamark:buoy_lateral:colour", "value": "green", - "description": "Green Buoy, Colour", + "description": "Green Buoy, Color", "object_types": ["node"] }, { "key": "seamark:buoy_lateral:colour", "value": "red", - "description": "Red Buoy, Colour", + "description": "Red Buoy, Color", "object_types": ["node"] }, { @@ -7244,17 +7244,17 @@ { "key": "seamark:beacon_lateral:colour", "value": "red", - "description": "Colour" + "description": "Color" }, { "key": "seamark:beacon_lateral:colour", "value": "green", - "description": "Colour" + "description": "Color" }, { "key": "seamark:beacon_lateral:colour", "value": "grey", - "description": "Colour" + "description": "Color" }, {"key": "seamark:beacon_lateral:shape", "description": "Shape"}, { @@ -7330,32 +7330,32 @@ { "key": "seamark:buoy_lateral:colour", "value": "red;white;red;white", - "description": "Colour" + "description": "Color" }, { "key": "seamark:buoy_lateral:colour", "value": "green;white;green;white", - "description": "Colour" + "description": "Color" }, { "key": "seamark:buoy_lateral:colour", "value": "red;green;red", - "description": "Colour" + "description": "Color" }, { "key": "seamark:buoy_lateral:colour", "value": "green;red;green", - "description": "Colour" + "description": "Color" }, { "key": "seamark:buoy_lateral:colour", "value": "white", - "description": "Colour" + "description": "Color" }, { "key": "seamark:buoy_lateral:colour", "value": "yellow", - "description": "Colour" + "description": "Color" }, {"key": "seamark:buoy_lateral:shape", "description": "Shape"}, { diff --git a/dist/locales/en.json b/dist/locales/en.json index 19ffe1642..61869528c 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2698,7 +2698,7 @@ } }, "seamark/beacon_lateral/colour": { - "label": "Colour", + "label": "Color", "options": { "red": "Red", "green": "Green", @@ -2733,7 +2733,7 @@ } }, "seamark/buoy_lateral/colour": { - "label": "Colour", + "label": "Color", "options": { "red": "Red", "green": "Green", @@ -3981,11 +3981,11 @@ "terms": "" }, "barrier/kerb": { - "name": "Kerb", + "name": "Curb", "terms": "" }, "barrier/kerb/lowered": { - "name": "Lowered Kerb", + "name": "Lowered Curb", "terms": "curb cut,curb ramp,kerb ramp,dropped kerb,pram ramp" }, "barrier/kissing_gate": { From 159622f57200d40ef73b13b3bca9375c06e482ec Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 7 Oct 2018 16:05:04 -0400 Subject: [PATCH 11/51] Don't lookup `postal_code` values from taginfo --- modules/services/taginfo.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/services/taginfo.js b/modules/services/taginfo.js index cc8df7041..6242be845 100644 --- a/modules/services/taginfo.js +++ b/modules/services/taginfo.js @@ -182,7 +182,9 @@ export default { init: function() { inflight = {}; taginfoCache = {}; - popularKeys = {}; + popularKeys = { + postal_code: true // #5377 + }; // Fetch popular keys. We'll exclude these from `values` // lookups because they stress taginfo, and they aren't likely From 68323f818e1f38a57fd07bcd995f8bb3c47c65fa Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 13:27:14 -0700 Subject: [PATCH 12/51] Adds documentation for the snake_case field property (closes #5379) Adds documentation for the caseSensitive, min_value, and max_value field properties --- data/presets/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/data/presets/README.md b/data/presets/README.md index 24df5b0e8..b76dac1a5 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -210,6 +210,21 @@ the user can not type their own value, they must choose one of the given values. If a combo field does not specify `options` or `strings`, the field will fetch common tag values from the Taginfo service to use as dropdown values. +##### `snake_case` + +For combo fields, spaces are replaced with underscores in the tag value if `snake_case` is `true`. The default is `true`. + +##### `caseSensitive` + +For combo fields, case-sensitve field values are allowed if `caseSensitive` is `true`. The default is `false`. + +##### `min_value` + +For number fields, the lowest valid value. There is no default. + +##### `max_value` + +For number fields, the greatest valid value. There is no defualt. ## Icons From 6fad8a5d40fe31815b259ea767503bba79afe7a2 Mon Sep 17 00:00:00 2001 From: James Kingdom Date: Sun, 7 Oct 2018 21:30:51 +0100 Subject: [PATCH 13/51] Fix spelling --- data/presets/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/presets/README.md b/data/presets/README.md index b76dac1a5..d9b4fa0ac 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -224,7 +224,7 @@ For number fields, the lowest valid value. There is no default. ##### `max_value` -For number fields, the greatest valid value. There is no defualt. +For number fields, the greatest valid value. There is no default. ## Icons From 73ede123c28a58e850161c3a574f5aaf46a1198a Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 7 Oct 2018 13:31:15 -0700 Subject: [PATCH 14/51] Converts the generator/output/electricity field from text to typeCombo --- data/presets/fields.json | 2 +- data/presets/fields/generator/output/electricity.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/presets/fields.json b/data/presets/fields.json index fc1b11195..e15156d4d 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -114,7 +114,7 @@ "gauge": {"key": "gauge", "type": "combo", "label": "Gauge"}, "gender": {"type": "radio", "keys": ["male", "female", "unisex"], "label": "Gender", "placeholder": "Unknown", "strings": {"options": {"male": "Male", "female": "Female", "unisex": "Unisex"}}}, "generator/method": {"key": "generator:method", "type": "combo", "label": "Method"}, - "generator/output/electricity": {"key": "generator:output:electricity", "type": "text", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW..."}, + "generator/output/electricity": {"key": "generator:output:electricity", "type": "typeCombo", "label": "Power Output", "placeholder": "50 MW, 100 MW, 200 MW...", "snake_case": false}, "generator/source": {"key": "generator:source", "type": "combo", "label": "Source"}, "generator/type": {"key": "generator:type", "type": "combo", "label": "Type"}, "government": {"key": "government", "type": "typeCombo", "label": "Type"}, diff --git a/data/presets/fields/generator/output/electricity.json b/data/presets/fields/generator/output/electricity.json index 50bc4cfd2..01efca521 100644 --- a/data/presets/fields/generator/output/electricity.json +++ b/data/presets/fields/generator/output/electricity.json @@ -1,6 +1,7 @@ { "key": "generator:output:electricity", - "type": "text", + "type": "typeCombo", "label": "Power Output", - "placeholder": "50 MW, 100 MW, 200 MW..." + "placeholder": "50 MW, 100 MW, 200 MW...", + "snake_case": false } From a916402c7f464bb6bc6167a38c03ebf015a61d82 Mon Sep 17 00:00:00 2001 From: J Guthrie Date: Mon, 8 Oct 2018 15:18:40 +0100 Subject: [PATCH 15/51] Changed styling of geocode-worldwide-search button --- css/80_app.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index 6b3047bcd..427022792 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -902,6 +902,18 @@ a.hide-toggle { border-radius: 0; } +.geocode-item { + width: 50%; + background-color: #ccc; + left: 25%; + margin-top: 30px; + border-radius: 2px; +} + +.geocode-item:hover { + background-color: #aaa; +} + .feature-list-item { background-color: #fff; font-weight: bold; From 29765f961d2a3f16a62b78e73d71bfed4242f69e Mon Sep 17 00:00:00 2001 From: Simon Bilsky-Rollins Date: Mon, 8 Oct 2018 12:14:03 -0400 Subject: [PATCH 16/51] add fields to bench preset and create seats field --- data/presets.yaml | 3 +++ data/presets/fields.json | 1 + data/presets/fields/seats.json | 5 ++++ data/presets/presets.json | 2 +- data/presets/presets/amenity/bench.json | 5 +++- data/taginfo.json | 1 + dist/locales/en.json | 31 +++---------------------- 7 files changed, 18 insertions(+), 30 deletions(-) create mode 100644 data/presets/fields/seats.json diff --git a/data/presets.yaml b/data/presets.yaml index cf7299c1e..1bbe52bd8 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1585,6 +1585,9 @@ en: seasonal: # seasonal=* label: Seasonal + seats: + # seats=* + label: Seats second_hand: # second_hand=* label: Sells Used diff --git a/data/presets/fields.json b/data/presets/fields.json index fc1b11195..ef5e3a31e 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -263,6 +263,7 @@ "seamark/type": {"key": "seamark:type", "type": "combo", "universal": true, "label": "Seamark"}, "seamark/wreck/category": {"key": "seamark:wreck:category", "type": "combo", "label": "Category"}, "seasonal": {"key": "seasonal", "type": "check", "label": "Seasonal"}, + "seats": {"key": "seats", "type": "number", "label": "Seats"}, "second_hand": {"key": "second_hand", "type": "combo", "label": "Sells Used", "placeholder": "Yes, No, Only", "strings": {"options": {"yes": "Yes", "no": "No", "only": "Only"}}}, "service_rail": {"key": "service", "type": "combo", "label": "Service Type", "strings": {"options": {"spur": "Spur", "yard": "Yard", "siding": "Siding", "crossover": "Crossover"}}}, "service_times": {"key": "service_times", "type": "text", "label": "Service Times"}, diff --git a/data/presets/fields/seats.json b/data/presets/fields/seats.json new file mode 100644 index 000000000..d3f5818f2 --- /dev/null +++ b/data/presets/fields/seats.json @@ -0,0 +1,5 @@ +{ + "key": "seats", + "type": "number", + "label": "Seats" +} diff --git a/data/presets/presets.json b/data/presets/presets.json index e8d5feadb..4ee17ff64 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -51,7 +51,7 @@ "amenity/bank": {"icon": "maki-bank", "fields": ["name", "atm", "operator", "address", "building_area", "opening_hours", "drive_through"], "geometry": ["point", "area"], "terms": ["credit union", "check", "deposit", "fund", "investment", "repository", "reserve", "safe", "savings", "stock", "treasury", "trust", "vault"], "tags": {"amenity": "bank"}, "name": "Bank"}, "amenity/bar": {"icon": "maki-bar", "fields": ["name", "operator", "address", "building_area", "opening_hours", "smoking", "outdoor_seating", "brewery"], "geometry": ["point", "area"], "terms": ["dive", "beer", "bier", "booze"], "tags": {"amenity": "bar"}, "name": "Bar"}, "amenity/bbq": {"icon": "maki-bbq", "fields": ["covered", "fuel", "access_simple"], "geometry": ["point"], "terms": ["bbq", "grill"], "tags": {"amenity": "bbq"}, "name": "Barbecue/Grill"}, - "amenity/bench": {"icon": "temaki-bench", "fields": ["backrest"], "geometry": ["point", "vertex", "line"], "terms": ["seat"], "tags": {"amenity": "bench"}, "name": "Bench"}, + "amenity/bench": {"icon": "temaki-bench", "fields": ["backrest", "material", "seats", "colour"], "geometry": ["point", "vertex", "line"], "terms": ["seat"], "tags": {"amenity": "bench"}, "name": "Bench"}, "amenity/bicycle_parking": {"icon": "maki-bicycle", "fields": ["bicycle_parking", "capacity", "operator", "covered", "access_simple"], "geometry": ["point", "vertex", "area"], "terms": ["bike"], "tags": {"amenity": "bicycle_parking"}, "name": "Bicycle Parking"}, "amenity/bicycle_rental": {"icon": "maki-bicycle", "fields": ["capacity", "network", "operator", "payment_multi"], "geometry": ["point", "vertex", "area"], "terms": ["bike"], "tags": {"amenity": "bicycle_rental"}, "name": "Bicycle Rental"}, "amenity/bicycle_repair_station": {"icon": "maki-bicycle", "fields": ["operator", "brand", "opening_hours", "fee", "service/bicycle"], "geometry": ["point", "vertex"], "terms": ["bike", "repair", "chain", "pump"], "tags": {"amenity": "bicycle_repair_station"}, "name": "Bicycle Repair Tool Stand"}, diff --git a/data/presets/presets/amenity/bench.json b/data/presets/presets/amenity/bench.json index fa9be5ffc..5d58127cd 100644 --- a/data/presets/presets/amenity/bench.json +++ b/data/presets/presets/amenity/bench.json @@ -1,7 +1,10 @@ { "icon": "temaki-bench", "fields": [ - "backrest" + "backrest", + "material", + "seats", + "colour" ], "geometry": [ "point", diff --git a/data/taginfo.json b/data/taginfo.json index ecf5e471c..578541ce6 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -7381,6 +7381,7 @@ {"key": "seamark:mooring:category", "description": "Category"}, {"key": "seamark:wreck:category", "description": "Category"}, {"key": "seasonal", "description": "Seasonal"}, + {"key": "seats", "description": "Seats"}, {"key": "second_hand", "value": "yes", "description": "Sells Used"}, {"key": "second_hand", "value": "no", "description": "Sells Used"}, {"key": "second_hand", "value": "only", "description": "Sells Used"}, diff --git a/dist/locales/en.json b/dist/locales/en.json index 61869528c..ec87ef3ae 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2769,6 +2769,9 @@ "seasonal": { "label": "Seasonal" }, + "seats": { + "label": "Seats" + }, "second_hand": { "label": "Sells Used", "placeholder": "Yes, No, Only", @@ -7384,34 +7387,6 @@ "description": "Orthofoto layer provided by basemap.at. \"Successor\" of geoimage.at imagery.", "name": "basemap.at Orthofoto" }, - "eufar-balaton": { - "attribution": { - "text": "EUFAR Balaton ortofotó 2010" - }, - "description": "1940 geo-tagged photography from Balaton Limnological Institute.", - "name": "EUFAR Balaton orthophotos" - }, - "finds.jp_KBN_2500": { - "attribution": { - "text": "GSI KIBAN 2500" - }, - "description": "GSI Kiban 2500 via finds.jp. Good for tracing, but a bit older.", - "name": "Japan GSI KIBAN 2500" - }, - "gsi.go.jp": { - "attribution": { - "text": "GSI Japan" - }, - "description": "Japan GSI ortho Imagery. Usually better than bing, but a bit older.", - "name": "Japan GSI ortho Imagery" - }, - "gsi.go.jp_std_map": { - "attribution": { - "text": "GSI Japan" - }, - "description": "Japan GSI Standard Map. Widely covered.", - "name": "Japan GSI Standard Map" - }, "hike_n_bike": { "attribution": { "text": "© OpenStreetMap contributors" From c886490f79a5d4038dc9e499b54d7ea4a3087564 Mon Sep 17 00:00:00 2001 From: Simon Bilsky-Rollins Date: Mon, 8 Oct 2018 22:09:34 -0400 Subject: [PATCH 17/51] add minValue and placeholder for seats field --- data/presets.yaml | 2 ++ data/presets/fields.json | 2 +- data/presets/fields/seats.json | 4 +++- dist/locales/en.json | 3 ++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 1bbe52bd8..3b55cbb35 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1588,6 +1588,8 @@ en: seats: # seats=* label: Seats + # seats field placeholder + placeholder: '2, 4, 6...' second_hand: # second_hand=* label: Sells Used diff --git a/data/presets/fields.json b/data/presets/fields.json index ef5e3a31e..19c54a78d 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -263,7 +263,7 @@ "seamark/type": {"key": "seamark:type", "type": "combo", "universal": true, "label": "Seamark"}, "seamark/wreck/category": {"key": "seamark:wreck:category", "type": "combo", "label": "Category"}, "seasonal": {"key": "seasonal", "type": "check", "label": "Seasonal"}, - "seats": {"key": "seats", "type": "number", "label": "Seats"}, + "seats": {"key": "seats", "type": "number", "minValue": 0, "label": "Seats", "placeholder": "2, 4, 6..."}, "second_hand": {"key": "second_hand", "type": "combo", "label": "Sells Used", "placeholder": "Yes, No, Only", "strings": {"options": {"yes": "Yes", "no": "No", "only": "Only"}}}, "service_rail": {"key": "service", "type": "combo", "label": "Service Type", "strings": {"options": {"spur": "Spur", "yard": "Yard", "siding": "Siding", "crossover": "Crossover"}}}, "service_times": {"key": "service_times", "type": "text", "label": "Service Times"}, diff --git a/data/presets/fields/seats.json b/data/presets/fields/seats.json index d3f5818f2..e9655f895 100644 --- a/data/presets/fields/seats.json +++ b/data/presets/fields/seats.json @@ -1,5 +1,7 @@ { "key": "seats", "type": "number", - "label": "Seats" + "minValue": 0, + "label": "Seats", + "placeholder": "2, 4, 6..." } diff --git a/dist/locales/en.json b/dist/locales/en.json index ec87ef3ae..6e8391569 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2770,7 +2770,8 @@ "label": "Seasonal" }, "seats": { - "label": "Seats" + "label": "Seats", + "placeholder": "2, 4, 6..." }, "second_hand": { "label": "Sells Used", From 35d8143f006bc38d13bf98f24c7c141d2d3e5634 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 8 Oct 2018 22:44:10 -0400 Subject: [PATCH 18/51] Upgrade FontAwesome (closes #5389, closes #5388) --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 4dd97c03f..7e05e92fd 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,10 @@ "wmf-sitematrix": "0.1.4" }, "devDependencies": { - "@fortawesome/fontawesome-svg-core": "~1.2.4", - "@fortawesome/free-brands-svg-icons": "~5.3.1", - "@fortawesome/free-regular-svg-icons": "~5.3.1", - "@fortawesome/free-solid-svg-icons": "~5.3.1", + "@fortawesome/fontawesome-svg-core": "~1.2.5", + "@fortawesome/free-brands-svg-icons": "~5.4.0", + "@fortawesome/free-regular-svg-icons": "~5.4.0", + "@fortawesome/free-solid-svg-icons": "~5.4.0", "@mapbox/maki": "^4.0.0", "chai": "^4.1.0", "colors": "^1.1.2", From 3586451392f5a841a469ecfb3c1691ab66f65e5b Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Tue, 9 Oct 2018 18:44:49 -0700 Subject: [PATCH 19/51] Adds custom CSS to `man_made=pipeline` features Adds `location` as a secondary tag class Adds the `tunnel` styling to lines tagged `location=underground` --- css/50_misc.css | 26 ++++++++++++++++++++++---- modules/svg/tag_classes.js | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/css/50_misc.css b/css/50_misc.css index be90c5e73..a2abc74aa 100644 --- a/css/50_misc.css +++ b/css/50_misc.css @@ -1,10 +1,12 @@ -/* power */ +/* power and pipeline */ .preset-icon .icon.tag-man_made-pipeline, .preset-icon .icon.tag-power { color: #939393; fill: #939393; } +/* power */ + path.stroke.tag-power { stroke: #939393; stroke-width: 2; @@ -13,6 +15,21 @@ path.casing.tag-power { stroke: none; } +/* pipeline */ + +path.stroke.tag-man_made-pipeline { + stroke: #CBD0D8; + stroke-linecap: butt; + stroke-width: 3; + stroke-dasharray: 80, 1.25; +} +path.casing.tag-man_made-pipeline { + stroke: #666; + stroke-width: 4.5; +} +.low-zoom path.stroke.tag-man_made-pipeline { + stroke-dasharray: 40, 1; +} /* boundaries */ path.stroke.tag-boundary { @@ -145,10 +162,12 @@ path.casing.tag-highway-bridleway.tag-bridge { /* tunnels */ -path.stroke.tag-tunnel { +path.stroke.tag-tunnel, +path.line.stroke.tag-location-underground { stroke-opacity: 0.3; } -path.casing.tag-tunnel { +path.casing.tag-tunnel, +path.line.casing.tag-location-underground { stroke-opacity: 0.5; stroke-linecap: butt; stroke-dasharray: none; @@ -325,4 +344,3 @@ path.stroke.tag-crossing.tag-crossing-zebra { .low-zoom path.stroke.tag-crossing.tag-crossing-zebra { stroke-dasharray: 3, 2; } - diff --git a/modules/svg/tag_classes.js b/modules/svg/tag_classes.js index d84b3c0f6..508c731a3 100644 --- a/modules/svg/tag_classes.js +++ b/modules/svg/tag_classes.js @@ -15,7 +15,7 @@ export function svgTagClasses() { var secondaries = [ 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', - 'public_transport' + 'public_transport', 'location' ]; var tagClassRe = /^tag-/; var _tags = function(entity) { return entity.tags; }; From 27158d77f3d715fd99d6e14e910ecc659decfdd6 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 10 Oct 2018 20:36:53 -0700 Subject: [PATCH 20/51] Adds a button that lets users manually download individual relation members Makes the map zoom to the chosen relation member if it is not currently visible --- dist/locales/en.json | 1 + modules/ui/raw_member_editor.js | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/dist/locales/en.json b/dist/locales/en.json index 8b6be7311..4ad9102d2 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1,6 +1,7 @@ { "en": { "icons": { + "download": "download", "information": "info", "remove": "remove", "undo": "undo" diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index 3d61148ab..dc5f56053 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -23,9 +23,24 @@ export function uiRawMemberEditor(context) { var taginfo = services.taginfo, _entityID; + function downloadMember(d) { + d3_event.preventDefault(); + // display the loading indicator + d3_select(this.parentNode).classed('tag-reference-loading', true); + context.loadEntity(d.id); + } + function selectMember(d) { d3_event.preventDefault(); + + var entity = context.entity(d.id); + var mapExtent = context.map().extent(); + if (!entity.intersects(mapExtent, context.graph())) { + // zoom to the entity if its extent is not visible now + context.map().zoomTo(entity); + } + context.enter(modeSelect(context, [d.id])); } @@ -125,9 +140,19 @@ export function uiRawMemberEditor(context) { .text(function(d) { return utilDisplayName(d.member); }); } else { - d3_select(this).append('label') + var incompleteLabel = d3_select(this).append('label') .attr('class', 'form-label') .text(t('inspector.incomplete', { id: d.id })); + + var wrap = incompleteLabel.append('div') + .attr('class', 'form-label-button-wrap'); + + wrap.append('button') + .attr('class', 'download-icon') + .attr('title', t('icons.download')) + .attr('tabindex', -1) + .call(svgIcon('#iD-icon-load')) + .on('click', downloadMember); } }); From 02bbe7bcc8750e7c653d74822452ce41e6f00464 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 10 Oct 2018 20:50:17 -0700 Subject: [PATCH 21/51] Checks in the core.yaml changes needed for the prior commit --- data/core.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/core.yaml b/data/core.yaml index 5472f81d1..ff8df3ff6 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1,5 +1,6 @@ en: icons: + download: download information: info remove: remove undo: undo From 4fefa515745b43ed02fabaeaeb9130188bab27bf Mon Sep 17 00:00:00 2001 From: yves Date: Thu, 11 Oct 2018 14:11:02 +0200 Subject: [PATCH 22/51] No differentiation of piste type field is needed, it's even disturbing to see the piste field change when you change the piste type. --- data/presets.yaml | 22 ---------- data/presets/fields.json | 2 - data/presets/fields/piste/type_downhill.json | 13 ------ data/presets/fields/piste/type_nordic.json | 12 ------ data/presets/presets.json | 14 +++--- data/presets/presets/piste/downhill.json | 2 +- data/presets/presets/piste/hike.json | 1 + data/presets/presets/piste/ice_skate.json | 1 + data/presets/presets/piste/nordic.json | 2 +- data/presets/presets/piste/skitour.json | 1 + data/presets/presets/piste/sled.json | 1 + data/presets/presets/piste/sleigh.json | 1 + dist/locales/en.json | 45 ++++++++++++-------- 13 files changed, 42 insertions(+), 75 deletions(-) delete mode 100644 data/presets/fields/piste/type_downhill.json delete mode 100644 data/presets/fields/piste/type_nordic.json diff --git a/data/presets.yaml b/data/presets.yaml index f0ed96ea0..bcade88e0 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1301,28 +1301,6 @@ en: sleigh: Sleigh # 'piste:type=snow_park' snow_park: Snow Park - piste/type_downhill: - # 'piste:type=*' - label: Type - options: - # 'piste:type=connection' - connection: Connection between pistes - # 'piste:type=downhill' - downhill: Downhill - # 'piste:type=playground' - playground: Playground - # 'piste:type=snow_park' - snow_park: Snow Park - piste/type_nordic: - # 'piste:type=*' - label: Type - options: - # 'piste:type=connection' - connection: Connection between pistes - # 'piste:type=nordic' - nordic: Nordic - # 'piste:type=playground' - playground: Playground place: # place=* label: Type diff --git a/data/presets/fields.json b/data/presets/fields.json index b7567c740..2a8bb55a6 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -210,8 +210,6 @@ "piste/grooming_hike": {"key": "piste:grooming", "type": "combo", "label": "Grooming", "strings": {"options": {"classic": "Classic - Winter Hiking", "backcountry": "Backcountry - Snowshoeing"}}}, "piste/grooming_nordic": {"key": "piste:grooming", "type": "combo", "label": "Grooming", "strings": {"options": {"classic": "Classic", "backcountry": "Backcountry, no grooming", "classic+skating": "Classic and Skating", "scooter": "Scooter/Snowmobile", "skating": "Skating"}}}, "piste/grooming": {"key": "piste:grooming", "type": "combo", "label": "Grooming", "strings": {"options": {"classic": "Classic", "mogul": "Mogul", "backcountry": "Backcountry", "classic+skating": "Classic and Skating", "scooter": "Scooter/Snowmobile", "skating": "Skating"}}}, - "piste/type_downhill": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"downhill": "Downhill", "snow_park": "Snow Park", "playground": "Playground", "connection": "Connection between pistes"}}}, - "piste/type_nordic": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"nordic": "Nordic", "playground": "Playground", "connection": "Connection between pistes"}}}, "piste/type": {"key": "piste:type", "type": "typeCombo", "label": "Type", "strings": {"options": {"downhill": "Downhill", "nordic": "Nordic", "skitour": "Skitour", "sled": "Sled", "hike": "Hike", "sleigh": "Sleigh", "ice_skate": "Ice Skate", "snow_park": "Snow Park", "playground": "Playground", "connection": "Connection"}}}, "place": {"key": "place", "type": "typeCombo", "label": "Type"}, "plant": {"key": "plant", "type": "combo", "label": "Plant"}, diff --git a/data/presets/fields/piste/type_downhill.json b/data/presets/fields/piste/type_downhill.json deleted file mode 100644 index 597f838e2..000000000 --- a/data/presets/fields/piste/type_downhill.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "key": "piste:type", - "type": "typeCombo", - "label": "Type", - "strings": { - "options": { - "downhill": "Downhill", - "snow_park": "Snow Park", - "playground": "Playground", - "connection": "Connection between pistes" - } - } -} diff --git a/data/presets/fields/piste/type_nordic.json b/data/presets/fields/piste/type_nordic.json deleted file mode 100644 index dba168b2f..000000000 --- a/data/presets/fields/piste/type_nordic.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "key": "piste:type", - "type": "typeCombo", - "label": "Type", - "strings": { - "options": { - "nordic": "Nordic", - "playground": "Playground", - "connection": "Connection between pistes" - } - } -} diff --git a/data/presets/presets.json b/data/presets/presets.json index 509ce6b2f..b2f94a36f 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -659,14 +659,14 @@ "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_downhill", "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/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", "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_nordic", "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/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/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/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/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/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/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"}, "place/farm": {"icon": "maki-farm", "geometry": ["point", "area"], "fields": ["name"], "tags": {"place": "farm"}, "name": "Farm", "searchable": false}, "place/city": {"icon": "maki-city", "fields": ["name", "population"], "geometry": ["point", "area"], "tags": {"place": "city"}, "name": "City"}, "place/hamlet": {"icon": "maki-triangle-stroked", "fields": ["name", "population"], "geometry": ["point", "area"], "tags": {"place": "hamlet"}, "name": "Hamlet"}, diff --git a/data/presets/presets/piste/downhill.json b/data/presets/presets/piste/downhill.json index a93097c42..c8e453def 100644 --- a/data/presets/presets/piste/downhill.json +++ b/data/presets/presets/piste/downhill.json @@ -2,7 +2,7 @@ "icon": "maki-skiing", "fields": [ "name", - "piste/type_downhill", + "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", diff --git a/data/presets/presets/piste/hike.json b/data/presets/presets/piste/hike.json index 9376515b7..79aaa6183 100644 --- a/data/presets/presets/piste/hike.json +++ b/data/presets/presets/piste/hike.json @@ -2,6 +2,7 @@ "icon": "fas-snowflake", "fields": [ "name", + "piste/type", "piste/difficulty", "piste/grooming_hike", "oneway", diff --git a/data/presets/presets/piste/ice_skate.json b/data/presets/presets/piste/ice_skate.json index 4e6a86bde..a64e4854c 100644 --- a/data/presets/presets/piste/ice_skate.json +++ b/data/presets/presets/piste/ice_skate.json @@ -2,6 +2,7 @@ "icon": "fas-snowflake", "fields": [ "name", + "piste/type", "sport_ice", "oneway", "lit" diff --git a/data/presets/presets/piste/nordic.json b/data/presets/presets/piste/nordic.json index 42a95f751..99d4d17f2 100644 --- a/data/presets/presets/piste/nordic.json +++ b/data/presets/presets/piste/nordic.json @@ -2,7 +2,7 @@ "icon": "maki-skiing", "fields": [ "name", - "piste/type_nordic", + "piste/type", "piste/difficulty_nordic", "piste/grooming_nordic", "oneway", diff --git a/data/presets/presets/piste/skitour.json b/data/presets/presets/piste/skitour.json index a8ab25a0b..831b84fe9 100644 --- a/data/presets/presets/piste/skitour.json +++ b/data/presets/presets/piste/skitour.json @@ -2,6 +2,7 @@ "icon": "maki-skiing", "fields": [ "name", + "piste/type", "piste/difficulty_skitour", "piste/grooming", "oneway", diff --git a/data/presets/presets/piste/sled.json b/data/presets/presets/piste/sled.json index 594cb743a..d64a55a7b 100644 --- a/data/presets/presets/piste/sled.json +++ b/data/presets/presets/piste/sled.json @@ -2,6 +2,7 @@ "icon": "fas-snowflake", "fields": [ "name", + "piste/type", "piste/difficulty", "piste/grooming", "oneway", diff --git a/data/presets/presets/piste/sleigh.json b/data/presets/presets/piste/sleigh.json index 8b919c431..dc7c7cd28 100644 --- a/data/presets/presets/piste/sleigh.json +++ b/data/presets/presets/piste/sleigh.json @@ -2,6 +2,7 @@ "icon": "fas-snowflake", "fields": [ "name", + "piste/type", "piste/difficulty", "piste/grooming", "oneway", diff --git a/dist/locales/en.json b/dist/locales/en.json index 8b6be7311..7d2ed2e9e 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2519,23 +2519,6 @@ "skating": "Skating" } }, - "piste/type_downhill": { - "label": "Type", - "options": { - "downhill": "Downhill", - "snow_park": "Snow Park", - "playground": "Playground", - "connection": "Connection between pistes" - } - }, - "piste/type_nordic": { - "label": "Type", - "options": { - "nordic": "Nordic", - "playground": "Playground", - "connection": "Connection between pistes" - } - }, "piste/type": { "label": "Type", "options": { @@ -7399,6 +7382,34 @@ "description": "Orthofoto layer provided by basemap.at. \"Successor\" of geoimage.at imagery.", "name": "basemap.at Orthofoto" }, + "eufar-balaton": { + "attribution": { + "text": "EUFAR Balaton ortofotó 2010" + }, + "description": "1940 geo-tagged photography from Balaton Limnological Institute.", + "name": "EUFAR Balaton orthophotos" + }, + "finds.jp_KBN_2500": { + "attribution": { + "text": "GSI KIBAN 2500" + }, + "description": "GSI Kiban 2500 via finds.jp. Good for tracing, but a bit older.", + "name": "Japan GSI KIBAN 2500" + }, + "gsi.go.jp": { + "attribution": { + "text": "GSI Japan" + }, + "description": "Japan GSI ortho Imagery. Usually better than bing, but a bit older.", + "name": "Japan GSI ortho Imagery" + }, + "gsi.go.jp_std_map": { + "attribution": { + "text": "GSI Japan" + }, + "description": "Japan GSI Standard Map. Widely covered.", + "name": "Japan GSI Standard Map" + }, "hike_n_bike": { "attribution": { "text": "© OpenStreetMap contributors" From 7ad55b50745881f433f5fa5f7b947bc2d80783ef Mon Sep 17 00:00:00 2001 From: Christopher Beddow Date: Wed, 10 Oct 2018 17:15:31 -0600 Subject: [PATCH 23/51] fix: traffic signs to new endpoint --- modules/services/mapillary.js | 1432 ++++++++++++++++----------------- 1 file changed, 716 insertions(+), 716 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 8be72b3f1..0656bfc22 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -1,716 +1,716 @@ -/* global Mapillary:false */ -import _find from 'lodash-es/find'; -import _flatten from 'lodash-es/flatten'; -import _forEach from 'lodash-es/forEach'; -import _isEmpty from 'lodash-es/isEmpty'; -import _map from 'lodash-es/map'; -import _some from 'lodash-es/some'; -import _union from 'lodash-es/union'; - -import { range as d3_range } from 'd3-array'; -import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { request as d3_request } from 'd3-request'; -import { - select as d3_select, - selectAll as d3_selectAll -} from 'd3-selection'; - -import rbush from 'rbush'; - -import { geoExtent, geoScaleToZoom } from '../geo'; -import { svgDefs } from '../svg'; -import { utilQsString, utilRebind, utilTiler } from '../util'; - - -var apibase = 'https://a.mapillary.com/v3/'; -var viewercss = 'mapillary-js/mapillary.min.css'; -var viewerjs = 'mapillary-js/mapillary.min.js'; -var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; -var maxResults = 1000; -var tileZoom = 14; -var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); -var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged'); -var _mlyFallback = false; -var _mlyCache; -var _mlyClicks; -var _mlySelectedImage; -var _mlyViewer; - - -function abortRequest(i) { - i.abort(); -} - - -function maxPageAtZoom(z) { - if (z < 15) return 2; - if (z === 15) return 5; - if (z === 16) return 10; - if (z === 17) return 20; - if (z === 18) return 40; - if (z > 18) return 80; -} - - -function loadTiles(which, url, projection) { - var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler.getTiles(projection); - - // abort inflight requests that are no longer needed - var cache = _mlyCache[which]; - _forEach(cache.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { return k.indexOf(tile.id + ',') === 0; }); - - if (!wanted) { - abortRequest(v); - delete cache.inflight[k]; - } - }); - - tiles.forEach(function(tile) { - loadNextTilePage(which, currZoom, url, tile); - }); -} - - -function loadNextTilePage(which, currZoom, url, tile) { - var cache = _mlyCache[which]; - var rect = tile.extent.rectangle(); - var maxPages = maxPageAtZoom(currZoom); - var nextPage = cache.nextPage[tile.id] || 0; - var nextURL = cache.nextURL[tile.id] || url + - utilQsString({ - per_page: maxResults, - page: nextPage, - client_id: clientId, - bbox: [rect[0], rect[1], rect[2], rect[3]].join(','), - }); - - if (nextPage > maxPages) return; - - var id = tile.id + ',' + String(nextPage); - if (cache.loaded[id] || cache.inflight[id]) return; - cache.inflight[id] = d3_request(nextURL) - .mimeType('application/json') - .response(function(xhr) { - var linkHeader = xhr.getResponseHeader('Link'); - if (linkHeader) { - var pagination = parsePagination(xhr.getResponseHeader('Link')); - if (pagination.next) { - cache.nextURL[tile.id] = pagination.next; - } - } - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { - cache.loaded[id] = true; - delete cache.inflight[id]; - if (err || !data.features || !data.features.length) return; - - var features = data.features.map(function(feature) { - var loc = feature.geometry.coordinates; - var d; - - if (which === 'images') { - d = { - loc: loc, - key: feature.properties.key, - ca: feature.properties.ca, - captured_at: feature.properties.captured_at, - captured_by: feature.properties.username, - pano: feature.properties.pano - }; - cache.forImageKey[d.key] = d; // cache imageKey -> image - - } else if (which === 'sequences') { - var sequenceKey = feature.properties.key; - cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString - feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) { - cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey - }); - return false; // because no `d` data worth loading into an rbush - - } else if (which === 'objects') { - d = { - loc: loc, - key: feature.properties.key, - value: feature.properties.value, - package: feature.properties.package, - detections: feature.properties.detections - }; - - // cache imageKey -> detectionKey - feature.properties.detections.forEach(function(detection) { - var imageKey = detection.image_key; - var detectionKey = detection.detection_key; - if (!_mlyCache.detections[imageKey]) { - _mlyCache.detections[imageKey] = {}; - } - if (!_mlyCache.detections[imageKey][detectionKey]) { - _mlyCache.detections[imageKey][detectionKey] = {}; - } - }); - } - - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; - - }).filter(Boolean); - - cache.rtree.load(features); - - if (which === 'images' || which === 'sequences') { - dispatch.call('loadedImages'); - } else if (which === 'objects') { - dispatch.call('loadedSigns'); - } - - if (data.features.length === maxResults) { // more pages to load - cache.nextPage[tile.id] = nextPage + 1; - loadNextTilePage(which, currZoom, url, tile); - } else { - cache.nextPage[tile.id] = Infinity; // no more pages to load - } - }); -} - -// extract links to pages of API results -function parsePagination(links) { - return links.split(',').map(function(rel) { - var elements = rel.split(';'); - if (elements.length === 2) { - return [ - /<(.+)>/.exec(elements[0])[1], - /rel="(.+)"/.exec(elements[1])[1] - ]; - } else { - return ['','']; - } - }).reduce(function(pagination, val) { - pagination[val[1]] = val[0]; - return pagination; - }, {}); -} - - -// partition viewport into `psize` x `psize` regions -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; - - rows.forEach(function(y) { - cols.forEach(function(x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push( - geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; -} - - -// no more than `limit` results per partition. -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; - - var partitions = partitionViewport(psize, projection); - var results; - - // console.time('previous'); - results = _flatten(_map(partitions, function(extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - })); - // console.timeEnd('previous'); - - // console.time('new'); - // results = partitions.reduce(function(result, extent) { - // var found = rtree.search(extent.bbox()) - // .map(function(d) { return d.data; }) - // .sort(function(a, b) { - // return a.loc[1] - b.loc[1]; - // // return a.key.localeCompare(b.key); - // }) - // .slice(0, limit); - - // return (found.length ? result.concat(found) : result); - // }, []); - // console.timeEnd('new'); - - return results; -} - - - -export default { - - init: function() { - if (!_mlyCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch, 'on'); - }, - - reset: function() { - var cache = _mlyCache; - - if (cache) { - if (cache.images && cache.images.inflight) { - _forEach(cache.images.inflight, abortRequest); - } - if (cache.objects && cache.objects.inflight) { - _forEach(cache.objects.inflight, abortRequest); - } - if (cache.sequences && cache.sequences.inflight) { - _forEach(cache.sequences.inflight, abortRequest); - } - } - - _mlyCache = { - images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {} }, - objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() }, - sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {}, lineString: {} }, - detections: {} - }; - - _mlySelectedImage = null; - _mlyClicks = []; - }, - - - images: function(projection) { - var psize = 16, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.images.rtree); - }, - - - signs: function(projection) { - var psize = 32, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.objects.rtree); - }, - - - sequences: 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(); - var sequenceKeys = {}; - - // all sequences for images in viewport - _mlyCache.images.rtree.search(bbox) - .forEach(function(d) { - var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key]; - if (sequenceKey) { - sequenceKeys[sequenceKey] = true; - } - }); - - // Return lineStrings for the sequences - return Object.keys(sequenceKeys).map(function(sequenceKey) { - return _mlyCache.sequences.lineString[sequenceKey]; - }); - }, - - - signsSupported: function() { - return true; - }, - - - loadImages: function(projection) { - loadTiles('images', apibase + 'images?', projection); - loadTiles('sequences', apibase + 'sequences?', projection); - }, - - - loadSigns: function(context, projection) { - // if we are looking at signs, we'll actually need to fetch images too - loadTiles('images', apibase + 'images?', projection); - loadTiles('objects', apibase + 'objects?', projection); - }, - - - loadViewer: function(context) { - // add mly-wrapper - var wrap = d3_select('#photoviewer').selectAll('.mly-wrapper') - .data([0]); - - wrap.enter() - .append('div') - .attr('id', 'mly') - .attr('class', 'photo-wrapper mly-wrapper') - .classed('hide', true); - - // load mapillary-viewercss - d3_select('head').selectAll('#mapillary-viewercss') - .data([0]) - .enter() - .append('link') - .attr('id', 'mapillary-viewercss') - .attr('rel', 'stylesheet') - .attr('href', context.asset(viewercss)); - - // load mapillary-viewerjs - d3_select('head').selectAll('#mapillary-viewerjs') - .data([0]) - .enter() - .append('script') - .attr('id', 'mapillary-viewerjs') - .attr('src', context.asset(viewerjs)); - - // load mapillary signs sprite - var defs = context.container().select('defs'); - defs.call(svgDefs(context).addSprites, ['mapillary-sprite']); - - // Register viewer resize handler - context.ui().on('photoviewerResize', function() { - if (_mlyViewer) { - _mlyViewer.resize(); - } - }); - }, - - - showViewer: function() { - var wrap = d3_select('#photoviewer') - .classed('hide', false); - - var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); - - if (isHidden && _mlyViewer) { - wrap - .selectAll('.photo-wrapper:not(.mly-wrapper)') - .classed('hide', true); - - wrap - .selectAll('.photo-wrapper.mly-wrapper') - .classed('hide', false); - - _mlyViewer.resize(); - } - - return this; - }, - - - hideViewer: function() { - _mlySelectedImage = null; - - if (!_mlyFallback && _mlyViewer) { - _mlyViewer.getComponent('sequence').stop(); - } - - var viewer = d3_select('#photoviewer'); - if (!viewer.empty()) viewer.datum(null); - - viewer - .classed('hide', true) - .selectAll('.photo-wrapper') - .classed('hide', true); - - d3_selectAll('.viewfield-group, .sequence, .icon-sign') - .classed('selected', false); - - return this.setStyles(null, true); - }, - - - parsePagination: parsePagination, - - - updateViewer: function(imageKey, context) { - if (!imageKey) return this; - - if (!_mlyViewer) { - this.initViewer(imageKey, context); - } else { - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - return this; - }, - - - initViewer: function(imageKey, context) { - var that = this; - if (window.Mapillary && imageKey) { - var opts = { - baseImageSize: 320, - component: { - cover: false, - keyboard: false, - tag: true - } - }; - - // Disable components requiring WebGL support - if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) { - _mlyFallback = true; - opts.component = { - cover: false, - direction: false, - imagePlane: false, - keyboard: false, - mouse: false, - sequence: false, - tag: false, - image: true, // fallback - navigation: true // fallback - }; - } - - _mlyViewer = new Mapillary.Viewer('mly', clientId, null, opts); - _mlyViewer.on('nodechanged', nodeChanged); - _mlyViewer.on('bearingchanged', bearingChanged); - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - // nodeChanged: called after the viewer has changed images and is ready. - // - // There is some logic here to batch up clicks into a _mlyClicks array - // because the user might click on a lot of markers quickly and nodechanged - // may be called out of order asychronously. - // - // Clicks are added to the array in `selectedImage` and removed here. - // - function nodeChanged(node) { - if (!_mlyFallback) { - _mlyViewer.getComponent('tag').removeAll(); // remove previous detections - } - - var clicks = _mlyClicks; - var index = clicks.indexOf(node.key); - var selectedKey = _mlySelectedImage && _mlySelectedImage.key; - - if (index > -1) { // `nodechanged` initiated from clicking on a marker.. - clicks.splice(index, 1); // remove the click - // If `node.key` matches the current _mlySelectedImage, call `selectImage()` - // one more time to update the detections and attribution.. - if (node.key === selectedKey) { - that.selectImage(_mlySelectedImage, node.key, true); - } - } else { // `nodechanged` initiated from the Mapillary viewer controls.. - var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat]; - context.map().centerEase(loc); - that.selectImage(undefined, node.key, true); - } - } - - function bearingChanged(e) { - dispatch.call('bearingChanged', undefined, e); - } - }, - - - // Pass the image datum itself in `d` or the `imageKey` string. - // This allows images to be selected from places that dont have access - // to the full image datum (like the street signs layer or the js viewer) - selectImage: function(d, imageKey, fromViewer) { - if (!d && imageKey) { - // If the user clicked on something that's not an image marker, we - // might get in here.. Cache lookup can fail, e.g. if the user - // clicked a streetsign, but images are loading slowly asynchronously. - // We'll try to carry on anyway if there is no datum. There just - // might be a delay before user sees detections, captured_at, etc. - d = _mlyCache.images.forImageKey[imageKey]; - } - - _mlySelectedImage = d; - var viewer = d3_select('#photoviewer'); - if (!viewer.empty()) viewer.datum(d); - - imageKey = (d && d.key) || imageKey; - if (!fromViewer && imageKey) { - _mlyClicks.push(imageKey); - } - - this.setStyles(null, true); - - d3_selectAll('.layer-mapillary-signs .icon-sign') - .classed('selected', function(d) { - return _some(d.detections, function(detection) { - return detection.image_key === imageKey; - }); - }); - - if (d) { - this.updateDetections(d); - } - - return this; - }, - - - getSelectedImage: function() { - return _mlySelectedImage; - }, - - - getSequenceKeyForImage: function(d) { - var imageKey = d && d.key; - return imageKey && _mlyCache.sequences.forImageKey[imageKey]; - }, - - - setStyles: function(hovered, reset) { - if (reset) { // reset all layers - d3_selectAll('.viewfield-group') - .classed('highlighted', false) - .classed('hovered', false) - .classed('selected', false); - - d3_selectAll('.sequence') - .classed('highlighted', false) - .classed('selected', false); - } - - var hoveredImageKey = hovered && hovered.key; - var hoveredSequenceKey = this.getSequenceKeyForImage(hovered); - var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey]; - var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || []; - - var viewer = d3_select('#photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - var selectedImageKey = selected && selected.key; - var selectedSequenceKey = this.getSequenceKeyForImage(selected); - var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey]; - var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || []; - - // highlight sibling viewfields on either the selected or the hovered sequences - var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys); - - d3_selectAll('.layer-mapillary-images .viewfield-group') - .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) - .classed('hovered', function(d) { return d.key === hoveredImageKey; }) - .classed('selected', function(d) { return d.key === selectedImageKey; }); - - d3_selectAll('.layer-mapillary-images .sequence') - .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) - .classed('selected', function(d) { return d.properties.key === selectedSequenceKey; }); - - // update viewfields if needed - d3_selectAll('.viewfield-group .viewfield') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano && d.key !== selectedImageKey) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - return this; - }, - - - updateDetections: function(d) { - if (!_mlyViewer || _mlyFallback) return; - - var imageKey = d && d.key; - var detections = (imageKey && _mlyCache.detections[imageKey]) || []; - - _forEach(detections, function(data, k) { - if (_isEmpty(data)) { - loadDetection(k); - } else { - var tag = makeTag(data); - if (tag) { - var tagComponent = _mlyViewer.getComponent('tag'); - tagComponent.add([tag]); - } - } - }); - - - function loadDetection(detectionKey) { - var url = apibase + 'detections/' + - detectionKey + '?' + utilQsString({ client_id: clientId }); - - d3_request(url) - .mimeType('application/json') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { - if (!data || !data.properties) return; - - var imageKey = data.properties.image_key; - _mlyCache.detections[imageKey][detectionKey] = data; - - var selectedKey = _mlySelectedImage && _mlySelectedImage.key; - if (imageKey === selectedKey) { - var tag = makeTag(data); - if (tag) { - var tagComponent = _mlyViewer.getComponent('tag'); - tagComponent.add([tag]); - } - } - }); - } - - - function makeTag(data) { - var valueParts = data.properties.value.split('--'); - if (valueParts.length !== 3) return; - - var text = valueParts[1].replace(/-/g, ' '); - var tag; - - // Currently only two shapes - if (data.properties.shape.type === 'Polygon') { - var polygonGeometry = new Mapillary - .TagComponent - .PolygonGeometry(data.properties.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.OutlineTag( - data.properties.key, - polygonGeometry, - { - text: text, - textColor: 0xffff00, - lineColor: 0xffff00, - lineWidth: 2, - fillColor: 0xffff00, - fillOpacity: 0.3, - } - ); - - } else if (data.properties.shape.type === 'Point') { - var pointGeometry = new Mapillary - .TagComponent - .PointGeometry(data.properties.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.SpotTag( - data.properties.key, - pointGeometry, - { - text: text, - color: 0xffff00, - textColor: 0xffff00 - } - ); - } - - return tag; - } - }, - - - cache: function() { - return _mlyCache; - } - -}; +/* global Mapillary:false */ +import _find from 'lodash-es/find'; +import _flatten from 'lodash-es/flatten'; +import _forEach from 'lodash-es/forEach'; +import _isEmpty from 'lodash-es/isEmpty'; +import _map from 'lodash-es/map'; +import _some from 'lodash-es/some'; +import _union from 'lodash-es/union'; + +import { range as d3_range } from 'd3-array'; +import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { request as d3_request } from 'd3-request'; +import { + select as d3_select, + selectAll as d3_selectAll +} from 'd3-selection'; + +import rbush from 'rbush'; + +import { geoExtent, geoScaleToZoom } from '../geo'; +import { svgDefs } from '../svg'; +import { utilQsString, utilRebind, utilTiler } from '../util'; + + +var apibase = 'https://a.mapillary.com/v3/'; +var viewercss = 'mapillary-js/mapillary.min.css'; +var viewerjs = 'mapillary-js/mapillary.min.js'; +var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; +var maxResults = 1000; +var tileZoom = 14; +var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); +var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged'); +var _mlyFallback = false; +var _mlyCache; +var _mlyClicks; +var _mlySelectedImage; +var _mlyViewer; + + +function abortRequest(i) { + i.abort(); +} + + +function maxPageAtZoom(z) { + if (z < 15) return 2; + if (z === 15) return 5; + if (z === 16) return 10; + if (z === 17) return 20; + if (z === 18) return 40; + if (z > 18) return 80; +} + + +function loadTiles(which, url, projection) { + var currZoom = Math.floor(geoScaleToZoom(projection.scale())); + var tiles = tiler.getTiles(projection); + + // abort inflight requests that are no longer needed + var cache = _mlyCache[which]; + _forEach(cache.inflight, function(v, k) { + var wanted = _find(tiles, function(tile) { return k.indexOf(tile.id + ',') === 0; }); + + if (!wanted) { + abortRequest(v); + delete cache.inflight[k]; + } + }); + + tiles.forEach(function(tile) { + loadNextTilePage(which, currZoom, url, tile); + }); +} + + +function loadNextTilePage(which, currZoom, url, tile) { + var cache = _mlyCache[which]; + var rect = tile.extent.rectangle(); + var maxPages = maxPageAtZoom(currZoom); + var nextPage = cache.nextPage[tile.id] || 0; + var nextURL = cache.nextURL[tile.id] || url + + utilQsString({ + per_page: maxResults, + page: nextPage, + client_id: clientId, + bbox: [rect[0], rect[1], rect[2], rect[3]].join(','), + }); + + if (nextPage > maxPages) return; + + var id = tile.id + ',' + String(nextPage); + if (cache.loaded[id] || cache.inflight[id]) return; + cache.inflight[id] = d3_request(nextURL) + .mimeType('application/json') + .response(function(xhr) { + var linkHeader = xhr.getResponseHeader('Link'); + if (linkHeader) { + var pagination = parsePagination(xhr.getResponseHeader('Link')); + if (pagination.next) { + cache.nextURL[tile.id] = pagination.next; + } + } + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + cache.loaded[id] = true; + delete cache.inflight[id]; + if (err || !data.features || !data.features.length) return; + + var features = data.features.map(function(feature) { + var loc = feature.geometry.coordinates; + var d; + + if (which === 'images') { + d = { + loc: loc, + key: feature.properties.key, + ca: feature.properties.ca, + captured_at: feature.properties.captured_at, + captured_by: feature.properties.username, + pano: feature.properties.pano + }; + cache.forImageKey[d.key] = d; // cache imageKey -> image + + } else if (which === 'sequences') { + var sequenceKey = feature.properties.key; + cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString + feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) { + cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey + }); + return false; // because no `d` data worth loading into an rbush + + } else if (which === 'map_features') { + d = { + loc: loc, + key: feature.properties.key, + value: feature.properties.value, + package: feature.properties.package, + detections: feature.properties.detections + }; + + // cache imageKey -> detectionKey + feature.properties.detections.forEach(function(detection) { + var imageKey = detection.image_key; + var detectionKey = detection.detection_key; + if (!_mlyCache.detections[imageKey]) { + _mlyCache.detections[imageKey] = {}; + } + if (!_mlyCache.detections[imageKey][detectionKey]) { + _mlyCache.detections[imageKey][detectionKey] = {}; + } + }); + } + + return { + minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d + }; + + }).filter(Boolean); + + cache.rtree.load(features); + + if (which === 'images' || which === 'sequences') { + dispatch.call('loadedImages'); + } else if (which === 'map_features') { + dispatch.call('loadedSigns'); + } + + if (data.features.length === maxResults) { // more pages to load + cache.nextPage[tile.id] = nextPage + 1; + loadNextTilePage(which, currZoom, url, tile); + } else { + cache.nextPage[tile.id] = Infinity; // no more pages to load + } + }); +} + +// extract links to pages of API results +function parsePagination(links) { + return links.split(',').map(function(rel) { + var elements = rel.split(';'); + if (elements.length === 2) { + return [ + /<(.+)>/.exec(elements[0])[1], + /rel="(.+)"/.exec(elements[1])[1] + ]; + } else { + return ['','']; + } + }).reduce(function(pagination, val) { + pagination[val[1]] = val[0]; + return pagination; + }, {}); +} + + +// partition viewport into `psize` x `psize` regions +function partitionViewport(psize, projection) { + var dimensions = projection.clipExtent()[1]; + psize = psize || 16; + var cols = d3_range(0, dimensions[0], psize); + var rows = d3_range(0, dimensions[1], psize); + var partitions = []; + + rows.forEach(function(y) { + cols.forEach(function(x) { + var min = [x, y + psize]; + var max = [x + psize, y]; + partitions.push( + geoExtent(projection.invert(min), projection.invert(max))); + }); + }); + + return partitions; +} + + +// no more than `limit` results per partition. +function searchLimited(psize, limit, projection, rtree) { + limit = limit || 3; + + var partitions = partitionViewport(psize, projection); + var results; + + // console.time('previous'); + results = _flatten(_map(partitions, function(extent) { + return rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); + })); + // console.timeEnd('previous'); + + // console.time('new'); + // results = partitions.reduce(function(result, extent) { + // var found = rtree.search(extent.bbox()) + // .map(function(d) { return d.data; }) + // .sort(function(a, b) { + // return a.loc[1] - b.loc[1]; + // // return a.key.localeCompare(b.key); + // }) + // .slice(0, limit); + + // return (found.length ? result.concat(found) : result); + // }, []); + // console.timeEnd('new'); + + return results; +} + + + +export default { + + init: function() { + if (!_mlyCache) { + this.reset(); + } + + this.event = utilRebind(this, dispatch, 'on'); + }, + + reset: function() { + var cache = _mlyCache; + + if (cache) { + if (cache.images && cache.images.inflight) { + _forEach(cache.images.inflight, abortRequest); + } + if (cache.objects && cache.objects.inflight) { + _forEach(cache.objects.inflight, abortRequest); + } + if (cache.sequences && cache.sequences.inflight) { + _forEach(cache.sequences.inflight, abortRequest); + } + } + + _mlyCache = { + images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {} }, + objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() }, + sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {}, lineString: {} }, + detections: {} + }; + + _mlySelectedImage = null; + _mlyClicks = []; + }, + + + images: function(projection) { + var psize = 16, limit = 3; + return searchLimited(psize, limit, projection, _mlyCache.images.rtree); + }, + + + signs: function(projection) { + var psize = 32, limit = 3; + return searchLimited(psize, limit, projection, _mlyCache.objects.rtree); + }, + + + sequences: 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(); + var sequenceKeys = {}; + + // all sequences for images in viewport + _mlyCache.images.rtree.search(bbox) + .forEach(function(d) { + var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key]; + if (sequenceKey) { + sequenceKeys[sequenceKey] = true; + } + }); + + // Return lineStrings for the sequences + return Object.keys(sequenceKeys).map(function(sequenceKey) { + return _mlyCache.sequences.lineString[sequenceKey]; + }); + }, + + + signsSupported: function() { + return true; + }, + + + loadImages: function(projection) { + loadTiles('images', apibase + 'images?', projection); + loadTiles('sequences', apibase + 'sequences?', projection); + }, + + + loadSigns: function(context, projection) { + // if we are looking at signs, we'll actually need to fetch images too + loadTiles('images', apibase + 'images?', projection); + loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&', projection); + }, + + + loadViewer: function(context) { + // add mly-wrapper + var wrap = d3_select('#photoviewer').selectAll('.mly-wrapper') + .data([0]); + + wrap.enter() + .append('div') + .attr('id', 'mly') + .attr('class', 'photo-wrapper mly-wrapper') + .classed('hide', true); + + // load mapillary-viewercss + d3_select('head').selectAll('#mapillary-viewercss') + .data([0]) + .enter() + .append('link') + .attr('id', 'mapillary-viewercss') + .attr('rel', 'stylesheet') + .attr('href', context.asset(viewercss)); + + // load mapillary-viewerjs + d3_select('head').selectAll('#mapillary-viewerjs') + .data([0]) + .enter() + .append('script') + .attr('id', 'mapillary-viewerjs') + .attr('src', context.asset(viewerjs)); + + // load mapillary signs sprite + var defs = context.container().select('defs'); + defs.call(svgDefs(context).addSprites, ['mapillary-sprite']); + + // Register viewer resize handler + context.ui().on('photoviewerResize', function() { + if (_mlyViewer) { + _mlyViewer.resize(); + } + }); + }, + + + showViewer: function() { + var wrap = d3_select('#photoviewer') + .classed('hide', false); + + var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); + + if (isHidden && _mlyViewer) { + wrap + .selectAll('.photo-wrapper:not(.mly-wrapper)') + .classed('hide', true); + + wrap + .selectAll('.photo-wrapper.mly-wrapper') + .classed('hide', false); + + _mlyViewer.resize(); + } + + return this; + }, + + + hideViewer: function() { + _mlySelectedImage = null; + + if (!_mlyFallback && _mlyViewer) { + _mlyViewer.getComponent('sequence').stop(); + } + + var viewer = d3_select('#photoviewer'); + if (!viewer.empty()) viewer.datum(null); + + viewer + .classed('hide', true) + .selectAll('.photo-wrapper') + .classed('hide', true); + + d3_selectAll('.viewfield-group, .sequence, .icon-sign') + .classed('selected', false); + + return this.setStyles(null, true); + }, + + + parsePagination: parsePagination, + + + updateViewer: function(imageKey, context) { + if (!imageKey) return this; + + if (!_mlyViewer) { + this.initViewer(imageKey, context); + } else { + _mlyViewer.moveToKey(imageKey) + .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console + } + + return this; + }, + + + initViewer: function(imageKey, context) { + var that = this; + if (window.Mapillary && imageKey) { + var opts = { + baseImageSize: 320, + component: { + cover: false, + keyboard: false, + tag: true + } + }; + + // Disable components requiring WebGL support + if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) { + _mlyFallback = true; + opts.component = { + cover: false, + direction: false, + imagePlane: false, + keyboard: false, + mouse: false, + sequence: false, + tag: false, + image: true, // fallback + navigation: true // fallback + }; + } + + _mlyViewer = new Mapillary.Viewer('mly', clientId, null, opts); + _mlyViewer.on('nodechanged', nodeChanged); + _mlyViewer.on('bearingchanged', bearingChanged); + _mlyViewer.moveToKey(imageKey) + .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console + } + + // nodeChanged: called after the viewer has changed images and is ready. + // + // There is some logic here to batch up clicks into a _mlyClicks array + // because the user might click on a lot of markers quickly and nodechanged + // may be called out of order asychronously. + // + // Clicks are added to the array in `selectedImage` and removed here. + // + function nodeChanged(node) { + if (!_mlyFallback) { + _mlyViewer.getComponent('tag').removeAll(); // remove previous detections + } + + var clicks = _mlyClicks; + var index = clicks.indexOf(node.key); + var selectedKey = _mlySelectedImage && _mlySelectedImage.key; + + if (index > -1) { // `nodechanged` initiated from clicking on a marker.. + clicks.splice(index, 1); // remove the click + // If `node.key` matches the current _mlySelectedImage, call `selectImage()` + // one more time to update the detections and attribution.. + if (node.key === selectedKey) { + that.selectImage(_mlySelectedImage, node.key, true); + } + } else { // `nodechanged` initiated from the Mapillary viewer controls.. + var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat]; + context.map().centerEase(loc); + that.selectImage(undefined, node.key, true); + } + } + + function bearingChanged(e) { + dispatch.call('bearingChanged', undefined, e); + } + }, + + + // Pass the image datum itself in `d` or the `imageKey` string. + // This allows images to be selected from places that dont have access + // to the full image datum (like the street signs layer or the js viewer) + selectImage: function(d, imageKey, fromViewer) { + if (!d && imageKey) { + // If the user clicked on something that's not an image marker, we + // might get in here.. Cache lookup can fail, e.g. if the user + // clicked a streetsign, but images are loading slowly asynchronously. + // We'll try to carry on anyway if there is no datum. There just + // might be a delay before user sees detections, captured_at, etc. + d = _mlyCache.images.forImageKey[imageKey]; + } + + _mlySelectedImage = d; + var viewer = d3_select('#photoviewer'); + if (!viewer.empty()) viewer.datum(d); + + imageKey = (d && d.key) || imageKey; + if (!fromViewer && imageKey) { + _mlyClicks.push(imageKey); + } + + this.setStyles(null, true); + + d3_selectAll('.layer-mapillary-signs .icon-sign') + .classed('selected', function(d) { + return _some(d.detections, function(detection) { + return detection.image_key === imageKey; + }); + }); + + if (d) { + this.updateDetections(d); + } + + return this; + }, + + + getSelectedImage: function() { + return _mlySelectedImage; + }, + + + getSequenceKeyForImage: function(d) { + var imageKey = d && d.key; + return imageKey && _mlyCache.sequences.forImageKey[imageKey]; + }, + + + setStyles: function(hovered, reset) { + if (reset) { // reset all layers + d3_selectAll('.viewfield-group') + .classed('highlighted', false) + .classed('hovered', false) + .classed('selected', false); + + d3_selectAll('.sequence') + .classed('highlighted', false) + .classed('selected', false); + } + + var hoveredImageKey = hovered && hovered.key; + var hoveredSequenceKey = this.getSequenceKeyForImage(hovered); + var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey]; + var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || []; + + var viewer = d3_select('#photoviewer'); + var selected = viewer.empty() ? undefined : viewer.datum(); + var selectedImageKey = selected && selected.key; + var selectedSequenceKey = this.getSequenceKeyForImage(selected); + var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey]; + var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || []; + + // highlight sibling viewfields on either the selected or the hovered sequences + var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys); + + d3_selectAll('.layer-mapillary-images .viewfield-group') + .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) + .classed('hovered', function(d) { return d.key === hoveredImageKey; }) + .classed('selected', function(d) { return d.key === selectedImageKey; }); + + d3_selectAll('.layer-mapillary-images .sequence') + .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) + .classed('selected', function(d) { return d.properties.key === selectedSequenceKey; }); + + // update viewfields if needed + d3_selectAll('.viewfield-group .viewfield') + .attr('d', viewfieldPath); + + function viewfieldPath() { + var d = this.parentNode.__data__; + if (d.pano && d.key !== selectedImageKey) { + return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; + } else { + return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; + } + } + + return this; + }, + + + updateDetections: function(d) { + if (!_mlyViewer || _mlyFallback) return; + + var imageKey = d && d.key; + var detections = (imageKey && _mlyCache.detections[imageKey]) || []; + + _forEach(detections, function(data, k) { + if (_isEmpty(data)) { + loadDetection(k); + } else { + var tag = makeTag(data); + if (tag) { + var tagComponent = _mlyViewer.getComponent('tag'); + tagComponent.add([tag]); + } + } + }); + + + function loadDetection(detectionKey) { + var url = apibase + 'image_detections/' + + detectionKey + '?' + utilQsString({ client_id: clientId }); + + d3_request(url) + .mimeType('application/json') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + if (!data || !data.properties) return; + + var imageKey = data.properties.image_key; + _mlyCache.detections[imageKey][detectionKey] = data; + + var selectedKey = _mlySelectedImage && _mlySelectedImage.key; + if (imageKey === selectedKey) { + var tag = makeTag(data); + if (tag) { + var tagComponent = _mlyViewer.getComponent('tag'); + tagComponent.add([tag]); + } + } + }); + } + + + function makeTag(data) { + var valueParts = data.properties.value.split('--'); + if (valueParts.length !== 3) return; + + var text = valueParts[1].replace(/-/g, ' '); + var tag; + + // Currently only two shapes + if (data.properties.shape.type === 'Polygon') { + var polygonGeometry = new Mapillary + .TagComponent + .PolygonGeometry(data.properties.shape.coordinates[0]); + + tag = new Mapillary.TagComponent.OutlineTag( + data.properties.key, + polygonGeometry, + { + text: text, + textColor: 0xffff00, + lineColor: 0xffff00, + lineWidth: 2, + fillColor: 0xffff00, + fillOpacity: 0.3, + } + ); + + } else if (data.properties.shape.type === 'Point') { + var pointGeometry = new Mapillary + .TagComponent + .PointGeometry(data.properties.shape.coordinates[0]); + + tag = new Mapillary.TagComponent.SpotTag( + data.properties.key, + pointGeometry, + { + text: text, + color: 0xffff00, + textColor: 0xffff00 + } + ); + } + + return tag; + } + }, + + + cache: function() { + return _mlyCache; + } + +}; From 0353b3110d0d3d0711cd21878a5c835c7e9c9add Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 11 Oct 2018 12:41:27 -0400 Subject: [PATCH 24/51] Fix parse error in testing osm.js --- test/spec/services/osm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index b8080b2c5..c977cf599 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -140,7 +140,7 @@ describe('iD.serviceOsm', function () { var path = '/api/0.6/map?bbox=-74.542,40.655,-74.541,40.656'; var response = '' + '' + - ' ' + ' ' + ' ' + ' ' + From 4cd442e09d580a32870eab7b8a6c2882fbd72a77 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 11 Oct 2018 12:53:47 -0400 Subject: [PATCH 25/51] Restore mapillary.js line endings back from CRLF -> CR, fix tests --- modules/services/mapillary.js | 1432 +++++++++++++++---------------- test/spec/services/mapillary.js | 152 ++-- 2 files changed, 792 insertions(+), 792 deletions(-) diff --git a/modules/services/mapillary.js b/modules/services/mapillary.js index 0656bfc22..77d516a6d 100644 --- a/modules/services/mapillary.js +++ b/modules/services/mapillary.js @@ -1,716 +1,716 @@ -/* global Mapillary:false */ -import _find from 'lodash-es/find'; -import _flatten from 'lodash-es/flatten'; -import _forEach from 'lodash-es/forEach'; -import _isEmpty from 'lodash-es/isEmpty'; -import _map from 'lodash-es/map'; -import _some from 'lodash-es/some'; -import _union from 'lodash-es/union'; - -import { range as d3_range } from 'd3-array'; -import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { request as d3_request } from 'd3-request'; -import { - select as d3_select, - selectAll as d3_selectAll -} from 'd3-selection'; - -import rbush from 'rbush'; - -import { geoExtent, geoScaleToZoom } from '../geo'; -import { svgDefs } from '../svg'; -import { utilQsString, utilRebind, utilTiler } from '../util'; - - -var apibase = 'https://a.mapillary.com/v3/'; -var viewercss = 'mapillary-js/mapillary.min.css'; -var viewerjs = 'mapillary-js/mapillary.min.js'; -var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; -var maxResults = 1000; -var tileZoom = 14; -var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); -var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged'); -var _mlyFallback = false; -var _mlyCache; -var _mlyClicks; -var _mlySelectedImage; -var _mlyViewer; - - -function abortRequest(i) { - i.abort(); -} - - -function maxPageAtZoom(z) { - if (z < 15) return 2; - if (z === 15) return 5; - if (z === 16) return 10; - if (z === 17) return 20; - if (z === 18) return 40; - if (z > 18) return 80; -} - - -function loadTiles(which, url, projection) { - var currZoom = Math.floor(geoScaleToZoom(projection.scale())); - var tiles = tiler.getTiles(projection); - - // abort inflight requests that are no longer needed - var cache = _mlyCache[which]; - _forEach(cache.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { return k.indexOf(tile.id + ',') === 0; }); - - if (!wanted) { - abortRequest(v); - delete cache.inflight[k]; - } - }); - - tiles.forEach(function(tile) { - loadNextTilePage(which, currZoom, url, tile); - }); -} - - -function loadNextTilePage(which, currZoom, url, tile) { - var cache = _mlyCache[which]; - var rect = tile.extent.rectangle(); - var maxPages = maxPageAtZoom(currZoom); - var nextPage = cache.nextPage[tile.id] || 0; - var nextURL = cache.nextURL[tile.id] || url + - utilQsString({ - per_page: maxResults, - page: nextPage, - client_id: clientId, - bbox: [rect[0], rect[1], rect[2], rect[3]].join(','), - }); - - if (nextPage > maxPages) return; - - var id = tile.id + ',' + String(nextPage); - if (cache.loaded[id] || cache.inflight[id]) return; - cache.inflight[id] = d3_request(nextURL) - .mimeType('application/json') - .response(function(xhr) { - var linkHeader = xhr.getResponseHeader('Link'); - if (linkHeader) { - var pagination = parsePagination(xhr.getResponseHeader('Link')); - if (pagination.next) { - cache.nextURL[tile.id] = pagination.next; - } - } - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { - cache.loaded[id] = true; - delete cache.inflight[id]; - if (err || !data.features || !data.features.length) return; - - var features = data.features.map(function(feature) { - var loc = feature.geometry.coordinates; - var d; - - if (which === 'images') { - d = { - loc: loc, - key: feature.properties.key, - ca: feature.properties.ca, - captured_at: feature.properties.captured_at, - captured_by: feature.properties.username, - pano: feature.properties.pano - }; - cache.forImageKey[d.key] = d; // cache imageKey -> image - - } else if (which === 'sequences') { - var sequenceKey = feature.properties.key; - cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString - feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) { - cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey - }); - return false; // because no `d` data worth loading into an rbush - - } else if (which === 'map_features') { - d = { - loc: loc, - key: feature.properties.key, - value: feature.properties.value, - package: feature.properties.package, - detections: feature.properties.detections - }; - - // cache imageKey -> detectionKey - feature.properties.detections.forEach(function(detection) { - var imageKey = detection.image_key; - var detectionKey = detection.detection_key; - if (!_mlyCache.detections[imageKey]) { - _mlyCache.detections[imageKey] = {}; - } - if (!_mlyCache.detections[imageKey][detectionKey]) { - _mlyCache.detections[imageKey][detectionKey] = {}; - } - }); - } - - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; - - }).filter(Boolean); - - cache.rtree.load(features); - - if (which === 'images' || which === 'sequences') { - dispatch.call('loadedImages'); - } else if (which === 'map_features') { - dispatch.call('loadedSigns'); - } - - if (data.features.length === maxResults) { // more pages to load - cache.nextPage[tile.id] = nextPage + 1; - loadNextTilePage(which, currZoom, url, tile); - } else { - cache.nextPage[tile.id] = Infinity; // no more pages to load - } - }); -} - -// extract links to pages of API results -function parsePagination(links) { - return links.split(',').map(function(rel) { - var elements = rel.split(';'); - if (elements.length === 2) { - return [ - /<(.+)>/.exec(elements[0])[1], - /rel="(.+)"/.exec(elements[1])[1] - ]; - } else { - return ['','']; - } - }).reduce(function(pagination, val) { - pagination[val[1]] = val[0]; - return pagination; - }, {}); -} - - -// partition viewport into `psize` x `psize` regions -function partitionViewport(psize, projection) { - var dimensions = projection.clipExtent()[1]; - psize = psize || 16; - var cols = d3_range(0, dimensions[0], psize); - var rows = d3_range(0, dimensions[1], psize); - var partitions = []; - - rows.forEach(function(y) { - cols.forEach(function(x) { - var min = [x, y + psize]; - var max = [x + psize, y]; - partitions.push( - geoExtent(projection.invert(min), projection.invert(max))); - }); - }); - - return partitions; -} - - -// no more than `limit` results per partition. -function searchLimited(psize, limit, projection, rtree) { - limit = limit || 3; - - var partitions = partitionViewport(psize, projection); - var results; - - // console.time('previous'); - results = _flatten(_map(partitions, function(extent) { - return rtree.search(extent.bbox()) - .slice(0, limit) - .map(function(d) { return d.data; }); - })); - // console.timeEnd('previous'); - - // console.time('new'); - // results = partitions.reduce(function(result, extent) { - // var found = rtree.search(extent.bbox()) - // .map(function(d) { return d.data; }) - // .sort(function(a, b) { - // return a.loc[1] - b.loc[1]; - // // return a.key.localeCompare(b.key); - // }) - // .slice(0, limit); - - // return (found.length ? result.concat(found) : result); - // }, []); - // console.timeEnd('new'); - - return results; -} - - - -export default { - - init: function() { - if (!_mlyCache) { - this.reset(); - } - - this.event = utilRebind(this, dispatch, 'on'); - }, - - reset: function() { - var cache = _mlyCache; - - if (cache) { - if (cache.images && cache.images.inflight) { - _forEach(cache.images.inflight, abortRequest); - } - if (cache.objects && cache.objects.inflight) { - _forEach(cache.objects.inflight, abortRequest); - } - if (cache.sequences && cache.sequences.inflight) { - _forEach(cache.sequences.inflight, abortRequest); - } - } - - _mlyCache = { - images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {} }, - objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() }, - sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {}, lineString: {} }, - detections: {} - }; - - _mlySelectedImage = null; - _mlyClicks = []; - }, - - - images: function(projection) { - var psize = 16, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.images.rtree); - }, - - - signs: function(projection) { - var psize = 32, limit = 3; - return searchLimited(psize, limit, projection, _mlyCache.objects.rtree); - }, - - - sequences: 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(); - var sequenceKeys = {}; - - // all sequences for images in viewport - _mlyCache.images.rtree.search(bbox) - .forEach(function(d) { - var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key]; - if (sequenceKey) { - sequenceKeys[sequenceKey] = true; - } - }); - - // Return lineStrings for the sequences - return Object.keys(sequenceKeys).map(function(sequenceKey) { - return _mlyCache.sequences.lineString[sequenceKey]; - }); - }, - - - signsSupported: function() { - return true; - }, - - - loadImages: function(projection) { - loadTiles('images', apibase + 'images?', projection); - loadTiles('sequences', apibase + 'sequences?', projection); - }, - - - loadSigns: function(context, projection) { - // if we are looking at signs, we'll actually need to fetch images too - loadTiles('images', apibase + 'images?', projection); - loadTiles('map_features', apibase + 'map_features?layers=trafficsigns&', projection); - }, - - - loadViewer: function(context) { - // add mly-wrapper - var wrap = d3_select('#photoviewer').selectAll('.mly-wrapper') - .data([0]); - - wrap.enter() - .append('div') - .attr('id', 'mly') - .attr('class', 'photo-wrapper mly-wrapper') - .classed('hide', true); - - // load mapillary-viewercss - d3_select('head').selectAll('#mapillary-viewercss') - .data([0]) - .enter() - .append('link') - .attr('id', 'mapillary-viewercss') - .attr('rel', 'stylesheet') - .attr('href', context.asset(viewercss)); - - // load mapillary-viewerjs - d3_select('head').selectAll('#mapillary-viewerjs') - .data([0]) - .enter() - .append('script') - .attr('id', 'mapillary-viewerjs') - .attr('src', context.asset(viewerjs)); - - // load mapillary signs sprite - var defs = context.container().select('defs'); - defs.call(svgDefs(context).addSprites, ['mapillary-sprite']); - - // Register viewer resize handler - context.ui().on('photoviewerResize', function() { - if (_mlyViewer) { - _mlyViewer.resize(); - } - }); - }, - - - showViewer: function() { - var wrap = d3_select('#photoviewer') - .classed('hide', false); - - var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); - - if (isHidden && _mlyViewer) { - wrap - .selectAll('.photo-wrapper:not(.mly-wrapper)') - .classed('hide', true); - - wrap - .selectAll('.photo-wrapper.mly-wrapper') - .classed('hide', false); - - _mlyViewer.resize(); - } - - return this; - }, - - - hideViewer: function() { - _mlySelectedImage = null; - - if (!_mlyFallback && _mlyViewer) { - _mlyViewer.getComponent('sequence').stop(); - } - - var viewer = d3_select('#photoviewer'); - if (!viewer.empty()) viewer.datum(null); - - viewer - .classed('hide', true) - .selectAll('.photo-wrapper') - .classed('hide', true); - - d3_selectAll('.viewfield-group, .sequence, .icon-sign') - .classed('selected', false); - - return this.setStyles(null, true); - }, - - - parsePagination: parsePagination, - - - updateViewer: function(imageKey, context) { - if (!imageKey) return this; - - if (!_mlyViewer) { - this.initViewer(imageKey, context); - } else { - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - return this; - }, - - - initViewer: function(imageKey, context) { - var that = this; - if (window.Mapillary && imageKey) { - var opts = { - baseImageSize: 320, - component: { - cover: false, - keyboard: false, - tag: true - } - }; - - // Disable components requiring WebGL support - if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) { - _mlyFallback = true; - opts.component = { - cover: false, - direction: false, - imagePlane: false, - keyboard: false, - mouse: false, - sequence: false, - tag: false, - image: true, // fallback - navigation: true // fallback - }; - } - - _mlyViewer = new Mapillary.Viewer('mly', clientId, null, opts); - _mlyViewer.on('nodechanged', nodeChanged); - _mlyViewer.on('bearingchanged', bearingChanged); - _mlyViewer.moveToKey(imageKey) - .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console - } - - // nodeChanged: called after the viewer has changed images and is ready. - // - // There is some logic here to batch up clicks into a _mlyClicks array - // because the user might click on a lot of markers quickly and nodechanged - // may be called out of order asychronously. - // - // Clicks are added to the array in `selectedImage` and removed here. - // - function nodeChanged(node) { - if (!_mlyFallback) { - _mlyViewer.getComponent('tag').removeAll(); // remove previous detections - } - - var clicks = _mlyClicks; - var index = clicks.indexOf(node.key); - var selectedKey = _mlySelectedImage && _mlySelectedImage.key; - - if (index > -1) { // `nodechanged` initiated from clicking on a marker.. - clicks.splice(index, 1); // remove the click - // If `node.key` matches the current _mlySelectedImage, call `selectImage()` - // one more time to update the detections and attribution.. - if (node.key === selectedKey) { - that.selectImage(_mlySelectedImage, node.key, true); - } - } else { // `nodechanged` initiated from the Mapillary viewer controls.. - var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat]; - context.map().centerEase(loc); - that.selectImage(undefined, node.key, true); - } - } - - function bearingChanged(e) { - dispatch.call('bearingChanged', undefined, e); - } - }, - - - // Pass the image datum itself in `d` or the `imageKey` string. - // This allows images to be selected from places that dont have access - // to the full image datum (like the street signs layer or the js viewer) - selectImage: function(d, imageKey, fromViewer) { - if (!d && imageKey) { - // If the user clicked on something that's not an image marker, we - // might get in here.. Cache lookup can fail, e.g. if the user - // clicked a streetsign, but images are loading slowly asynchronously. - // We'll try to carry on anyway if there is no datum. There just - // might be a delay before user sees detections, captured_at, etc. - d = _mlyCache.images.forImageKey[imageKey]; - } - - _mlySelectedImage = d; - var viewer = d3_select('#photoviewer'); - if (!viewer.empty()) viewer.datum(d); - - imageKey = (d && d.key) || imageKey; - if (!fromViewer && imageKey) { - _mlyClicks.push(imageKey); - } - - this.setStyles(null, true); - - d3_selectAll('.layer-mapillary-signs .icon-sign') - .classed('selected', function(d) { - return _some(d.detections, function(detection) { - return detection.image_key === imageKey; - }); - }); - - if (d) { - this.updateDetections(d); - } - - return this; - }, - - - getSelectedImage: function() { - return _mlySelectedImage; - }, - - - getSequenceKeyForImage: function(d) { - var imageKey = d && d.key; - return imageKey && _mlyCache.sequences.forImageKey[imageKey]; - }, - - - setStyles: function(hovered, reset) { - if (reset) { // reset all layers - d3_selectAll('.viewfield-group') - .classed('highlighted', false) - .classed('hovered', false) - .classed('selected', false); - - d3_selectAll('.sequence') - .classed('highlighted', false) - .classed('selected', false); - } - - var hoveredImageKey = hovered && hovered.key; - var hoveredSequenceKey = this.getSequenceKeyForImage(hovered); - var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey]; - var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || []; - - var viewer = d3_select('#photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - var selectedImageKey = selected && selected.key; - var selectedSequenceKey = this.getSequenceKeyForImage(selected); - var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey]; - var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || []; - - // highlight sibling viewfields on either the selected or the hovered sequences - var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys); - - d3_selectAll('.layer-mapillary-images .viewfield-group') - .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) - .classed('hovered', function(d) { return d.key === hoveredImageKey; }) - .classed('selected', function(d) { return d.key === selectedImageKey; }); - - d3_selectAll('.layer-mapillary-images .sequence') - .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) - .classed('selected', function(d) { return d.properties.key === selectedSequenceKey; }); - - // update viewfields if needed - d3_selectAll('.viewfield-group .viewfield') - .attr('d', viewfieldPath); - - function viewfieldPath() { - var d = this.parentNode.__data__; - if (d.pano && d.key !== selectedImageKey) { - return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; - } else { - return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; - } - } - - return this; - }, - - - updateDetections: function(d) { - if (!_mlyViewer || _mlyFallback) return; - - var imageKey = d && d.key; - var detections = (imageKey && _mlyCache.detections[imageKey]) || []; - - _forEach(detections, function(data, k) { - if (_isEmpty(data)) { - loadDetection(k); - } else { - var tag = makeTag(data); - if (tag) { - var tagComponent = _mlyViewer.getComponent('tag'); - tagComponent.add([tag]); - } - } - }); - - - function loadDetection(detectionKey) { - var url = apibase + 'image_detections/' + - detectionKey + '?' + utilQsString({ client_id: clientId }); - - d3_request(url) - .mimeType('application/json') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { - if (!data || !data.properties) return; - - var imageKey = data.properties.image_key; - _mlyCache.detections[imageKey][detectionKey] = data; - - var selectedKey = _mlySelectedImage && _mlySelectedImage.key; - if (imageKey === selectedKey) { - var tag = makeTag(data); - if (tag) { - var tagComponent = _mlyViewer.getComponent('tag'); - tagComponent.add([tag]); - } - } - }); - } - - - function makeTag(data) { - var valueParts = data.properties.value.split('--'); - if (valueParts.length !== 3) return; - - var text = valueParts[1].replace(/-/g, ' '); - var tag; - - // Currently only two shapes - if (data.properties.shape.type === 'Polygon') { - var polygonGeometry = new Mapillary - .TagComponent - .PolygonGeometry(data.properties.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.OutlineTag( - data.properties.key, - polygonGeometry, - { - text: text, - textColor: 0xffff00, - lineColor: 0xffff00, - lineWidth: 2, - fillColor: 0xffff00, - fillOpacity: 0.3, - } - ); - - } else if (data.properties.shape.type === 'Point') { - var pointGeometry = new Mapillary - .TagComponent - .PointGeometry(data.properties.shape.coordinates[0]); - - tag = new Mapillary.TagComponent.SpotTag( - data.properties.key, - pointGeometry, - { - text: text, - color: 0xffff00, - textColor: 0xffff00 - } - ); - } - - return tag; - } - }, - - - cache: function() { - return _mlyCache; - } - -}; +/* global Mapillary:false */ +import _find from 'lodash-es/find'; +import _flatten from 'lodash-es/flatten'; +import _forEach from 'lodash-es/forEach'; +import _isEmpty from 'lodash-es/isEmpty'; +import _map from 'lodash-es/map'; +import _some from 'lodash-es/some'; +import _union from 'lodash-es/union'; + +import { range as d3_range } from 'd3-array'; +import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { request as d3_request } from 'd3-request'; +import { + select as d3_select, + selectAll as d3_selectAll +} from 'd3-selection'; + +import rbush from 'rbush'; + +import { geoExtent, geoScaleToZoom } from '../geo'; +import { svgDefs } from '../svg'; +import { utilQsString, utilRebind, utilTiler } from '../util'; + + +var apibase = 'https://a.mapillary.com/v3/'; +var viewercss = 'mapillary-js/mapillary.min.css'; +var viewerjs = 'mapillary-js/mapillary.min.js'; +var clientId = 'NzNRM2otQkR2SHJzaXJmNmdQWVQ0dzo1ZWYyMmYwNjdmNDdlNmVi'; +var maxResults = 1000; +var tileZoom = 14; +var tiler = utilTiler().zoomExtent([tileZoom, tileZoom]).skipNullIsland(true); +var dispatch = d3_dispatch('loadedImages', 'loadedSigns', 'bearingChanged'); +var _mlyFallback = false; +var _mlyCache; +var _mlyClicks; +var _mlySelectedImage; +var _mlyViewer; + + +function abortRequest(i) { + i.abort(); +} + + +function maxPageAtZoom(z) { + if (z < 15) return 2; + if (z === 15) return 5; + if (z === 16) return 10; + if (z === 17) return 20; + if (z === 18) return 40; + if (z > 18) return 80; +} + + +function loadTiles(which, url, projection) { + var currZoom = Math.floor(geoScaleToZoom(projection.scale())); + var tiles = tiler.getTiles(projection); + + // abort inflight requests that are no longer needed + var cache = _mlyCache[which]; + _forEach(cache.inflight, function(v, k) { + var wanted = _find(tiles, function(tile) { return k.indexOf(tile.id + ',') === 0; }); + + if (!wanted) { + abortRequest(v); + delete cache.inflight[k]; + } + }); + + tiles.forEach(function(tile) { + loadNextTilePage(which, currZoom, url, tile); + }); +} + + +function loadNextTilePage(which, currZoom, url, tile) { + var cache = _mlyCache[which]; + var rect = tile.extent.rectangle(); + var maxPages = maxPageAtZoom(currZoom); + var nextPage = cache.nextPage[tile.id] || 0; + var nextURL = cache.nextURL[tile.id] || url + + utilQsString({ + per_page: maxResults, + page: nextPage, + client_id: clientId, + bbox: [rect[0], rect[1], rect[2], rect[3]].join(','), + }); + + if (nextPage > maxPages) return; + + var id = tile.id + ',' + String(nextPage); + if (cache.loaded[id] || cache.inflight[id]) return; + cache.inflight[id] = d3_request(nextURL) + .mimeType('application/json') + .response(function(xhr) { + var linkHeader = xhr.getResponseHeader('Link'); + if (linkHeader) { + var pagination = parsePagination(xhr.getResponseHeader('Link')); + if (pagination.next) { + cache.nextURL[tile.id] = pagination.next; + } + } + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + cache.loaded[id] = true; + delete cache.inflight[id]; + if (err || !data.features || !data.features.length) return; + + var features = data.features.map(function(feature) { + var loc = feature.geometry.coordinates; + var d; + + if (which === 'images') { + d = { + loc: loc, + key: feature.properties.key, + ca: feature.properties.ca, + captured_at: feature.properties.captured_at, + captured_by: feature.properties.username, + pano: feature.properties.pano + }; + cache.forImageKey[d.key] = d; // cache imageKey -> image + + } else if (which === 'sequences') { + var sequenceKey = feature.properties.key; + cache.lineString[sequenceKey] = feature; // cache sequenceKey -> lineString + feature.properties.coordinateProperties.image_keys.forEach(function(imageKey) { + cache.forImageKey[imageKey] = sequenceKey; // cache imageKey -> sequenceKey + }); + return false; // because no `d` data worth loading into an rbush + + } else if (which === 'objects') { + d = { + loc: loc, + key: feature.properties.key, + value: feature.properties.value, + package: feature.properties.package, + detections: feature.properties.detections + }; + + // cache imageKey -> detectionKey + feature.properties.detections.forEach(function(detection) { + var imageKey = detection.image_key; + var detectionKey = detection.detection_key; + if (!_mlyCache.detections[imageKey]) { + _mlyCache.detections[imageKey] = {}; + } + if (!_mlyCache.detections[imageKey][detectionKey]) { + _mlyCache.detections[imageKey][detectionKey] = {}; + } + }); + } + + return { + minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d + }; + + }).filter(Boolean); + + cache.rtree.load(features); + + if (which === 'images' || which === 'sequences') { + dispatch.call('loadedImages'); + } else if (which === 'objects') { + dispatch.call('loadedSigns'); + } + + if (data.features.length === maxResults) { // more pages to load + cache.nextPage[tile.id] = nextPage + 1; + loadNextTilePage(which, currZoom, url, tile); + } else { + cache.nextPage[tile.id] = Infinity; // no more pages to load + } + }); +} + +// extract links to pages of API results +function parsePagination(links) { + return links.split(',').map(function(rel) { + var elements = rel.split(';'); + if (elements.length === 2) { + return [ + /<(.+)>/.exec(elements[0])[1], + /rel="(.+)"/.exec(elements[1])[1] + ]; + } else { + return ['','']; + } + }).reduce(function(pagination, val) { + pagination[val[1]] = val[0]; + return pagination; + }, {}); +} + + +// partition viewport into `psize` x `psize` regions +function partitionViewport(psize, projection) { + var dimensions = projection.clipExtent()[1]; + psize = psize || 16; + var cols = d3_range(0, dimensions[0], psize); + var rows = d3_range(0, dimensions[1], psize); + var partitions = []; + + rows.forEach(function(y) { + cols.forEach(function(x) { + var min = [x, y + psize]; + var max = [x + psize, y]; + partitions.push( + geoExtent(projection.invert(min), projection.invert(max))); + }); + }); + + return partitions; +} + + +// no more than `limit` results per partition. +function searchLimited(psize, limit, projection, rtree) { + limit = limit || 3; + + var partitions = partitionViewport(psize, projection); + var results; + + // console.time('previous'); + results = _flatten(_map(partitions, function(extent) { + return rtree.search(extent.bbox()) + .slice(0, limit) + .map(function(d) { return d.data; }); + })); + // console.timeEnd('previous'); + + // console.time('new'); + // results = partitions.reduce(function(result, extent) { + // var found = rtree.search(extent.bbox()) + // .map(function(d) { return d.data; }) + // .sort(function(a, b) { + // return a.loc[1] - b.loc[1]; + // // return a.key.localeCompare(b.key); + // }) + // .slice(0, limit); + + // return (found.length ? result.concat(found) : result); + // }, []); + // console.timeEnd('new'); + + return results; +} + + + +export default { + + init: function() { + if (!_mlyCache) { + this.reset(); + } + + this.event = utilRebind(this, dispatch, 'on'); + }, + + reset: function() { + var cache = _mlyCache; + + if (cache) { + if (cache.images && cache.images.inflight) { + _forEach(cache.images.inflight, abortRequest); + } + if (cache.objects && cache.objects.inflight) { + _forEach(cache.objects.inflight, abortRequest); + } + if (cache.sequences && cache.sequences.inflight) { + _forEach(cache.sequences.inflight, abortRequest); + } + } + + _mlyCache = { + images: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {} }, + objects: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush() }, + sequences: { inflight: {}, loaded: {}, nextPage: {}, nextURL: {}, rtree: rbush(), forImageKey: {}, lineString: {} }, + detections: {} + }; + + _mlySelectedImage = null; + _mlyClicks = []; + }, + + + images: function(projection) { + var psize = 16, limit = 3; + return searchLimited(psize, limit, projection, _mlyCache.images.rtree); + }, + + + signs: function(projection) { + var psize = 32, limit = 3; + return searchLimited(psize, limit, projection, _mlyCache.objects.rtree); + }, + + + sequences: 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(); + var sequenceKeys = {}; + + // all sequences for images in viewport + _mlyCache.images.rtree.search(bbox) + .forEach(function(d) { + var sequenceKey = _mlyCache.sequences.forImageKey[d.data.key]; + if (sequenceKey) { + sequenceKeys[sequenceKey] = true; + } + }); + + // Return lineStrings for the sequences + return Object.keys(sequenceKeys).map(function(sequenceKey) { + return _mlyCache.sequences.lineString[sequenceKey]; + }); + }, + + + signsSupported: function() { + return true; + }, + + + loadImages: function(projection) { + loadTiles('images', apibase + 'images?', projection); + loadTiles('sequences', apibase + 'sequences?', projection); + }, + + + loadSigns: function(context, projection) { + // if we are looking at signs, we'll actually need to fetch images too + loadTiles('images', apibase + 'images?', projection); + loadTiles('objects', apibase + 'map_features?layers=trafficsigns&', projection); + }, + + + loadViewer: function(context) { + // add mly-wrapper + var wrap = d3_select('#photoviewer').selectAll('.mly-wrapper') + .data([0]); + + wrap.enter() + .append('div') + .attr('id', 'mly') + .attr('class', 'photo-wrapper mly-wrapper') + .classed('hide', true); + + // load mapillary-viewercss + d3_select('head').selectAll('#mapillary-viewercss') + .data([0]) + .enter() + .append('link') + .attr('id', 'mapillary-viewercss') + .attr('rel', 'stylesheet') + .attr('href', context.asset(viewercss)); + + // load mapillary-viewerjs + d3_select('head').selectAll('#mapillary-viewerjs') + .data([0]) + .enter() + .append('script') + .attr('id', 'mapillary-viewerjs') + .attr('src', context.asset(viewerjs)); + + // load mapillary signs sprite + var defs = context.container().select('defs'); + defs.call(svgDefs(context).addSprites, ['mapillary-sprite']); + + // Register viewer resize handler + context.ui().on('photoviewerResize', function() { + if (_mlyViewer) { + _mlyViewer.resize(); + } + }); + }, + + + showViewer: function() { + var wrap = d3_select('#photoviewer') + .classed('hide', false); + + var isHidden = wrap.selectAll('.photo-wrapper.mly-wrapper.hide').size(); + + if (isHidden && _mlyViewer) { + wrap + .selectAll('.photo-wrapper:not(.mly-wrapper)') + .classed('hide', true); + + wrap + .selectAll('.photo-wrapper.mly-wrapper') + .classed('hide', false); + + _mlyViewer.resize(); + } + + return this; + }, + + + hideViewer: function() { + _mlySelectedImage = null; + + if (!_mlyFallback && _mlyViewer) { + _mlyViewer.getComponent('sequence').stop(); + } + + var viewer = d3_select('#photoviewer'); + if (!viewer.empty()) viewer.datum(null); + + viewer + .classed('hide', true) + .selectAll('.photo-wrapper') + .classed('hide', true); + + d3_selectAll('.viewfield-group, .sequence, .icon-sign') + .classed('selected', false); + + return this.setStyles(null, true); + }, + + + parsePagination: parsePagination, + + + updateViewer: function(imageKey, context) { + if (!imageKey) return this; + + if (!_mlyViewer) { + this.initViewer(imageKey, context); + } else { + _mlyViewer.moveToKey(imageKey) + .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console + } + + return this; + }, + + + initViewer: function(imageKey, context) { + var that = this; + if (window.Mapillary && imageKey) { + var opts = { + baseImageSize: 320, + component: { + cover: false, + keyboard: false, + tag: true + } + }; + + // Disable components requiring WebGL support + if (!Mapillary.isSupported() && Mapillary.isFallbackSupported()) { + _mlyFallback = true; + opts.component = { + cover: false, + direction: false, + imagePlane: false, + keyboard: false, + mouse: false, + sequence: false, + tag: false, + image: true, // fallback + navigation: true // fallback + }; + } + + _mlyViewer = new Mapillary.Viewer('mly', clientId, null, opts); + _mlyViewer.on('nodechanged', nodeChanged); + _mlyViewer.on('bearingchanged', bearingChanged); + _mlyViewer.moveToKey(imageKey) + .catch(function(e) { console.error('mly3', e); }); // eslint-disable-line no-console + } + + // nodeChanged: called after the viewer has changed images and is ready. + // + // There is some logic here to batch up clicks into a _mlyClicks array + // because the user might click on a lot of markers quickly and nodechanged + // may be called out of order asychronously. + // + // Clicks are added to the array in `selectedImage` and removed here. + // + function nodeChanged(node) { + if (!_mlyFallback) { + _mlyViewer.getComponent('tag').removeAll(); // remove previous detections + } + + var clicks = _mlyClicks; + var index = clicks.indexOf(node.key); + var selectedKey = _mlySelectedImage && _mlySelectedImage.key; + + if (index > -1) { // `nodechanged` initiated from clicking on a marker.. + clicks.splice(index, 1); // remove the click + // If `node.key` matches the current _mlySelectedImage, call `selectImage()` + // one more time to update the detections and attribution.. + if (node.key === selectedKey) { + that.selectImage(_mlySelectedImage, node.key, true); + } + } else { // `nodechanged` initiated from the Mapillary viewer controls.. + var loc = node.computedLatLon ? [node.computedLatLon.lon, node.computedLatLon.lat] : [node.latLon.lon, node.latLon.lat]; + context.map().centerEase(loc); + that.selectImage(undefined, node.key, true); + } + } + + function bearingChanged(e) { + dispatch.call('bearingChanged', undefined, e); + } + }, + + + // Pass the image datum itself in `d` or the `imageKey` string. + // This allows images to be selected from places that dont have access + // to the full image datum (like the street signs layer or the js viewer) + selectImage: function(d, imageKey, fromViewer) { + if (!d && imageKey) { + // If the user clicked on something that's not an image marker, we + // might get in here.. Cache lookup can fail, e.g. if the user + // clicked a streetsign, but images are loading slowly asynchronously. + // We'll try to carry on anyway if there is no datum. There just + // might be a delay before user sees detections, captured_at, etc. + d = _mlyCache.images.forImageKey[imageKey]; + } + + _mlySelectedImage = d; + var viewer = d3_select('#photoviewer'); + if (!viewer.empty()) viewer.datum(d); + + imageKey = (d && d.key) || imageKey; + if (!fromViewer && imageKey) { + _mlyClicks.push(imageKey); + } + + this.setStyles(null, true); + + d3_selectAll('.layer-mapillary-signs .icon-sign') + .classed('selected', function(d) { + return _some(d.detections, function(detection) { + return detection.image_key === imageKey; + }); + }); + + if (d) { + this.updateDetections(d); + } + + return this; + }, + + + getSelectedImage: function() { + return _mlySelectedImage; + }, + + + getSequenceKeyForImage: function(d) { + var imageKey = d && d.key; + return imageKey && _mlyCache.sequences.forImageKey[imageKey]; + }, + + + setStyles: function(hovered, reset) { + if (reset) { // reset all layers + d3_selectAll('.viewfield-group') + .classed('highlighted', false) + .classed('hovered', false) + .classed('selected', false); + + d3_selectAll('.sequence') + .classed('highlighted', false) + .classed('selected', false); + } + + var hoveredImageKey = hovered && hovered.key; + var hoveredSequenceKey = this.getSequenceKeyForImage(hovered); + var hoveredLineString = hoveredSequenceKey && _mlyCache.sequences.lineString[hoveredSequenceKey]; + var hoveredImageKeys = (hoveredLineString && hoveredLineString.properties.coordinateProperties.image_keys) || []; + + var viewer = d3_select('#photoviewer'); + var selected = viewer.empty() ? undefined : viewer.datum(); + var selectedImageKey = selected && selected.key; + var selectedSequenceKey = this.getSequenceKeyForImage(selected); + var selectedLineString = selectedSequenceKey && _mlyCache.sequences.lineString[selectedSequenceKey]; + var selectedImageKeys = (selectedLineString && selectedLineString.properties.coordinateProperties.image_keys) || []; + + // highlight sibling viewfields on either the selected or the hovered sequences + var highlightedImageKeys = _union(hoveredImageKeys, selectedImageKeys); + + d3_selectAll('.layer-mapillary-images .viewfield-group') + .classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; }) + .classed('hovered', function(d) { return d.key === hoveredImageKey; }) + .classed('selected', function(d) { return d.key === selectedImageKey; }); + + d3_selectAll('.layer-mapillary-images .sequence') + .classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; }) + .classed('selected', function(d) { return d.properties.key === selectedSequenceKey; }); + + // update viewfields if needed + d3_selectAll('.viewfield-group .viewfield') + .attr('d', viewfieldPath); + + function viewfieldPath() { + var d = this.parentNode.__data__; + if (d.pano && d.key !== selectedImageKey) { + return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0'; + } else { + return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z'; + } + } + + return this; + }, + + + updateDetections: function(d) { + if (!_mlyViewer || _mlyFallback) return; + + var imageKey = d && d.key; + var detections = (imageKey && _mlyCache.detections[imageKey]) || []; + + _forEach(detections, function(data, k) { + if (_isEmpty(data)) { + loadDetection(k); + } else { + var tag = makeTag(data); + if (tag) { + var tagComponent = _mlyViewer.getComponent('tag'); + tagComponent.add([tag]); + } + } + }); + + + function loadDetection(detectionKey) { + var url = apibase + 'image_detections/' + + detectionKey + '?' + utilQsString({ client_id: clientId }); + + d3_request(url) + .mimeType('application/json') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + if (!data || !data.properties) return; + + var imageKey = data.properties.image_key; + _mlyCache.detections[imageKey][detectionKey] = data; + + var selectedKey = _mlySelectedImage && _mlySelectedImage.key; + if (imageKey === selectedKey) { + var tag = makeTag(data); + if (tag) { + var tagComponent = _mlyViewer.getComponent('tag'); + tagComponent.add([tag]); + } + } + }); + } + + + function makeTag(data) { + var valueParts = data.properties.value.split('--'); + if (valueParts.length !== 3) return; + + var text = valueParts[1].replace(/-/g, ' '); + var tag; + + // Currently only two shapes + if (data.properties.shape.type === 'Polygon') { + var polygonGeometry = new Mapillary + .TagComponent + .PolygonGeometry(data.properties.shape.coordinates[0]); + + tag = new Mapillary.TagComponent.OutlineTag( + data.properties.key, + polygonGeometry, + { + text: text, + textColor: 0xffff00, + lineColor: 0xffff00, + lineWidth: 2, + fillColor: 0xffff00, + fillOpacity: 0.3, + } + ); + + } else if (data.properties.shape.type === 'Point') { + var pointGeometry = new Mapillary + .TagComponent + .PointGeometry(data.properties.shape.coordinates[0]); + + tag = new Mapillary.TagComponent.SpotTag( + data.properties.key, + pointGeometry, + { + text: text, + color: 0xffff00, + textColor: 0xffff00 + } + ); + } + + return tag; + } + }, + + + cache: function() { + return _mlyCache; + } + +}; diff --git a/test/spec/services/mapillary.js b/test/spec/services/mapillary.js index 72046b1b6..eaadd6ba4 100644 --- a/test/spec/services/mapillary.js +++ b/test/spec/services/mapillary.js @@ -1,6 +1,6 @@ describe('iD.serviceMapillary', function() { - var dimensions = [64, 64], - context, server, mapillary; + var dimensions = [64, 64]; + var context, server, mapillary; before(function() { @@ -59,13 +59,13 @@ describe('iD.serviceMapillary', function() { mapillary.on('loadedImages', spy); mapillary.loadImages(context.projection); - var match = /images/, - features = [{ - type: 'Feature', - geometry: { type: 'Point', coordinates: [10,0] }, - properties: { ca: 90, key: '0' } - }], - response = { type: 'FeatureCollection', features: features }; + var match = /images/; + var features = [{ + type: 'Feature', + geometry: { type: 'Point', coordinates: [10,0] }, + properties: { ca: 90, key: '0' } + }]; + var response = { type: 'FeatureCollection', features: features }; server.respondWith('GET', match, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ]); @@ -80,13 +80,13 @@ describe('iD.serviceMapillary', function() { mapillary.on('loadedImages', spy); mapillary.loadImages(context.projection); - var match = /images/, - features = [{ - type: 'Feature', - geometry: { type: 'Point', coordinates: [0,0] }, - properties: { ca: 90, key: '0' } - }], - response = { type: 'FeatureCollection', features: features }; + var match = /images/; + var features = [{ + type: 'Feature', + geometry: { type: 'Point', coordinates: [0,0] }, + properties: { ca: 90, key: '0' } + }]; + var response = { type: 'FeatureCollection', features: features }; server.respondWith('GET', match, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ]); @@ -100,9 +100,9 @@ describe('iD.serviceMapillary', function() { mapillary.on('loadedImages', spy); mapillary.loadImages(context.projection); - var features0 = [], - features1 = [], - i; + var features0 = []; + var features1 = []; + var i; for (i = 0; i < 1000; i++) { features0.push({ @@ -119,10 +119,10 @@ describe('iD.serviceMapillary', function() { }); } - var match0 = /page=0/, - response0 = { type: 'FeatureCollection', features: features0 }, - match1 = /page=1/, - response1 = { type: 'FeatureCollection', features: features1 }; + var match0 = /page=0/; + var response0 = { type: 'FeatureCollection', features: features0 }; + var match1 = /page=1/; + var response1 = { type: 'FeatureCollection', features: features1 }; server.respondWith('GET', match0, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response0) ]); @@ -140,17 +140,17 @@ describe('iD.serviceMapillary', function() { mapillary.on('loadedSigns', spy); mapillary.loadSigns(context, context.projection); - var match = /objects/, - detections = [{ - detection_key: '0', - image_key: '0' - }], - features = [{ - type: 'Feature', - geometry: { type: 'Point', coordinates: [10,0] }, - properties: { detections: detections, key: '0', value: 'not-in-set' } - }], - response = { type: 'FeatureCollection', features: features }; + var match = /map_features/; + var detections = [{ + detection_key: '0', + image_key: '0' + }]; + var features = [{ + type: 'Feature', + geometry: { type: 'Point', coordinates: [10,0] }, + properties: { detections: detections, key: '0', value: 'not-in-set' } + }]; + var response = { type: 'FeatureCollection', features: features }; server.respondWith('GET', match, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ]); @@ -165,17 +165,17 @@ describe('iD.serviceMapillary', function() { mapillary.on('loadedSigns', spy); mapillary.loadSigns(context, context.projection); - var match = /objects/, - detections = [{ - detection_key: '0', - image_key: '0' - }], - features = [{ - type: 'Feature', - geometry: { type: 'Point', coordinates: [0,0] }, - properties: { detections: detections, key: '0', value: 'not-in-set' } - }], - response = { type: 'FeatureCollection', features: features }; + var match = /map_features/; + var detections = [{ + detection_key: '0', + image_key: '0' + }]; + var features = [{ + type: 'Feature', + geometry: { type: 'Point', coordinates: [0,0] }, + properties: { detections: detections, key: '0', value: 'not-in-set' } + }]; + var response = { type: 'FeatureCollection', features: features }; server.respondWith('GET', match, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ]); @@ -190,15 +190,15 @@ describe('iD.serviceMapillary', function() { mapillary.loadSigns(context, context.projection); var rects = [{ - package: 'trafficsign', - rect: [ 0.805, 0.463, 0.833, 0.502 ], - length: 4, - score: '1.27', - type: 'regulatory--maximum-speed-limit-65--us' - }], - features0 = [], - features1 = [], - i; + package: 'trafficsign', + rect: [ 0.805, 0.463, 0.833, 0.502 ], + length: 4, + score: '1.27', + type: 'regulatory--maximum-speed-limit-65--us' + }]; + var features0 = []; + var features1 = []; + var i; for (i = 0; i < 1000; i++) { features0.push({ @@ -215,10 +215,10 @@ describe('iD.serviceMapillary', function() { }); } - var match0 = /page=0/, - response0 = { type: 'FeatureCollection', features: features0 }, - match1 = /page=1/, - response1 = { type: 'FeatureCollection', features: features1 }; + var match0 = /page=0/; + var response0 = { type: 'FeatureCollection', features: features0 }; + var match1 = /page=1/; + var response1 = { type: 'FeatureCollection', features: features1 }; server.respondWith('GET', match0, [200, { 'Content-Type': 'application/json' }, JSON.stringify(response0) ]); @@ -266,14 +266,14 @@ describe('iD.serviceMapillary', function() { describe('#signs', function() { it('returns signs in the visible map area', function() { var detections = [{ - detection_key: '78vqha63gs1upg15s823qckcmn', - image_key: 'bwYs-uXLDvm_meo_EC5Nzw' - }], - features = [ - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } }, - { minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], detections: detections } } - ]; + detection_key: '78vqha63gs1upg15s823qckcmn', + image_key: 'bwYs-uXLDvm_meo_EC5Nzw' + }]; + var features = [ + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } }, + { minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], detections: detections } } + ]; mapillary.cache().objects.rtree.load(features); var res = mapillary.signs(context.projection); @@ -286,16 +286,16 @@ describe('iD.serviceMapillary', function() { it('limits results no more than 3 stacked signs in one spot', function() { var detections = [{ - detection_key: '78vqha63gs1upg15s823qckcmn', - image_key: 'bwYs-uXLDvm_meo_EC5Nzw' - }], - features = [ - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], detections: detections } }, - { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } } - ]; + detection_key: '78vqha63gs1upg15s823qckcmn', + image_key: 'bwYs-uXLDvm_meo_EC5Nzw' + }]; + var features = [ + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], detections: detections } }, + { minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], detections: detections } } + ]; mapillary.cache().objects.rtree.load(features); var res = mapillary.signs(context.projection); From 51b2335b968beb0ed2cadcb0439edd1041f08962 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 11 Oct 2018 19:04:01 -0700 Subject: [PATCH 26/51] Corrects CSS for focused preset list item button Fixes arrow key navigation for right-to-left layouts --- css/80_app.css | 3 ++- modules/ui/preset_list.js | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 427022792..a8e255a0e 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1129,7 +1129,8 @@ a.hide-toggle { border-radius: 3px 0 0 3px; } -.preset-list-button:hover .label { +.preset-list-button:hover .label, +.preset-list-button:focus .label { background-color: #ececec; } diff --git a/modules/ui/preset_list.js b/modules/ui/preset_list.js index 2d83c045e..1242e2fe2 100644 --- a/modules/ui/preset_list.js +++ b/modules/ui/preset_list.js @@ -235,7 +235,7 @@ export function uiPresetList(context) { } } // arrow left, move focus to the parent item if there is one - else if (d3_event.keyCode === d3_keybinding.keyCodes['←']) { + else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { d3_event.preventDefault(); d3_event.stopPropagation(); @@ -246,7 +246,7 @@ export function uiPresetList(context) { } } // arrow right, choose this item - else if (d3_event.keyCode === d3_keybinding.keyCodes['→']) { + else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { d3_event.preventDefault(); d3_event.stopPropagation(); item.datum().choose(); @@ -282,7 +282,7 @@ export function uiPresetList(context) { .on('click', click) .on('keydown', function() { // right arrow, expand the focused item - if (d3_event.keyCode === d3_keybinding.keyCodes['→']) { + if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '←' : '→']) { d3_event.preventDefault(); d3_event.stopPropagation(); // if the item isn't expanded @@ -291,7 +291,7 @@ export function uiPresetList(context) { click.call(this); } // left arrow, collapse the focused item - } else if (d3_event.keyCode === d3_keybinding.keyCodes['←']) { + } else if (d3_event.keyCode === d3_keybinding.keyCodes[(textDirection === 'rtl') ? '→' : '←']) { d3_event.preventDefault(); d3_event.stopPropagation(); // if the item is expanded From 494ea13aac336f810c671b040a95744270fe3e0a Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 11 Oct 2018 19:32:24 -0700 Subject: [PATCH 27/51] Adds the member type (node, way, or relation) to the label of non-downloaded relation members --- modules/ui/raw_member_editor.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index dc5f56053..498588e46 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -141,7 +141,14 @@ export function uiRawMemberEditor(context) { } else { var incompleteLabel = d3_select(this).append('label') - .attr('class', 'form-label') + .attr('class', 'form-label'); + + incompleteLabel.append('span') + .attr('class', 'member-entity-type') + .text(t('inspector.'+d.type, { id: d.id })); + + incompleteLabel.append('span') + .attr('class', 'member-entity-name') .text(t('inspector.incomplete', { id: d.id })); var wrap = incompleteLabel.append('div') From b7192d65c09f3a2e72bd2ac1d158504555459d9c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 12 Oct 2018 16:08:50 -0400 Subject: [PATCH 28/51] Add "Garage" to the "Building Features" category (closes #5375) --- data/presets/categories.json | 1 + data/presets/categories/building.json | 1 + 2 files changed, 2 insertions(+) diff --git a/data/presets/categories.json b/data/presets/categories.json index 60309de77..c1a7c16a0 100644 --- a/data/presets/categories.json +++ b/data/presets/categories.json @@ -22,6 +22,7 @@ "building", "building/house", "building/apartments", + "building/garage", "building/retail", "building/commercial", "building/industrial", diff --git a/data/presets/categories/building.json b/data/presets/categories/building.json index a85736bb8..05a116721 100644 --- a/data/presets/categories/building.json +++ b/data/presets/categories/building.json @@ -6,6 +6,7 @@ "building", "building/house", "building/apartments", + "building/garage", "building/retail", "building/commercial", "building/industrial", From e98a10c990f10d88735c5eca7c05add22407f0fb Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 12 Oct 2018 16:28:44 -0400 Subject: [PATCH 29/51] Switch parking icons from "P" to car picture (closes #5341) --- data/presets.yaml | 7 ++-- data/presets/presets.json | 4 +- data/presets/presets/amenity/parking.json | 9 +++-- .../presets/amenity/parking/multi-storey.json | 37 +++++++++++++------ data/taginfo.json | 6 +-- dist/locales/en.json | 6 +-- 6 files changed, 43 insertions(+), 26 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index bcade88e0..c29bf6393 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -2447,12 +2447,13 @@ en: name: Nursing Home amenity/parking: # amenity=parking - name: Car Parking - terms: '' + name: Parking Lot + # 'terms: car,parking lot' + terms: '' amenity/parking/multi-storey: # 'amenity=parking, parking=multi-storey' name: Multilevel Car Parking - # 'terms: multistorey car park,parking garage,parkade,parking structure,parking ramp,parking deck,parking building,indoor parking' + # 'terms: car,indoor parking,multistorey car park,parkade,parking building,parking deck,parking garage,parking ramp,parking structure' terms: '' amenity/parking_entrance: # amenity=parking_entrance diff --git a/data/presets/presets.json b/data/presets/presets.json index b2f94a36f..18d2d2afc 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -104,8 +104,8 @@ "amenity/nightclub": {"icon": "maki-bar", "fields": ["name", "operator", "address", "building_area", "opening_hours", "smoking"], "geometry": ["point", "area"], "tags": {"amenity": "nightclub"}, "terms": ["disco*", "night club", "dancing", "dance club"], "name": "Nightclub"}, "amenity/parking_entrance": {"icon": "maki-entrance-alt1", "fields": ["access_simple", "ref"], "geometry": ["vertex"], "tags": {"amenity": "parking_entrance"}, "name": "Parking Garage Entrance/Exit"}, "amenity/parking_space": {"fields": ["capacity"], "geometry": ["point", "vertex", "area"], "terms": [], "tags": {"amenity": "parking_space"}, "matchScore": 0.95, "name": "Parking Space"}, - "amenity/parking": {"icon": "maki-parking", "fields": ["name", "operator", "parking", "capacity", "fee", "access_simple", "supervised", "park_ride", "surface", "maxstay"], "geometry": ["point", "vertex", "area"], "tags": {"amenity": "parking"}, "terms": [], "name": "Car Parking"}, - "amenity/parking/multi-storey": {"icon": "maki-parking-garage", "fields": ["name", "building", "levels", "height", "address"], "geometry": ["area"], "terms": ["multistorey car park", "parking garage", "parkade", "parking structure", "parking ramp", "parking deck", "parking building", "indoor parking"], "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"}, "name": "Multilevel Car Parking"}, + "amenity/parking": {"icon": "maki-car", "fields": ["name", "operator", "parking", "capacity", "fee", "access_simple", "supervised", "park_ride", "surface", "maxstay"], "geometry": ["point", "vertex", "area"], "tags": {"amenity": "parking"}, "terms": ["car", "parking lot"], "name": "Parking Lot"}, + "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 Car Parking"}, "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/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-buddhism", "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"}, diff --git a/data/presets/presets/amenity/parking.json b/data/presets/presets/amenity/parking.json index f26e20d78..df7f37137 100644 --- a/data/presets/presets/amenity/parking.json +++ b/data/presets/presets/amenity/parking.json @@ -1,5 +1,5 @@ { - "icon": "maki-parking", + "icon": "maki-car", "fields": [ "name", "operator", @@ -20,6 +20,9 @@ "tags": { "amenity": "parking" }, - "terms": [], - "name": "Car Parking" + "terms": [ + "car", + "parking lot" + ], + "name": "Parking Lot" } diff --git a/data/presets/presets/amenity/parking/multi-storey.json b/data/presets/presets/amenity/parking/multi-storey.json index 6e1fd256a..f443cb538 100644 --- a/data/presets/presets/amenity/parking/multi-storey.json +++ b/data/presets/presets/amenity/parking/multi-storey.json @@ -1,16 +1,18 @@ { - "icon": "maki-parking-garage", - "fields": ["name", "building", "levels", "height", "address"], - "geometry": ["area"], - "terms": [ - "multistorey car park", - "parking garage", - "parkade", - "parking structure", - "parking ramp", - "parking deck", - "parking building", - "indoor parking" + "icon": "maki-car", + "fields": [ + "name", + "operator", + "building", + "levels", + "height", + "address", + "capacity", + "fee", + "access_simple" + ], + "geometry": [ + "area" ], "tags": { "amenity": "parking", @@ -30,5 +32,16 @@ "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 Car Parking" } diff --git a/data/taginfo.json b/data/taginfo.json index 74d050641..f5227d705 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -703,16 +703,16 @@ { "key": "amenity", "value": "parking", - "description": "Car Parking", + "description": "Parking Lot", "object_types": ["node", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/parking-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true" }, { "key": "parking", "value": "multi-storey", "description": "Multilevel Car Parking, Type", "object_types": ["area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/parking-garage-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true" }, { "key": "amenity", diff --git a/dist/locales/en.json b/dist/locales/en.json index a8bc9eca8..39a1965ae 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -3568,12 +3568,12 @@ "terms": "" }, "amenity/parking": { - "name": "Car Parking", - "terms": "" + "name": "Parking Lot", + "terms": "car,parking lot" }, "amenity/parking/multi-storey": { "name": "Multilevel Car Parking", - "terms": "multistorey car park,parking garage,parkade,parking structure,parking ramp,parking deck,parking building,indoor parking" + "terms": "car,indoor parking,multistorey car park,parkade,parking building,parking deck,parking garage,parking ramp,parking structure" }, "amenity/pharmacy": { "name": "Pharmacy", From ac341cdf5aef1d19b1bc00fd6579027ea966ab91 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 12 Oct 2018 16:44:49 -0400 Subject: [PATCH 30/51] Usability changes for parking lots - Rename "Car Parking" to "Parking Lot" - Rename "Multilevel Car Parking" to "Multilevel Parking Garage" - Add red styling for Multilevel Parking Garage preset icon --- css/50_misc.css | 1 + data/presets.yaml | 4 ++-- data/presets/presets.json | 2 +- data/presets/presets/amenity/parking/multi-storey.json | 2 +- data/taginfo.json | 2 +- dist/locales/en.json | 2 +- modules/svg/tag_classes.js | 2 +- 7 files changed, 8 insertions(+), 7 deletions(-) diff --git a/css/50_misc.css b/css/50_misc.css index a2abc74aa..3ebbf2a5b 100644 --- a/css/50_misc.css +++ b/css/50_misc.css @@ -237,6 +237,7 @@ path.fill.tag-building { stroke: rgba(224, 110, 95, 0.3); fill: rgba(224, 110, 95, 0.3); } +.preset-icon-fill-area.tag-parking-multi-storey, .preset-icon-fill-area.tag-building { border-color: rgb(224, 110, 95); background-color: rgba(224, 110, 95, 0.3); diff --git a/data/presets.yaml b/data/presets.yaml index c29bf6393..3bc157065 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -2452,9 +2452,9 @@ en: terms: '' amenity/parking/multi-storey: # 'amenity=parking, parking=multi-storey' - name: Multilevel Car Parking + name: Multilevel Parking Garage # 'terms: car,indoor parking,multistorey car park,parkade,parking building,parking deck,parking garage,parking ramp,parking structure' - terms: '' + terms: '' amenity/parking_entrance: # amenity=parking_entrance name: Parking Garage Entrance/Exit diff --git a/data/presets/presets.json b/data/presets/presets.json index 18d2d2afc..9e2def04c 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -105,7 +105,7 @@ "amenity/parking_entrance": {"icon": "maki-entrance-alt1", "fields": ["access_simple", "ref"], "geometry": ["vertex"], "tags": {"amenity": "parking_entrance"}, "name": "Parking Garage Entrance/Exit"}, "amenity/parking_space": {"fields": ["capacity"], "geometry": ["point", "vertex", "area"], "terms": [], "tags": {"amenity": "parking_space"}, "matchScore": 0.95, "name": "Parking Space"}, "amenity/parking": {"icon": "maki-car", "fields": ["name", "operator", "parking", "capacity", "fee", "access_simple", "supervised", "park_ride", "surface", "maxstay"], "geometry": ["point", "vertex", "area"], "tags": {"amenity": "parking"}, "terms": ["car", "parking lot"], "name": "Parking Lot"}, - "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 Car Parking"}, + "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/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/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-buddhism", "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"}, diff --git a/data/presets/presets/amenity/parking/multi-storey.json b/data/presets/presets/amenity/parking/multi-storey.json index f443cb538..f8cd7008a 100644 --- a/data/presets/presets/amenity/parking/multi-storey.json +++ b/data/presets/presets/amenity/parking/multi-storey.json @@ -43,5 +43,5 @@ "parking ramp", "parking structure" ], - "name": "Multilevel Car Parking" + "name": "Multilevel Parking Garage" } diff --git a/data/taginfo.json b/data/taginfo.json index f5227d705..2f7b886cf 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -710,7 +710,7 @@ { "key": "parking", "value": "multi-storey", - "description": "Multilevel Car Parking, Type", + "description": "Multilevel Parking Garage, Type", "object_types": ["area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true" }, diff --git a/dist/locales/en.json b/dist/locales/en.json index 39a1965ae..c3d5d4fd2 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -3572,7 +3572,7 @@ "terms": "car,parking lot" }, "amenity/parking/multi-storey": { - "name": "Multilevel Car Parking", + "name": "Multilevel Parking Garage", "terms": "car,indoor parking,multistorey car park,parkade,parking building,parking deck,parking garage,parking ramp,parking structure" }, "amenity/pharmacy": { diff --git a/modules/svg/tag_classes.js b/modules/svg/tag_classes.js index 508c731a3..cffe9af30 100644 --- a/modules/svg/tag_classes.js +++ b/modules/svg/tag_classes.js @@ -15,7 +15,7 @@ export function svgTagClasses() { var secondaries = [ 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier', 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport', - 'public_transport', 'location' + 'public_transport', 'location', 'parking' ]; var tagClassRe = /^tag-/; var _tags = function(entity) { return entity.tags; }; From cf1b391cf0de6974b2009a0762e5cb24ba34f347 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 13 Oct 2018 18:53:04 -0700 Subject: [PATCH 31/51] Adds highlighting of relation members in the map when hovering on their list item --- modules/ui/raw_member_editor.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index 498588e46..f86fb403a 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -122,6 +122,16 @@ export function uiRawMemberEditor(context) { enter .each(function(d) { if (d.member) { + + // highlight the member feature in the map while hovering on the list item + var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; + d3_select(this).on('mouseover', function() { + context.surface().selectAll(selectorPrefix+d.id).classed('related', true); + }); + d3_select(this).on('mouseout', function() { + context.surface().selectAll(selectorPrefix+d.id).classed('related', false); + }); + var label = d3_select(this).append('label') .attr('class', 'form-label') .append('a') @@ -142,7 +152,7 @@ export function uiRawMemberEditor(context) { } else { var incompleteLabel = d3_select(this).append('label') .attr('class', 'form-label'); - + incompleteLabel.append('span') .attr('class', 'member-entity-type') .text(t('inspector.'+d.type, { id: d.id })); From b24256973f22cc6a3bb5cb22eb7271736bc991f5 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 13 Oct 2018 23:04:35 -0700 Subject: [PATCH 32/51] Makes relation members highlighted via hovering on list items more prominent --- css/20_map.css | 12 ++++++++++++ modules/ui/raw_member_editor.js | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index 13c72a12b..def29ff99 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -265,6 +265,18 @@ text.point { opacity: 0.8; } +/* Highlighting */ + +g.point.highlighted .shadow, +path.shadow.highlighted { + stroke-opacity: 0.95; + stroke: #7092ff; +} +g.vertex.highlighted .shadow { + stroke-width: 7; + stroke-opacity: 0.95; + stroke: #7092ff; +} /* Turn Restrictions */ diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index f86fb403a..86224afc6 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -126,10 +126,10 @@ export function uiRawMemberEditor(context) { // highlight the member feature in the map while hovering on the list item var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; d3_select(this).on('mouseover', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('related', true); + context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); }); d3_select(this).on('mouseout', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('related', false); + context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); }); var label = d3_select(this).append('label') From aeed2cc29f6527a2f8d73c84b6fbe603a506b66e Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 14 Oct 2018 18:31:30 -0700 Subject: [PATCH 33/51] Adds the same hover highlighting to the selected features list as the relation members list --- modules/ui/selection_list.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 150974962..6362a7cea 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -1,4 +1,7 @@ -import { event as d3_event } from 'd3-selection'; +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; import { t } from '../util/locale'; import { modeSelect } from '../modes'; @@ -64,6 +67,18 @@ export function uiSelectionList(context, selectedIDs) { .attr('class', 'feature-list-item') .on('click', selectEntity); + enter + .each(function(d) { + // highlight the feature in the map while hovering on the list item + var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; + d3_select(this).on('mouseover', function() { + context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + }); + d3_select(this).on('mouseout', function() { + context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); + }); + }); + var label = enter .append('button') .attr('class', 'label'); From ffe38c7975408207547255bacd5c7da682a1fc08 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 15 Oct 2018 20:41:45 -0700 Subject: [PATCH 34/51] Adds a button to the relation member list item for zooming to the feature without selecting it Adds "remove" tooltip to the delete relation member button --- css/80_app.css | 1 - data/core.yaml | 1 + dist/locales/en.json | 3 ++- modules/ui/raw_member_editor.js | 32 ++++++++++++++++++++++++++++---- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index a8e255a0e..ab1b8e156 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1253,7 +1253,6 @@ a.hide-toggle { top: 0; right: 0; height: 100%; - width: 100%; background: transparent; text-align: right; } diff --git a/data/core.yaml b/data/core.yaml index ff8df3ff6..55b846840 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -4,6 +4,7 @@ en: information: info remove: remove undo: undo + zoom_to: zoom to modes: add_area: title: Area diff --git a/dist/locales/en.json b/dist/locales/en.json index c3d5d4fd2..81e861d3c 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -4,7 +4,8 @@ "download": "download", "information": "info", "remove": "remove", - "undo": "undo" + "undo": "undo", + "zoom_to": "zoom to" }, "modes": { "add_area": { diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index 86224afc6..c510ec94d 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -30,6 +30,17 @@ export function uiRawMemberEditor(context) { context.loadEntity(d.id); } + function zoomToMember(d) { + d3_event.preventDefault(); + + var entity = context.entity(d.id); + context.map().zoomTo(entity); + + // highlight the feature in case it wasn't previously on-screen + var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; + context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + } + function selectMember(d) { d3_event.preventDefault(); @@ -133,22 +144,34 @@ export function uiRawMemberEditor(context) { }); var label = d3_select(this).append('label') - .attr('class', 'form-label') - .append('a') + .attr('class', 'form-label'); + + // add the button wrap beneath the label text + var buttonWrap = label.append('div') + .attr('class', 'form-label-button-wrap'); + + var labelLink = label.append('a') .attr('href', '#') .on('click', selectMember); - label.append('span') + labelLink.append('span') .attr('class', 'member-entity-type') .text(function(d) { var matched = context.presets().match(d.member, context.graph()); return (matched && matched.name()) || utilDisplayType(d.member.id); }); - label.append('span') + labelLink.append('span') .attr('class', 'member-entity-name') .text(function(d) { return utilDisplayName(d.member); }); + buttonWrap.append('button') + .attr('class', 'download-icon') + .attr('title', t('icons.zoom_to')) + .attr('tabindex', -1) + .call(svgIcon('#iD-icon-geolocate')) + .on('click', zoomToMember); + } else { var incompleteLabel = d3_select(this).append('label') .attr('class', 'form-label'); @@ -186,6 +209,7 @@ export function uiRawMemberEditor(context) { enter .append('button') .attr('tabindex', -1) + .attr('title', t('icons.remove')) .attr('class', 'remove button-input-action member-delete minor') .on('click', deleteMember) .call(svgIcon('#iD-operation-delete')); From 169a92df7c48fd7c7f04122eb938e53aa176fe03 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Mon, 15 Oct 2018 21:20:44 -0700 Subject: [PATCH 35/51] Fixes label button wrap on right-to-left layouts --- css/80_app.css | 4 ++-- modules/ui/raw_member_editor.js | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index ab1b8e156..307eafdfb 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1254,10 +1254,10 @@ a.hide-toggle { right: 0; height: 100%; background: transparent; - text-align: right; } [dir='rtl'] .form-label-button-wrap { - text-align: left; + right: auto; + left: 0; } .form-label-button-wrap .tag-reference-button { diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index c510ec94d..a37153464 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -146,10 +146,6 @@ export function uiRawMemberEditor(context) { var label = d3_select(this).append('label') .attr('class', 'form-label'); - // add the button wrap beneath the label text - var buttonWrap = label.append('div') - .attr('class', 'form-label-button-wrap'); - var labelLink = label.append('a') .attr('href', '#') .on('click', selectMember); @@ -165,6 +161,9 @@ export function uiRawMemberEditor(context) { .attr('class', 'member-entity-name') .text(function(d) { return utilDisplayName(d.member); }); + var buttonWrap = label.append('div') + .attr('class', 'form-label-button-wrap'); + buttonWrap.append('button') .attr('class', 'download-icon') .attr('title', t('icons.zoom_to')) From 266756bd9dbdfecdf9451579fed82a31be6fb97b Mon Sep 17 00:00:00 2001 From: castriganoj Date: Wed, 17 Oct 2018 20:29:46 -0400 Subject: [PATCH 36/51] Add fields for To and From and add fields to all route presets. --- data/presets.yaml | 6 ++++ data/presets/fields.json | 2 ++ data/presets/fields/from.json | 5 +++ data/presets/fields/to.json | 5 +++ data/presets/presets.json | 32 +++++++++---------- data/presets/presets/route/ferry.json | 4 ++- data/presets/presets/type/route/bicycle.json | 4 ++- data/presets/presets/type/route/bus.json | 4 ++- data/presets/presets/type/route/detour.json | 4 ++- data/presets/presets/type/route/ferry.json | 4 ++- data/presets/presets/type/route/foot.json | 4 ++- data/presets/presets/type/route/hiking.json | 4 ++- data/presets/presets/type/route/horse.json | 4 ++- .../presets/type/route/light_rail.json | 4 ++- data/presets/presets/type/route/pipeline.json | 4 ++- data/presets/presets/type/route/piste.json | 4 ++- data/presets/presets/type/route/power.json | 4 ++- data/presets/presets/type/route/road.json | 4 ++- data/presets/presets/type/route/subway.json | 4 ++- data/presets/presets/type/route/train.json | 4 ++- data/presets/presets/type/route/tram.json | 4 ++- data/taginfo.json | 2 ++ dist/locales/en.json | 20 ++++++++++++ 23 files changed, 104 insertions(+), 32 deletions(-) create mode 100644 data/presets/fields/from.json create mode 100644 data/presets/fields/to.json diff --git a/data/presets.yaml b/data/presets.yaml index 3bc157065..201c35451 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -633,6 +633,9 @@ en: frequency: # frequency=* label: Operating Frequency + from: + # from=* + label: From fuel: # fuel=* label: Fuel @@ -1800,6 +1803,9 @@ en: 'yes': 'Yes' # takeaway field placeholder placeholder: 'Yes, No, Takeaway Only...' + to: + # to =* + label: To toilets/disposal: # 'toilets:disposal=*' label: Disposal diff --git a/data/presets/fields.json b/data/presets/fields.json index 2a8bb55a6..5ad079d20 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -109,6 +109,7 @@ "flag/type": {"key": "flag:type", "type": "combo", "label": "Flag Type"}, "ford": {"key": "ford", "type": "typeCombo", "label": "Type", "placeholder": "Default"}, "frequency": {"key": "frequency", "type": "combo", "label": "Operating Frequency"}, + "from": {"key": "from", "type": "text", "label": "From"}, "fuel_multi": {"key": "fuel:", "type": "multiCombo", "label": "Fuel Types"}, "fuel": {"key": "fuel", "type": "combo", "label": "Fuel"}, "gauge": {"key": "gauge", "type": "combo", "label": "Gauge"}, @@ -303,6 +304,7 @@ "switch": {"key": "switch", "type": "combo", "label": "Type", "strings": {"options": {"mechanical": "Mechanical", "circuit_breaker": "Circuit Breaker", "disconnector": "Disconnector", "earthing": "Earthing"}}}, "tactile_paving": {"key": "tactile_paving", "type": "check", "label": "Tactile Paving"}, "takeaway": {"key": "takeaway", "type": "combo", "label": "Takeaway", "placeholder": "Yes, No, Takeaway Only...", "strings": {"options": {"yes": "Yes", "no": "No", "only": "Takeaway Only"}}}, + "to": {"key": "to ", "type": "text", "label": "To"}, "toilets/disposal": {"key": "toilets:disposal", "type": "combo", "label": "Disposal", "strings": {"options": {"flush": "Flush", "pitlatrine": "Pit/Latrine", "chemical": "Chemical", "bucket": "Bucket"}}}, "toll": {"key": "toll", "type": "check", "label": "Toll"}, "tomb": {"key": "tomb", "type": "typeCombo", "label": "Type"}, diff --git a/data/presets/fields/from.json b/data/presets/fields/from.json new file mode 100644 index 000000000..5a96cc469 --- /dev/null +++ b/data/presets/fields/from.json @@ -0,0 +1,5 @@ +{ + "key": "from", + "type": "text", + "label": "From" +} \ No newline at end of file diff --git a/data/presets/fields/to.json b/data/presets/fields/to.json new file mode 100644 index 000000000..c3236efba --- /dev/null +++ b/data/presets/fields/to.json @@ -0,0 +1,5 @@ +{ + "key": "to ", + "type": "text", + "label": "To" +} \ No newline at end of file diff --git a/data/presets/presets.json b/data/presets/presets.json index 9e2def04c..b71ea08a7 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -776,7 +776,7 @@ "railway/train_wash": {"icon": "maki-rail", "geometry": ["point", "vertex", "area"], "fields": ["operator", "building_area"], "tags": {"railway": "wash"}, "terms": ["wash", "clean"], "name": "Train Wash"}, "railway/tram": {"icon": "iD-railway-light-rail", "fields": ["name", "structure", "gauge", "electrified", "service_rail", "usage_rail", "voltage", "frequency"], "geometry": ["line"], "tags": {"railway": "tram"}, "terms": ["light rail", "streetcar", "tram", "trolley"], "name": "Tram"}, "relation": {"icon": "iD-relation", "fields": ["name", "relation"], "geometry": ["relation"], "tags": {}, "name": "Relation"}, - "route/ferry": {"icon": "iD-ferry-line", "geometry": ["line"], "fields": ["name", "operator", "duration", "access"], "tags": {"route": "ferry"}, "name": "Ferry Route"}, + "route/ferry": {"icon": "iD-ferry-line", "geometry": ["line"], "fields": ["name", "operator", "duration", "access", "to", "from"], "tags": {"route": "ferry"}, "name": "Ferry Route"}, "seamark/beacon_isolated_danger": {"fields": ["ref", "operator", "seamark/beacon_isolated_danger/shape"], "geometry": ["point", "vertex"], "terms": ["beacon isolated danger", "isolated danger beacon", "iala"], "tags": {"seamark:type": "beacon_isolated_danger"}, "name": "Danger Beacon"}, "seamark/beacon_lateral": {"fields": ["ref", "operator", "seamark/beacon_lateral/colour", "seamark/beacon_lateral/category", "seamark/beacon_lateral/shape", "seamark/beacon_lateral/system"], "geometry": ["point", "vertex"], "terms": ["lateral beacon", "beacon lateral", "cevni", "channel marker", "iala", "lateral mark"], "tags": {"seamark:type": "beacon_lateral"}, "name": "Channel Beacon"}, "seamark/buoy_lateral": {"fields": ["ref", "operator", "seamark/buoy_lateral/colour", "seamark/buoy_lateral/category", "seamark/buoy_lateral/shape", "seamark/buoy_lateral/system"], "geometry": ["point", "vertex"], "terms": ["lateral buoy", "buoy lateral", "cevni", "channel marker", "iala", "lateral mark"], "tags": {"seamark:type": "buoy_lateral"}, "name": "Channel Buoy"}, @@ -971,21 +971,21 @@ "type/restriction/only_u_turn": {"icon": "iD-restriction-only-u-turn", "fields": ["except"], "geometry": ["relation"], "tags": {"type": "restriction", "restriction": "only_u_turn"}, "name": "Only U-turn"}, "type/route_master": {"icon": "iD-route-master", "fields": ["name", "route_master", "ref", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route_master"}, "name": "Route Master"}, "type/route": {"icon": "iD-route", "fields": ["name", "route", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route"}, "name": "Route"}, - "type/route/bicycle": {"icon": "iD-route-bicycle", "fields": ["name", "ref_route", "network_bicycle", "cycle_network"], "geometry": ["relation"], "tags": {"type": "route", "route": "bicycle"}, "name": "Cycle Route"}, - "type/route/bus": {"icon": "iD-route-bus", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "bus"}, "name": "Bus Route"}, - "type/route/detour": {"icon": "iD-route-detour", "fields": ["name", "ref_route"], "geometry": ["relation"], "tags": {"type": "route", "route": "detour"}, "name": "Detour Route"}, - "type/route/ferry": {"icon": "iD-route-ferry", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "ferry"}, "name": "Ferry Route"}, - "type/route/foot": {"icon": "iD-route-foot", "fields": ["name", "ref_route", "operator", "network_foot"], "geometry": ["relation"], "tags": {"type": "route", "route": "foot"}, "name": "Foot Route"}, - "type/route/hiking": {"icon": "iD-route-foot", "fields": ["name", "ref_route", "operator", "network_foot", "description", "distance"], "geometry": ["relation"], "tags": {"type": "route", "route": "hiking"}, "name": "Hiking Route"}, - "type/route/horse": {"icon": "iD-route-horse", "fields": ["name", "ref_route", "operator", "network_horse", "description", "distance"], "geometry": ["relation"], "tags": {"type": "route", "route": "horse"}, "name": "Riding Route"}, - "type/route/light_rail": {"icon": "iD-route-light-rail", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "light_rail"}, "name": "Light Rail Route"}, - "type/route/pipeline": {"icon": "iD-route-pipeline", "fields": ["name", "ref_route", "operator"], "geometry": ["relation"], "tags": {"type": "route", "route": "pipeline"}, "name": "Pipeline Route"}, - "type/route/piste": {"icon": "iD-route-piste", "fields": ["name", "piste/type", "colour", "ref_route", "operator"], "geometry": ["relation"], "tags": {"type": "route", "route": "piste"}, "name": "Piste/Ski Route"}, - "type/route/power": {"icon": "iD-route-power", "fields": ["name", "ref_route", "operator"], "geometry": ["relation"], "tags": {"type": "route", "route": "power"}, "name": "Power Route"}, - "type/route/road": {"icon": "iD-route-road", "fields": ["name", "ref_route", "network_road"], "geometry": ["relation"], "tags": {"type": "route", "route": "road"}, "name": "Road Route"}, - "type/route/subway": {"icon": "iD-route-subway", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "subway"}, "name": "Subway Route"}, - "type/route/train": {"icon": "iD-route-train", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "train"}, "name": "Train Route"}, - "type/route/tram": {"icon": "iD-route-tram", "fields": ["name", "ref_route", "operator", "network"], "geometry": ["relation"], "tags": {"type": "route", "route": "tram"}, "name": "Tram Route"}, + "type/route/bicycle": {"icon": "iD-route-bicycle", "fields": ["name", "ref_route", "network_bicycle", "cycle_network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "bicycle"}, "name": "Cycle Route"}, + "type/route/bus": {"icon": "iD-route-bus", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "bus"}, "name": "Bus Route"}, + "type/route/detour": {"icon": "iD-route-detour", "fields": ["name", "ref_route", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "detour"}, "name": "Detour Route"}, + "type/route/ferry": {"icon": "iD-route-ferry", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "ferry"}, "name": "Ferry Route"}, + "type/route/foot": {"icon": "iD-route-foot", "fields": ["name", "ref_route", "operator", "network_foot", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "foot"}, "name": "Foot Route"}, + "type/route/hiking": {"icon": "iD-route-foot", "fields": ["name", "ref_route", "operator", "network_foot", "description", "distance", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "hiking"}, "name": "Hiking Route"}, + "type/route/horse": {"icon": "iD-route-horse", "fields": ["name", "ref_route", "operator", "network_horse", "description", "distance", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "horse"}, "name": "Riding Route"}, + "type/route/light_rail": {"icon": "iD-route-light-rail", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "light_rail"}, "name": "Light Rail Route"}, + "type/route/pipeline": {"icon": "iD-route-pipeline", "fields": ["name", "ref_route", "operator", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "pipeline"}, "name": "Pipeline Route"}, + "type/route/piste": {"icon": "iD-route-piste", "fields": ["name", "piste/type", "colour", "ref_route", "operator", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "piste"}, "name": "Piste/Ski Route"}, + "type/route/power": {"icon": "iD-route-power", "fields": ["name", "ref_route", "operator", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "power"}, "name": "Power Route"}, + "type/route/road": {"icon": "iD-route-road", "fields": ["name", "ref_route", "network_road", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "road"}, "name": "Road Route"}, + "type/route/subway": {"icon": "iD-route-subway", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "subway"}, "name": "Subway Route"}, + "type/route/train": {"icon": "iD-route-train", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "train"}, "name": "Train Route"}, + "type/route/tram": {"icon": "iD-route-tram", "fields": ["name", "ref_route", "operator", "network", "to", "from"], "geometry": ["relation"], "tags": {"type": "route", "route": "tram"}, "name": "Tram Route"}, "type/site": {"icon": "iD-relation", "fields": ["name", "site"], "geometry": ["relation"], "tags": {"type": "site"}, "name": "Site"}, "type/waterway": {"icon": "iD-route-water", "fields": ["name", "waterway", "ref"], "geometry": ["relation"], "tags": {"type": "waterway"}, "name": "Waterway"}, "vertex": {"fields": ["name"], "geometry": ["vertex"], "tags": {}, "name": "Other", "matchScore": 0.1}, diff --git a/data/presets/presets/route/ferry.json b/data/presets/presets/route/ferry.json index 4a33de905..8be49a069 100644 --- a/data/presets/presets/route/ferry.json +++ b/data/presets/presets/route/ferry.json @@ -7,7 +7,9 @@ "name", "operator", "duration", - "access" + "access", + "to", + "from" ], "tags": { "route": "ferry" diff --git a/data/presets/presets/type/route/bicycle.json b/data/presets/presets/type/route/bicycle.json index 6efc659a5..2a26657e7 100644 --- a/data/presets/presets/type/route/bicycle.json +++ b/data/presets/presets/type/route/bicycle.json @@ -4,7 +4,9 @@ "name", "ref_route", "network_bicycle", - "cycle_network" + "cycle_network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/bus.json b/data/presets/presets/type/route/bus.json index b62cc5fc1..8f6b97f2b 100644 --- a/data/presets/presets/type/route/bus.json +++ b/data/presets/presets/type/route/bus.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/detour.json b/data/presets/presets/type/route/detour.json index 879ada780..3966df1a9 100644 --- a/data/presets/presets/type/route/detour.json +++ b/data/presets/presets/type/route/detour.json @@ -2,7 +2,9 @@ "icon": "iD-route-detour", "fields": [ "name", - "ref_route" + "ref_route", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/ferry.json b/data/presets/presets/type/route/ferry.json index eacec19d8..96c21fe3b 100644 --- a/data/presets/presets/type/route/ferry.json +++ b/data/presets/presets/type/route/ferry.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/foot.json b/data/presets/presets/type/route/foot.json index 91c5706be..f8c841a91 100644 --- a/data/presets/presets/type/route/foot.json +++ b/data/presets/presets/type/route/foot.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network_foot" + "network_foot", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/hiking.json b/data/presets/presets/type/route/hiking.json index db0c04889..a18d19517 100644 --- a/data/presets/presets/type/route/hiking.json +++ b/data/presets/presets/type/route/hiking.json @@ -6,7 +6,9 @@ "operator", "network_foot", "description", - "distance" + "distance", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/horse.json b/data/presets/presets/type/route/horse.json index a524346d8..d4501ff03 100644 --- a/data/presets/presets/type/route/horse.json +++ b/data/presets/presets/type/route/horse.json @@ -6,7 +6,9 @@ "operator", "network_horse", "description", - "distance" + "distance", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/light_rail.json b/data/presets/presets/type/route/light_rail.json index 8bf10a7ad..6e9f505b3 100644 --- a/data/presets/presets/type/route/light_rail.json +++ b/data/presets/presets/type/route/light_rail.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/pipeline.json b/data/presets/presets/type/route/pipeline.json index 5f6cadbdc..d15c7547b 100644 --- a/data/presets/presets/type/route/pipeline.json +++ b/data/presets/presets/type/route/pipeline.json @@ -3,7 +3,9 @@ "fields": [ "name", "ref_route", - "operator" + "operator", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/piste.json b/data/presets/presets/type/route/piste.json index 42993a796..7f478bd04 100644 --- a/data/presets/presets/type/route/piste.json +++ b/data/presets/presets/type/route/piste.json @@ -5,7 +5,9 @@ "piste/type", "colour", "ref_route", - "operator" + "operator", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/power.json b/data/presets/presets/type/route/power.json index d3f4d1865..9689d02bb 100644 --- a/data/presets/presets/type/route/power.json +++ b/data/presets/presets/type/route/power.json @@ -3,7 +3,9 @@ "fields": [ "name", "ref_route", - "operator" + "operator", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/road.json b/data/presets/presets/type/route/road.json index 875c1d57c..21908c406 100644 --- a/data/presets/presets/type/route/road.json +++ b/data/presets/presets/type/route/road.json @@ -3,7 +3,9 @@ "fields": [ "name", "ref_route", - "network_road" + "network_road", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/subway.json b/data/presets/presets/type/route/subway.json index 013bd804c..209b63c72 100644 --- a/data/presets/presets/type/route/subway.json +++ b/data/presets/presets/type/route/subway.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/train.json b/data/presets/presets/type/route/train.json index 64d740478..d4766262c 100644 --- a/data/presets/presets/type/route/train.json +++ b/data/presets/presets/type/route/train.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/presets/presets/type/route/tram.json b/data/presets/presets/type/route/tram.json index 0706d2b68..3970a1ac2 100644 --- a/data/presets/presets/type/route/tram.json +++ b/data/presets/presets/type/route/tram.json @@ -4,7 +4,9 @@ "name", "ref_route", "operator", - "network" + "network", + "to", + "from" ], "geometry": [ "relation" diff --git a/data/taginfo.json b/data/taginfo.json index 2f7b886cf..9d364ebf9 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -6860,6 +6860,7 @@ {"key": "flag:type", "description": "Flag Type"}, {"key": "ford", "description": "Type"}, {"key": "frequency", "description": "Operating Frequency"}, + {"key": "from", "description": "From"}, {"key": "fuel:", "description": "Fuel Types"}, {"key": "fuel", "description": "Fuel"}, {"key": "gauge", "description": "Gauge"}, @@ -7493,6 +7494,7 @@ {"key": "takeaway", "value": "yes", "description": "Takeaway"}, {"key": "takeaway", "value": "no", "description": "Takeaway"}, {"key": "takeaway", "value": "only", "description": "Takeaway"}, + {"key": "to ", "description": "To"}, {"key": "toilets:disposal", "value": "flush", "description": "Disposal"}, { "key": "toilets:disposal", diff --git a/dist/locales/en.json b/dist/locales/en.json index 81e861d3c..5152e14f2 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -2004,6 +2004,9 @@ "frequency": { "label": "Operating Frequency" }, + "from": { + "label": "From" + }, "fuel_multi": { "label": "Fuel Types" }, @@ -2954,6 +2957,9 @@ "only": "Takeaway Only" } }, + "to": { + "label": "To" + }, "toilets/disposal": { "label": "Disposal", "options": { @@ -7412,6 +7418,13 @@ "description": "Japan GSI Standard Map. Widely covered.", "name": "Japan GSI Standard Map" }, + "helsingborg-orto": { + "attribution": { + "text": "© Helsingborg municipality" + }, + "description": "Orthophotos from the municipality of Helsingborg 2016, public domain", + "name": "Helsingborg Orthophoto" + }, "hike_n_bike": { "attribution": { "text": "© OpenStreetMap contributors" @@ -7503,6 +7516,13 @@ }, "name": "Stamen Terrain" }, + "stockholm-orto": { + "attribution": { + "text": "© Stockholm municipality, CC0" + }, + "description": "Orthophotos from the municipality of Stockholm 2015, CC0 license", + "name": "Stockholm Orthophoto" + }, "tf-cycle": { "attribution": { "text": "Maps © Thunderforest, Data © OpenStreetMap contributors" From 70c3d9cf24c9fbec0f0bb0c85932b3060f661bfd Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 18 Oct 2018 17:36:30 -0700 Subject: [PATCH 37/51] Adding a new point on a way now adds a vertex, not a standalone point --- modules/modes/add_point.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index 678be7c68..ac978babf 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -3,6 +3,8 @@ import { actionAddEntity } from '../actions'; import { behaviorDraw } from '../behavior'; import { modeBrowse, modeSelect } from './index'; import { osmNode } from '../osm'; +import { geoChooseEdge } from '../geo'; +import { actionAddMidpoint } from '../actions'; export function modeAddPoint(context) { @@ -37,8 +39,17 @@ export function modeAddPoint(context) { } - function addWay(loc) { - add(loc); + function addWay(loc, edge, d) { + var node = osmNode(); + + context.perform( + actionAddMidpoint({loc: loc, edge: edge}, node), + t('operations.add.annotation.vertex') + ); + + context.enter( + modeSelect(context, [node.id]).newFeature(true) + ); } From 38bfeb852d7d8992cfcc5ba2af20d8561fcfdf9e Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 18 Oct 2018 18:31:43 -0700 Subject: [PATCH 38/51] Adds custom CSS styling for ferry routes Closes #5414 --- css/45_waterways.css | 24 +++++++++++++++++++++++- modules/svg/tag_classes.js | 2 +- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/css/45_waterways.css b/css/45_waterways.css index fb71ec09b..41f954db3 100644 --- a/css/45_waterways.css +++ b/css/45_waterways.css @@ -4,7 +4,6 @@ fill: #77d3de; } .preset-icon .icon.iD-category-water, -.preset-icon .icon.tag-route-ferry, .preset-icon .icon.tag-type-waterway, .preset-icon .icon.tag-waterway { color: #77d3de; @@ -95,3 +94,26 @@ path.area.fill.tag-waterway-fuel { fill: rgba(255, 255, 255, 0.3); } +/* ferry routes */ +.preset-icon .icon.tag-route-ferry { + color: #58a9ed; + fill: #fff; +} +path.shadow.tag-route-ferry { + stroke-width: 16; +} +path.stroke.tag-route-ferry { + stroke-width: 3; + stroke-linecap: butt; + stroke-dasharray: 12,8; +} +.low-zoom path.shadow.tag-route-ferry { + stroke-width: 12; +} +.low-zoom path.stroke.tag-route-ferry { + stroke-width: 2; + stroke-dasharray: 6,4; +} +path.stroke.tag-route-ferry { + stroke: #58a9ed; +} diff --git a/modules/svg/tag_classes.js b/modules/svg/tag_classes.js index cffe9af30..35ca42181 100644 --- a/modules/svg/tag_classes.js +++ b/modules/svg/tag_classes.js @@ -6,7 +6,7 @@ export function svgTagClasses() { var primaries = [ 'building', 'highway', 'railway', 'waterway', 'aeroway', 'motorway', 'boundary', 'power', 'amenity', 'natural', 'landuse', - 'leisure', 'military', 'place', 'man_made' + 'leisure', 'military', 'place', 'man_made', 'route' ]; var statuses = [ 'proposed', 'construction', 'disused', 'abandoned', 'dismantled', From 510d7e93e5590bfb78b1c961e05ee70dd88d6b0f Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Thu, 18 Oct 2018 18:47:04 -0700 Subject: [PATCH 39/51] Fixes two lint warnings for unused vars --- modules/modes/add_point.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/modes/add_point.js b/modules/modes/add_point.js index ac978babf..89623a012 100644 --- a/modules/modes/add_point.js +++ b/modules/modes/add_point.js @@ -3,7 +3,6 @@ import { actionAddEntity } from '../actions'; import { behaviorDraw } from '../behavior'; import { modeBrowse, modeSelect } from './index'; import { osmNode } from '../osm'; -import { geoChooseEdge } from '../geo'; import { actionAddMidpoint } from '../actions'; @@ -39,7 +38,7 @@ export function modeAddPoint(context) { } - function addWay(loc, edge, d) { + function addWay(loc, edge) { var node = osmNode(); context.perform( From 613bb08e201f7ab129e04a54c49ff8d55caf5317 Mon Sep 17 00:00:00 2001 From: thefifthisa Date: Fri, 19 Oct 2018 11:02:46 -0400 Subject: [PATCH 40/51] add takeaway field to cafe preset --- data/presets/presets.json | 2 +- data/presets/presets/amenity/cafe.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/presets/presets.json b/data/presets/presets.json index 9e2def04c..350d8762d 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -58,7 +58,7 @@ "amenity/biergarten": {"icon": "maki-beer", "fields": ["name", "address", "building", "opening_hours", "smoking", "outdoor_seating", "brewery"], "geometry": ["point", "area"], "tags": {"amenity": "biergarten"}, "terms": ["beer", "bier", "booze"], "name": "Biergarten"}, "amenity/boat_rental": {"fields": ["name", "operator", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "boat_rental"}, "name": "Boat Rental"}, "amenity/bureau_de_change": {"icon": "maki-bank", "fields": ["name", "currency_multi", "operator", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "terms": ["bureau de change", "money changer"], "tags": {"amenity": "bureau_de_change"}, "name": "Currency Exchange"}, - "amenity/cafe": {"icon": "maki-cafe", "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "geometry": ["point", "area"], "terms": ["bistro", "coffee", "tea"], "tags": {"amenity": "cafe"}, "name": "Cafe"}, + "amenity/cafe": {"icon": "maki-cafe", "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "geometry": ["point", "area"], "terms": ["bistro", "coffee", "tea"], "tags": {"amenity": "cafe"}, "name": "Cafe"}, "amenity/car_pooling": {"icon": "maki-car", "fields": ["name", "operator", "capacity"], "geometry": ["point", "area"], "tags": {"amenity": "car_pooling"}, "name": "Car Pooling"}, "amenity/car_rental": {"icon": "maki-car", "fields": ["name", "operator", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "car_rental"}, "name": "Car Rental"}, "amenity/car_sharing": {"icon": "maki-car", "fields": ["name", "operator", "capacity", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "car_sharing"}, "name": "Car Sharing"}, diff --git a/data/presets/presets/amenity/cafe.json b/data/presets/presets/amenity/cafe.json index 2bde8ed60..75be1725c 100644 --- a/data/presets/presets/amenity/cafe.json +++ b/data/presets/presets/amenity/cafe.json @@ -10,7 +10,8 @@ "internet_access/fee", "internet_access/ssid", "smoking", - "outdoor_seating" + "outdoor_seating", + "takeaway" ], "geometry": [ "point", From 5ed1f3ae59f4e392ff372af36452ae472ec58f9a Mon Sep 17 00:00:00 2001 From: FrikanRw Date: Sat, 20 Oct 2018 09:10:40 +0200 Subject: [PATCH 41/51] Added maxspeed field to Speed camera preset issue : #5417 Update: Added "maxspeed" to speed_camera.json --- data/presets/presets.json | 150 +++++++++--------- .../presets/presets/highway/speed_camera.json | 3 +- 2 files changed, 77 insertions(+), 76 deletions(-) diff --git a/data/presets/presets.json b/data/presets/presets.json index 350d8762d..cc712f325 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -410,7 +410,7 @@ "highway/service/emergency_access": {"icon": "iD-highway-service", "fields": ["name", "oneway", "maxspeed", "structure", "access", "surface", "maxheight"], "geometry": ["line"], "tags": {"highway": "service", "service": "emergency_access"}, "reference": {"key": "service", "value": "emergency_access"}, "name": "Emergency Access"}, "highway/service/parking_aisle": {"icon": "iD-highway-service", "fields": ["name", "oneway", "maxspeed", "structure", "access", "surface", "maxheight"], "geometry": ["line"], "tags": {"highway": "service", "service": "parking_aisle"}, "reference": {"key": "service", "value": "parking_aisle"}, "name": "Parking Aisle"}, "highway/services": {"icon": "maki-car", "fields": ["name"], "geometry": ["point", "vertex", "area"], "tags": {"highway": "services"}, "terms": ["services", "travel plaza", "service station"], "name": "Service Area"}, - "highway/speed_camera": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["direction", "ref"], "tags": {"highway": "speed_camera"}, "terms": [], "name": "Speed Camera"}, + "highway/speed_camera": {"icon": "maki-attraction", "geometry": ["point", "vertex"], "fields": ["direction", "ref", "maxspeed"], "tags": {"highway": "speed_camera"}, "terms": [], "name": "Speed Camera"}, "highway/steps": {"icon": "iD-highway-steps", "fields": ["surface", "lit", "width", "incline_steps", "handrail", "step_count"], "geometry": ["line"], "tags": {"highway": "steps"}, "terms": ["stairs", "staircase"], "name": "Steps"}, "highway/steps/conveying": {"icon": "maki-entrance", "fields": ["name", "incline_steps", "conveying", "access_simple", "lit", "width", "handrail", "step_count"], "geometry": ["line"], "terms": ["moving staircase", "moving stairway", "people mover"], "tags": {"highway": "steps", "conveying": "*"}, "name": "Escalator"}, "highway/stop": {"icon": "temaki-stop", "fields": ["stop", "direction_vertex"], "geometry": ["vertex"], "tags": {"highway": "stop"}, "terms": ["stop", "halt", "sign"], "name": "Stop Sign"}, @@ -1513,80 +1513,80 @@ "amenity/bureau_de_change/Abitab": {"tags": {"name": "Abitab", "amenity": "bureau_de_change"}, "name": "Abitab", "icon": "maki-bank", "geometry": ["point", "area"], "fields": ["name", "currency_multi", "operator", "address", "building_area", "opening_hours"], "suggestion": true}, "amenity/bureau_de_change/Change": {"tags": {"name": "Change", "amenity": "bureau_de_change"}, "name": "Change", "icon": "maki-bank", "geometry": ["point", "area"], "fields": ["name", "currency_multi", "operator", "address", "building_area", "opening_hours"], "suggestion": true}, "amenity/bureau_de_change/Travelex": {"tags": {"name": "Travelex", "amenity": "bureau_de_change"}, "name": "Travelex", "icon": "maki-bank", "geometry": ["point", "area"], "fields": ["name", "currency_multi", "operator", "address", "building_area", "opening_hours"], "suggestion": true}, - "amenity/cafe/85度C": {"tags": {"name": "85度C", "amenity": "cafe"}, "name": "85度C", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Bar Kafe": {"tags": {"name": "Bar Kafe", "amenity": "cafe"}, "name": "Bar Kafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Barista": {"tags": {"name": "Barista", "amenity": "cafe"}, "name": "Barista", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Bonafide": {"tags": {"name": "Bonafide", "amenity": "cafe"}, "name": "Bonafide", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Cafe Coffee Day": {"tags": {"name": "Cafe Coffee Day", "amenity": "cafe"}, "name": "Cafe Coffee Day", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Cafe Nero": {"tags": {"name": "Cafe Nero", "amenity": "cafe"}, "name": "Cafe Nero", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Cafeteria": {"tags": {"name": "Cafeteria", "amenity": "cafe"}, "name": "Cafeteria", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Cafetería": {"tags": {"name": "Cafetería", "amenity": "cafe"}, "name": "Cafetería", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Caffè Nero": {"tags": {"name": "Caffè Nero", "amenity": "cafe"}, "name": "Caffè Nero", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Café Amazon": {"tags": {"name": "Café Amazon", "amenity": "cafe"}, "name": "Café Amazon", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Café Central": {"tags": {"name": "Café Central", "amenity": "cafe"}, "name": "Café Central", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Café de la Place": {"tags": {"name": "Café de la Place", "amenity": "cafe"}, "name": "Café de la Place", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Café des Sports": {"tags": {"name": "Café des Sports", "amenity": "cafe"}, "name": "Café des Sports", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Caribou Coffee": {"tags": {"name": "Caribou Coffee", "amenity": "cafe"}, "name": "Caribou Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Coffee Fellows": {"tags": {"name": "Coffee Fellows", "amenity": "cafe"}, "name": "Coffee Fellows", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Coffee House": {"tags": {"name": "Coffee House", "amenity": "cafe"}, "name": "Coffee House", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Coffee Island": {"tags": {"name": "Coffee Island", "amenity": "cafe"}, "name": "Coffee Island", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Coffee Time": {"tags": {"name": "Coffee Time", "amenity": "cafe"}, "name": "Coffee Time", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Costa": {"tags": {"name": "Costa", "amenity": "cafe"}, "name": "Costa", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Country Style": {"tags": {"name": "Country Style", "amenity": "cafe"}, "name": "Country Style", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Dunkin' Donuts": {"tags": {"name": "Dunkin' Donuts", "cuisine": "donut", "amenity": "cafe"}, "name": "Dunkin' Donuts", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Eiscafe Dolomiti": {"tags": {"name": "Eiscafe Dolomiti", "amenity": "cafe"}, "name": "Eiscafe Dolomiti", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Eiscafe Venezia": {"tags": {"name": "Eiscafe Venezia", "amenity": "cafe"}, "name": "Eiscafe Venezia", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Espresso House": {"tags": {"name": "Espresso House", "amenity": "cafe"}, "name": "Espresso House", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Havanna": {"tags": {"name": "Havanna", "amenity": "cafe"}, "name": "Havanna", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Internet Cafe": {"tags": {"name": "Internet Cafe", "amenity": "cafe"}, "name": "Internet Cafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Kafe": {"tags": {"name": "Kafe", "amenity": "cafe"}, "name": "Kafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Krispy Kreme": {"tags": {"name": "Krispy Kreme", "amenity": "cafe"}, "name": "Krispy Kreme", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Le Pain Quotidien": {"tags": {"name": "Le Pain Quotidien", "amenity": "cafe"}, "name": "Le Pain Quotidien", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/McCafé": {"tags": {"name": "McCafé", "amenity": "cafe", "cuisine": "coffee_shop"}, "name": "McCafé", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Peet's Coffee & Tea": {"tags": {"name": "Peet's Coffee & Tea", "amenity": "cafe"}, "name": "Peet's Coffee & Tea", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Pret A Manger": {"tags": {"name": "Pret A Manger", "amenity": "cafe"}, "name": "Pret A Manger", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Prime": {"tags": {"name": "Prime", "amenity": "cafe"}, "name": "Prime", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Second Cup": {"tags": {"name": "Second Cup", "amenity": "cafe"}, "name": "Second Cup", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Segafredo": {"tags": {"name": "Segafredo", "amenity": "cafe"}, "name": "Segafredo", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Starbucks": {"tags": {"name": "Starbucks", "cuisine": "coffee_shop", "amenity": "cafe"}, "name": "Starbucks", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/The Coffee Bean & Tea Leaf": {"tags": {"name": "The Coffee Bean & Tea Leaf", "amenity": "cafe"}, "name": "The Coffee Bean & Tea Leaf", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/The Coffee Club": {"tags": {"name": "The Coffee Club", "amenity": "cafe"}, "name": "The Coffee Club", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Tim Hortons": {"tags": {"name": "Tim Hortons", "amenity": "cafe"}, "name": "Tim Hortons", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Traveler's Coffee": {"tags": {"name": "Traveler's Coffee", "amenity": "cafe"}, "name": "Traveler's Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Wayne's Coffee": {"tags": {"name": "Wayne's Coffee", "amenity": "cafe"}, "name": "Wayne's Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Бистро": {"tags": {"name": "Бистро", "amenity": "cafe"}, "name": "Бистро", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Буфет": {"tags": {"name": "Буфет", "amenity": "cafe"}, "name": "Буфет", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Встреча": {"tags": {"name": "Встреча", "amenity": "cafe"}, "name": "Встреча", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Даблби": {"tags": {"name": "Даблби", "amenity": "cafe"}, "name": "Даблби", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Закусочная": {"tags": {"name": "Закусочная", "amenity": "cafe"}, "name": "Закусочная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Кофе Хауз": {"tags": {"name": "Кофе Хауз", "amenity": "cafe"}, "name": "Кофе Хауз", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Кофейня": {"tags": {"name": "Кофейня", "amenity": "cafe"}, "name": "Кофейня", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Лакомка": {"tags": {"name": "Лакомка", "amenity": "cafe"}, "name": "Лакомка", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Летнее кафе": {"tags": {"name": "Летнее кафе", "amenity": "cafe"}, "name": "Летнее кафе", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Пельменная": {"tags": {"name": "Пельменная", "amenity": "cafe"}, "name": "Пельменная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Пиццерия": {"tags": {"name": "Пиццерия", "amenity": "cafe"}, "name": "Пиццерия", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Рандеву": {"tags": {"name": "Рандеву", "amenity": "cafe"}, "name": "Рандеву", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Сказка": {"tags": {"name": "Сказка", "amenity": "cafe"}, "name": "Сказка", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Старбакс": {"tags": {"name": "Старбакс", "amenity": "cafe"}, "name": "Старбакс", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Столовая": {"tags": {"name": "Столовая", "amenity": "cafe"}, "name": "Столовая", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Уют": {"tags": {"name": "Уют", "amenity": "cafe"}, "name": "Уют", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Хуторок": {"tags": {"name": "Хуторок", "amenity": "cafe"}, "name": "Хуторок", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Шашлычная": {"tags": {"name": "Шашлычная", "amenity": "cafe"}, "name": "Шашлычная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Шоколад": {"tags": {"name": "Шоколад", "amenity": "cafe"}, "name": "Шоколад", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/Шоколадница": {"tags": {"name": "Шоколадница", "amenity": "cafe"}, "name": "Шоколадница", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/ארומה": {"tags": {"name": "ארומה", "amenity": "cafe"}, "name": "ארומה", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/مقهى": {"tags": {"name": "مقهى", "amenity": "cafe"}, "name": "مقهى", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/คาเฟ่ อเมซอน": {"tags": {"name": "คาเฟ่ อเมซอน", "amenity": "cafe"}, "name": "คาเฟ่ อเมซอน", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/エクセルシオール カフェ": {"tags": {"name": "エクセルシオール カフェ", "amenity": "cafe"}, "name": "エクセルシオール カフェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/カフェ・ド・クリエ": {"tags": {"name": "カフェ・ド・クリエ", "name:en": "Cafe de CRIE", "amenity": "cafe"}, "name": "カフェ・ド・クリエ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/カフェ・ベローチェ": {"tags": {"name": "カフェ・ベローチェ", "amenity": "cafe"}, "name": "カフェ・ベローチェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/コメダ珈琲店": {"tags": {"name": "コメダ珈琲店", "amenity": "cafe"}, "name": "コメダ珈琲店", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/サンマルクカフェ": {"tags": {"name": "サンマルクカフェ", "amenity": "cafe"}, "name": "サンマルクカフェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/スターバックス": {"tags": {"name": "スターバックス", "name:en": "Starbucks", "amenity": "cafe"}, "name": "スターバックス", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/タリーズコーヒー": {"tags": {"name": "タリーズコーヒー", "amenity": "cafe"}, "name": "タリーズコーヒー", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/ドトールコーヒーショップ": {"tags": {"name": "ドトールコーヒーショップ", "name:en": "Doutor", "amenity": "cafe"}, "name": "ドトールコーヒーショップ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/星巴克": {"tags": {"name": "星巴克", "amenity": "cafe"}, "name": "星巴克", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, - "amenity/cafe/스타벅스": {"tags": {"name": "스타벅스", "amenity": "cafe"}, "name": "스타벅스", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating"], "suggestion": true}, + "amenity/cafe/85度C": {"tags": {"name": "85度C", "amenity": "cafe"}, "name": "85度C", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Bar Kafe": {"tags": {"name": "Bar Kafe", "amenity": "cafe"}, "name": "Bar Kafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Barista": {"tags": {"name": "Barista", "amenity": "cafe"}, "name": "Barista", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Bonafide": {"tags": {"name": "Bonafide", "amenity": "cafe"}, "name": "Bonafide", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Cafe Coffee Day": {"tags": {"name": "Cafe Coffee Day", "amenity": "cafe"}, "name": "Cafe Coffee Day", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Cafe Nero": {"tags": {"name": "Cafe Nero", "amenity": "cafe"}, "name": "Cafe Nero", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Cafeteria": {"tags": {"name": "Cafeteria", "amenity": "cafe"}, "name": "Cafeteria", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Cafetería": {"tags": {"name": "Cafetería", "amenity": "cafe"}, "name": "Cafetería", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Caffè Nero": {"tags": {"name": "Caffè Nero", "amenity": "cafe"}, "name": "Caffè Nero", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Café Amazon": {"tags": {"name": "Café Amazon", "amenity": "cafe"}, "name": "Café Amazon", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Café Central": {"tags": {"name": "Café Central", "amenity": "cafe"}, "name": "Café Central", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Café de la Place": {"tags": {"name": "Café de la Place", "amenity": "cafe"}, "name": "Café de la Place", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Café des Sports": {"tags": {"name": "Café des Sports", "amenity": "cafe"}, "name": "Café des Sports", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Caribou Coffee": {"tags": {"name": "Caribou Coffee", "amenity": "cafe"}, "name": "Caribou Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Coffee Fellows": {"tags": {"name": "Coffee Fellows", "amenity": "cafe"}, "name": "Coffee Fellows", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Coffee House": {"tags": {"name": "Coffee House", "amenity": "cafe"}, "name": "Coffee House", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Coffee Island": {"tags": {"name": "Coffee Island", "amenity": "cafe"}, "name": "Coffee Island", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Coffee Time": {"tags": {"name": "Coffee Time", "amenity": "cafe"}, "name": "Coffee Time", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Costa": {"tags": {"name": "Costa", "amenity": "cafe"}, "name": "Costa", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Country Style": {"tags": {"name": "Country Style", "amenity": "cafe"}, "name": "Country Style", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Dunkin' Donuts": {"tags": {"name": "Dunkin' Donuts", "cuisine": "donut", "amenity": "cafe"}, "name": "Dunkin' Donuts", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Eiscafe Dolomiti": {"tags": {"name": "Eiscafe Dolomiti", "amenity": "cafe"}, "name": "Eiscafe Dolomiti", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Eiscafe Venezia": {"tags": {"name": "Eiscafe Venezia", "amenity": "cafe"}, "name": "Eiscafe Venezia", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Espresso House": {"tags": {"name": "Espresso House", "amenity": "cafe"}, "name": "Espresso House", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Havanna": {"tags": {"name": "Havanna", "amenity": "cafe"}, "name": "Havanna", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Internet Cafe": {"tags": {"name": "Internet Cafe", "amenity": "cafe"}, "name": "Internet Cafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Kafe": {"tags": {"name": "Kafe", "amenity": "cafe"}, "name": "Kafe", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Krispy Kreme": {"tags": {"name": "Krispy Kreme", "amenity": "cafe"}, "name": "Krispy Kreme", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Le Pain Quotidien": {"tags": {"name": "Le Pain Quotidien", "amenity": "cafe"}, "name": "Le Pain Quotidien", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/McCafé": {"tags": {"name": "McCafé", "amenity": "cafe", "cuisine": "coffee_shop"}, "name": "McCafé", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Peet's Coffee & Tea": {"tags": {"name": "Peet's Coffee & Tea", "amenity": "cafe"}, "name": "Peet's Coffee & Tea", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Pret A Manger": {"tags": {"name": "Pret A Manger", "amenity": "cafe"}, "name": "Pret A Manger", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Prime": {"tags": {"name": "Prime", "amenity": "cafe"}, "name": "Prime", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Second Cup": {"tags": {"name": "Second Cup", "amenity": "cafe"}, "name": "Second Cup", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Segafredo": {"tags": {"name": "Segafredo", "amenity": "cafe"}, "name": "Segafredo", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Starbucks": {"tags": {"name": "Starbucks", "cuisine": "coffee_shop", "amenity": "cafe"}, "name": "Starbucks", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/The Coffee Bean & Tea Leaf": {"tags": {"name": "The Coffee Bean & Tea Leaf", "amenity": "cafe"}, "name": "The Coffee Bean & Tea Leaf", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/The Coffee Club": {"tags": {"name": "The Coffee Club", "amenity": "cafe"}, "name": "The Coffee Club", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Tim Hortons": {"tags": {"name": "Tim Hortons", "amenity": "cafe"}, "name": "Tim Hortons", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Traveler's Coffee": {"tags": {"name": "Traveler's Coffee", "amenity": "cafe"}, "name": "Traveler's Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Wayne's Coffee": {"tags": {"name": "Wayne's Coffee", "amenity": "cafe"}, "name": "Wayne's Coffee", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Бистро": {"tags": {"name": "Бистро", "amenity": "cafe"}, "name": "Бистро", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Буфет": {"tags": {"name": "Буфет", "amenity": "cafe"}, "name": "Буфет", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Встреча": {"tags": {"name": "Встреча", "amenity": "cafe"}, "name": "Встреча", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Даблби": {"tags": {"name": "Даблби", "amenity": "cafe"}, "name": "Даблби", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Закусочная": {"tags": {"name": "Закусочная", "amenity": "cafe"}, "name": "Закусочная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Кофе Хауз": {"tags": {"name": "Кофе Хауз", "amenity": "cafe"}, "name": "Кофе Хауз", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Кофейня": {"tags": {"name": "Кофейня", "amenity": "cafe"}, "name": "Кофейня", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Лакомка": {"tags": {"name": "Лакомка", "amenity": "cafe"}, "name": "Лакомка", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Летнее кафе": {"tags": {"name": "Летнее кафе", "amenity": "cafe"}, "name": "Летнее кафе", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Пельменная": {"tags": {"name": "Пельменная", "amenity": "cafe"}, "name": "Пельменная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Пиццерия": {"tags": {"name": "Пиццерия", "amenity": "cafe"}, "name": "Пиццерия", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Рандеву": {"tags": {"name": "Рандеву", "amenity": "cafe"}, "name": "Рандеву", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Сказка": {"tags": {"name": "Сказка", "amenity": "cafe"}, "name": "Сказка", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Старбакс": {"tags": {"name": "Старбакс", "amenity": "cafe"}, "name": "Старбакс", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Столовая": {"tags": {"name": "Столовая", "amenity": "cafe"}, "name": "Столовая", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Уют": {"tags": {"name": "Уют", "amenity": "cafe"}, "name": "Уют", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Хуторок": {"tags": {"name": "Хуторок", "amenity": "cafe"}, "name": "Хуторок", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Шашлычная": {"tags": {"name": "Шашлычная", "amenity": "cafe"}, "name": "Шашлычная", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Шоколад": {"tags": {"name": "Шоколад", "amenity": "cafe"}, "name": "Шоколад", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/Шоколадница": {"tags": {"name": "Шоколадница", "amenity": "cafe"}, "name": "Шоколадница", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/ארומה": {"tags": {"name": "ארומה", "amenity": "cafe"}, "name": "ארומה", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/مقهى": {"tags": {"name": "مقهى", "amenity": "cafe"}, "name": "مقهى", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/คาเฟ่ อเมซอน": {"tags": {"name": "คาเฟ่ อเมซอน", "amenity": "cafe"}, "name": "คาเฟ่ อเมซอน", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/エクセルシオール カフェ": {"tags": {"name": "エクセルシオール カフェ", "amenity": "cafe"}, "name": "エクセルシオール カフェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/カフェ・ド・クリエ": {"tags": {"name": "カフェ・ド・クリエ", "name:en": "Cafe de CRIE", "amenity": "cafe"}, "name": "カフェ・ド・クリエ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/カフェ・ベローチェ": {"tags": {"name": "カフェ・ベローチェ", "amenity": "cafe"}, "name": "カフェ・ベローチェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/コメダ珈琲店": {"tags": {"name": "コメダ珈琲店", "amenity": "cafe"}, "name": "コメダ珈琲店", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/サンマルクカフェ": {"tags": {"name": "サンマルクカフェ", "amenity": "cafe"}, "name": "サンマルクカフェ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/スターバックス": {"tags": {"name": "スターバックス", "name:en": "Starbucks", "amenity": "cafe"}, "name": "スターバックス", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/タリーズコーヒー": {"tags": {"name": "タリーズコーヒー", "amenity": "cafe"}, "name": "タリーズコーヒー", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/ドトールコーヒーショップ": {"tags": {"name": "ドトールコーヒーショップ", "name:en": "Doutor", "amenity": "cafe"}, "name": "ドトールコーヒーショップ", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/星巴克": {"tags": {"name": "星巴克", "amenity": "cafe"}, "name": "星巴克", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, + "amenity/cafe/스타벅스": {"tags": {"name": "스타벅스", "amenity": "cafe"}, "name": "스타벅스", "icon": "maki-cafe", "geometry": ["point", "area"], "fields": ["name", "cuisine", "address", "building_area", "opening_hours", "internet_access", "internet_access/fee", "internet_access/ssid", "smoking", "outdoor_seating", "takeaway"], "suggestion": true}, "amenity/car_rental/Alamo": {"tags": {"name": "Alamo", "amenity": "car_rental"}, "name": "Alamo", "icon": "maki-car", "geometry": ["point", "area"], "fields": ["name", "operator", "payment_multi"], "suggestion": true}, "amenity/car_rental/Avis": {"tags": {"name": "Avis", "amenity": "car_rental"}, "name": "Avis", "icon": "maki-car", "geometry": ["point", "area"], "fields": ["name", "operator", "payment_multi"], "suggestion": true}, "amenity/car_rental/Budget": {"tags": {"name": "Budget", "amenity": "car_rental"}, "name": "Budget", "icon": "maki-car", "geometry": ["point", "area"], "fields": ["name", "operator", "payment_multi"], "suggestion": true}, diff --git a/data/presets/presets/highway/speed_camera.json b/data/presets/presets/highway/speed_camera.json index f35daf8a8..b58332836 100644 --- a/data/presets/presets/highway/speed_camera.json +++ b/data/presets/presets/highway/speed_camera.json @@ -6,7 +6,8 @@ ], "fields": [ "direction", - "ref" + "ref", + "maxspeed" ], "tags": { "highway": "speed_camera" From 10dbbbe3302ccace60b567ee06e527dd8e81a887 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Oct 2018 11:05:53 -0400 Subject: [PATCH 42/51] Update data/presets/fields/to.json Co-Authored-By: castriganoj --- data/presets/fields/to.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/presets/fields/to.json b/data/presets/fields/to.json index c3236efba..b95ac745d 100644 --- a/data/presets/fields/to.json +++ b/data/presets/fields/to.json @@ -1,5 +1,5 @@ { - "key": "to ", + "key": "to", "type": "text", "label": "To" -} \ No newline at end of file +} From 7709a7ef3f55a87321b4a0a15156272985bf6f90 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 24 Oct 2018 13:29:54 -0400 Subject: [PATCH 43/51] npm run build to remove extra space --- data/presets.yaml | 2 +- data/presets/fields.json | 2 +- data/taginfo.json | 2 +- dist/locales/en.json | 14 -------------- 4 files changed, 3 insertions(+), 17 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 201c35451..53dc28ae7 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -1804,7 +1804,7 @@ en: # takeaway field placeholder placeholder: 'Yes, No, Takeaway Only...' to: - # to =* + # to=* label: To toilets/disposal: # 'toilets:disposal=*' diff --git a/data/presets/fields.json b/data/presets/fields.json index 5ad079d20..a16c79ab8 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -304,7 +304,7 @@ "switch": {"key": "switch", "type": "combo", "label": "Type", "strings": {"options": {"mechanical": "Mechanical", "circuit_breaker": "Circuit Breaker", "disconnector": "Disconnector", "earthing": "Earthing"}}}, "tactile_paving": {"key": "tactile_paving", "type": "check", "label": "Tactile Paving"}, "takeaway": {"key": "takeaway", "type": "combo", "label": "Takeaway", "placeholder": "Yes, No, Takeaway Only...", "strings": {"options": {"yes": "Yes", "no": "No", "only": "Takeaway Only"}}}, - "to": {"key": "to ", "type": "text", "label": "To"}, + "to": {"key": "to", "type": "text", "label": "To"}, "toilets/disposal": {"key": "toilets:disposal", "type": "combo", "label": "Disposal", "strings": {"options": {"flush": "Flush", "pitlatrine": "Pit/Latrine", "chemical": "Chemical", "bucket": "Bucket"}}}, "toll": {"key": "toll", "type": "check", "label": "Toll"}, "tomb": {"key": "tomb", "type": "typeCombo", "label": "Type"}, diff --git a/data/taginfo.json b/data/taginfo.json index 9d364ebf9..e907ea4ad 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -7494,7 +7494,7 @@ {"key": "takeaway", "value": "yes", "description": "Takeaway"}, {"key": "takeaway", "value": "no", "description": "Takeaway"}, {"key": "takeaway", "value": "only", "description": "Takeaway"}, - {"key": "to ", "description": "To"}, + {"key": "to", "description": "To"}, {"key": "toilets:disposal", "value": "flush", "description": "Disposal"}, { "key": "toilets:disposal", diff --git a/dist/locales/en.json b/dist/locales/en.json index 5152e14f2..5d261942f 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -7418,13 +7418,6 @@ "description": "Japan GSI Standard Map. Widely covered.", "name": "Japan GSI Standard Map" }, - "helsingborg-orto": { - "attribution": { - "text": "© Helsingborg municipality" - }, - "description": "Orthophotos from the municipality of Helsingborg 2016, public domain", - "name": "Helsingborg Orthophoto" - }, "hike_n_bike": { "attribution": { "text": "© OpenStreetMap contributors" @@ -7516,13 +7509,6 @@ }, "name": "Stamen Terrain" }, - "stockholm-orto": { - "attribution": { - "text": "© Stockholm municipality, CC0" - }, - "description": "Orthophotos from the municipality of Stockholm 2015, CC0 license", - "name": "Stockholm Orthophoto" - }, "tf-cycle": { "attribution": { "text": "Maps © Thunderforest, Data © OpenStreetMap contributors" From 81feb1cd990993cc432ef13dea5ec74805a6ab75 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:17:28 -0700 Subject: [PATCH 44/51] Adds hover-highlighting for relations in the raw membership list Moves hover-highlighting behavior to its own function Hover-highlighting now correctly highlights all members of the target relation --- modules/ui/entity_highlight.js | 17 +++++++++++++++++ modules/ui/raw_member_editor.js | 10 ++++------ modules/ui/raw_membership_editor.js | 10 ++++++++++ modules/ui/selection_list.js | 8 ++++---- 4 files changed, 35 insertions(+), 10 deletions(-) create mode 100644 modules/ui/entity_highlight.js diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js new file mode 100644 index 000000000..a9f222c1c --- /dev/null +++ b/modules/ui/entity_highlight.js @@ -0,0 +1,17 @@ +import _forEach from 'lodash-es/forEach'; + +export function highlightEntity(context, entity, highlighted) { + + // highlight the member feature in the map while hovering on the list item + var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; + context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); + if (entity.members) { + // recursively highlight members so that relations will appear highlighted + _forEach(entity.members, function(member){ + if (member.id && context.hasEntity(member.id)) { + highlightEntity(context, context.entity(member.id), highlighted); + } + }); + } + +} diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index a37153464..bf820776a 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -7,6 +7,7 @@ import { d3combobox as d3_combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { actionChangeMember, actionDeleteMember } from '../actions'; +import { highlightEntity } from './entity_highlight'; import { modeBrowse, modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -37,8 +38,7 @@ export function uiRawMemberEditor(context) { context.map().zoomTo(entity); // highlight the feature in case it wasn't previously on-screen - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d.member, true); } @@ -134,13 +134,11 @@ export function uiRawMemberEditor(context) { .each(function(d) { if (d.member) { - // highlight the member feature in the map while hovering on the list item - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; d3_select(this).on('mouseover', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d.member, true); }); d3_select(this).on('mouseout', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); + highlightEntity(context, d.member, false); }); var label = d3_select(this).append('label') diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index 0fadc1b7b..1c71eeb2c 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -19,6 +19,7 @@ import { actionDeleteMember } from '../actions'; +import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity, osmRelation } from '../osm'; import { services } from '../services'; @@ -173,6 +174,15 @@ export function uiRawMembershipEditor(context) { .append('li') .attr('class', 'member-row member-row-normal form-field'); + enter.each(function(d){ + d3_select(this).on('mouseover', function() { + highlightEntity(context, d.relation, true); + }); + d3_select(this).on('mouseout', function() { + highlightEntity(context, d.relation, false); + }); + }); + var label = enter .append('label') .attr('class', 'form-label') diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 6362a7cea..0345943bf 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -4,6 +4,7 @@ import { } from 'd3-selection'; import { t } from '../util/locale'; +import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -69,13 +70,12 @@ export function uiSelectionList(context, selectedIDs) { enter .each(function(d) { - // highlight the feature in the map while hovering on the list item - var selectorPrefix = d.type === 'node' ? 'g.' : 'path.'; + d3_select(this).on('mouseover', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', true); + highlightEntity(context, d, true); }); d3_select(this).on('mouseout', function() { - context.surface().selectAll(selectorPrefix+d.id).classed('highlighted', false); + highlightEntity(context, d, false); }); }); From ddb6e1b119d4c37b3e500a322263336dd3254d6d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:27:37 -0700 Subject: [PATCH 45/51] Fixes comments related to hover-highlighting --- modules/ui/entity_highlight.js | 2 +- modules/ui/raw_member_editor.js | 1 + modules/ui/raw_membership_editor.js | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js index a9f222c1c..cc92a0250 100644 --- a/modules/ui/entity_highlight.js +++ b/modules/ui/entity_highlight.js @@ -2,8 +2,8 @@ import _forEach from 'lodash-es/forEach'; export function highlightEntity(context, entity, highlighted) { - // highlight the member feature in the map while hovering on the list item var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; + // set the class for the SVG to add or remove the highlighted styling context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); if (entity.members) { // recursively highlight members so that relations will appear highlighted diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index bf820776a..a2a70be89 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -134,6 +134,7 @@ export function uiRawMemberEditor(context) { .each(function(d) { if (d.member) { + // highlight the member feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d.member, true); }); diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index 1c71eeb2c..a63c9e668 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -175,6 +175,7 @@ export function uiRawMembershipEditor(context) { .attr('class', 'member-row member-row-normal form-field'); enter.each(function(d){ + // highlight the relation in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d.relation, true); }); From 51386e03ae93943af718aab10e6457e3dafac6df Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 24 Oct 2018 22:30:29 -0700 Subject: [PATCH 46/51] Restores the hover-highlight comment in selection_list.js --- modules/ui/selection_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 0345943bf..0e12a66a6 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -70,7 +70,7 @@ export function uiSelectionList(context, selectedIDs) { enter .each(function(d) { - + // highlight the feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { highlightEntity(context, d, true); }); From 483d3abbdc784d7c6cf85ce9022be395c387ebdf Mon Sep 17 00:00:00 2001 From: wouter van der plas Date: Thu, 25 Oct 2018 22:02:06 +0200 Subject: [PATCH 47/51] adds a place=city_block preset --- data/presets/presets/place/city_block.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 data/presets/presets/place/city_block.json diff --git a/data/presets/presets/place/city_block.json b/data/presets/presets/place/city_block.json new file mode 100644 index 000000000..64bfa83f1 --- /dev/null +++ b/data/presets/presets/place/city_block.json @@ -0,0 +1,13 @@ +{ + "fields": [ + "name" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "place": "city_block" + }, + "name": "City block" +} From 8606121aa4a1aec26a2d6021fded67a8c414be80 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 25 Oct 2018 17:45:54 -0400 Subject: [PATCH 48/51] npm run build for new preset, and correct capitalization --- data/presets.yaml | 4 ++++ data/presets/presets.json | 1 + data/presets/presets/place/city_block.json | 23 +++++++++++----------- data/taginfo.json | 7 +++++++ dist/locales/en.json | 4 ++++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 53dc28ae7..5fa2399f2 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -5040,6 +5040,10 @@ en: # place=city name: City terms: '' + place/city_block: + # place=city_block + name: City Block + terms: '' place/farm: # place=farm name: Farm diff --git a/data/presets/presets.json b/data/presets/presets.json index 27dbd491f..c6d04331d 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -668,6 +668,7 @@ "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"}, "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"}, "place/hamlet": {"icon": "maki-triangle-stroked", "fields": ["name", "population"], "geometry": ["point", "area"], "tags": {"place": "hamlet"}, "name": "Hamlet"}, "place/island": {"icon": "maki-mountain", "geometry": ["point", "area"], "fields": ["name"], "terms": ["archipelago", "atoll", "bar", "cay", "isle", "islet", "key", "reef"], "tags": {"place": "island"}, "name": "Island"}, diff --git a/data/presets/presets/place/city_block.json b/data/presets/presets/place/city_block.json index 64bfa83f1..9dcb2d3ed 100644 --- a/data/presets/presets/place/city_block.json +++ b/data/presets/presets/place/city_block.json @@ -1,13 +1,14 @@ { - "fields": [ - "name" - ], - "geometry": [ - "point", - "area" - ], - "tags": { - "place": "city_block" - }, - "name": "City block" + "icon": "maki-triangle-stroked", + "fields": [ + "name" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "place": "city_block" + }, + "name": "City Block" } diff --git a/data/taginfo.json b/data/taginfo.json index e907ea4ad..525b902e9 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -4474,6 +4474,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/farm-15.svg?sanitize=true" }, + { + "key": "place", + "value": "city_block", + "description": "City Block", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/triangle-stroked-15.svg?sanitize=true" + }, { "key": "place", "value": "city", diff --git a/dist/locales/en.json b/dist/locales/en.json index 5d261942f..ec8f839ab 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -5830,6 +5830,10 @@ "name": "Farm", "terms": "" }, + "place/city_block": { + "name": "City Block", + "terms": "" + }, "place/city": { "name": "City", "terms": "" From 1161767932dd04b04b8704eea51a4bc9ab9c3f74 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Fri, 26 Oct 2018 18:39:01 -0700 Subject: [PATCH 49/51] Moves the entity highlighting function to util/util.js Breaks out the code for getting a selector for entities and all their descendants into a generic function Accounts for circular relations when recursively getting all relation member IDs --- modules/ui/entity_highlight.js | 17 --------------- modules/ui/raw_member_editor.js | 10 ++++----- modules/ui/raw_membership_editor.js | 7 +++--- modules/ui/selection_list.js | 7 +++--- modules/util/index.js | 2 ++ modules/util/util.js | 33 +++++++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 30 deletions(-) delete mode 100644 modules/ui/entity_highlight.js diff --git a/modules/ui/entity_highlight.js b/modules/ui/entity_highlight.js deleted file mode 100644 index cc92a0250..000000000 --- a/modules/ui/entity_highlight.js +++ /dev/null @@ -1,17 +0,0 @@ -import _forEach from 'lodash-es/forEach'; - -export function highlightEntity(context, entity, highlighted) { - - var selectorPrefix = entity.type === 'node' ? 'g.' : 'path.'; - // set the class for the SVG to add or remove the highlighted styling - context.surface().selectAll(selectorPrefix+entity.id).classed('highlighted', highlighted); - if (entity.members) { - // recursively highlight members so that relations will appear highlighted - _forEach(entity.members, function(member){ - if (member.id && context.hasEntity(member.id)) { - highlightEntity(context, context.entity(member.id), highlighted); - } - }); - } - -} diff --git a/modules/ui/raw_member_editor.js b/modules/ui/raw_member_editor.js index a2a70be89..0d1080dca 100644 --- a/modules/ui/raw_member_editor.js +++ b/modules/ui/raw_member_editor.js @@ -7,7 +7,6 @@ import { d3combobox as d3_combobox } from '../lib/d3.combobox.js'; import { t } from '../util/locale'; import { actionChangeMember, actionDeleteMember } from '../actions'; -import { highlightEntity } from './entity_highlight'; import { modeBrowse, modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; @@ -16,7 +15,8 @@ import { uiDisclosure } from './disclosure'; import { utilDisplayName, utilDisplayType, - utilNoAuto + utilNoAuto, + utilHighlightEntity } from '../util'; @@ -38,7 +38,7 @@ export function uiRawMemberEditor(context) { context.map().zoomTo(entity); // highlight the feature in case it wasn't previously on-screen - highlightEntity(context, d.member, true); + utilHighlightEntity(d.id, true, context); } @@ -136,10 +136,10 @@ export function uiRawMemberEditor(context) { // highlight the member feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d.member, true); + utilHighlightEntity(d.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d.member, false); + utilHighlightEntity(d.id, false, context); }); var label = d3_select(this).append('label') diff --git a/modules/ui/raw_membership_editor.js b/modules/ui/raw_membership_editor.js index a63c9e668..f856cd7ab 100644 --- a/modules/ui/raw_membership_editor.js +++ b/modules/ui/raw_membership_editor.js @@ -19,13 +19,12 @@ import { actionDeleteMember } from '../actions'; -import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity, osmRelation } from '../osm'; import { services } from '../services'; import { svgIcon } from '../svg'; import { uiDisclosure } from './disclosure'; -import { utilDisplayName, utilNoAuto } from '../util'; +import { utilDisplayName, utilNoAuto, utilHighlightEntity } from '../util'; export function uiRawMembershipEditor(context) { @@ -177,10 +176,10 @@ export function uiRawMembershipEditor(context) { enter.each(function(d){ // highlight the relation in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d.relation, true); + utilHighlightEntity(d.relation.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d.relation, false); + utilHighlightEntity(d.relation.id, false, context); }); }); diff --git a/modules/ui/selection_list.js b/modules/ui/selection_list.js index 0e12a66a6..f7f3a3ee7 100644 --- a/modules/ui/selection_list.js +++ b/modules/ui/selection_list.js @@ -4,11 +4,10 @@ import { } from 'd3-selection'; import { t } from '../util/locale'; -import { highlightEntity } from './entity_highlight'; import { modeSelect } from '../modes'; import { osmEntity } from '../osm'; import { svgIcon } from '../svg'; -import { utilDisplayName } from '../util'; +import { utilDisplayName, utilHighlightEntity } from '../util'; export function uiSelectionList(context, selectedIDs) { @@ -72,10 +71,10 @@ export function uiSelectionList(context, selectedIDs) { .each(function(d) { // highlight the feature in the map while hovering on the list item d3_select(this).on('mouseover', function() { - highlightEntity(context, d, true); + utilHighlightEntity(d.id, true, context); }); d3_select(this).on('mouseout', function() { - highlightEntity(context, d, false); + utilHighlightEntity(d.id, false, context); }); }); diff --git a/modules/util/index.js b/modules/util/index.js index 16460cd14..a0269240f 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -7,12 +7,14 @@ export { utilDisplayType } from './util'; export { utilEditDistance } from './util'; export { utilEntitySelector } from './util'; export { utilEntityOrMemberSelector } from './util'; +export { utilEntityOrDeepMemberSelector } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; +export { utilHighlightEntity } from './util'; export { utilIdleWorker } from './idle_worker'; export { utilNoAuto } from './util'; export { utilPrefixCSSProperty } from './util'; diff --git a/modules/util/util.js b/modules/util/util.js index 6b03c9f4c..5eb821ced 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -34,6 +34,32 @@ 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; + // mark the entity as seen + seen[id] = true; + // add the id; + allIDs.push(id); + if (graph.hasEntity(id)) { + var entity = graph.entity(id); + if (entity.type === 'relation' && entity.members) { + entity.members.forEach(function(member){ + addEntityAndMembersIfNotYetSeen(member.id); + }); + } + } + } + ids.forEach(function(id) { + addEntityAndMembersIfNotYetSeen(id); + }); + return utilEntitySelector(allIDs); +} + + export function utilGetAllNodes(ids, graph) { var seen = {}; var nodes = []; @@ -282,3 +308,10 @@ export function utilHashcode(str) { } return hash; } + +// Adds or removes highlight styling for the specified entity's SVG elements in the map. +export function utilHighlightEntity(id, highlighted, context) { + context.surface() + .selectAll(utilEntityOrDeepMemberSelector([id], context.graph())) + .classed('highlighted', highlighted); +} From bc0ec1f380d60d9a54b5c9ce021164508a282e61 Mon Sep 17 00:00:00 2001 From: n42k Date: Sat, 27 Oct 2018 16:21:59 +0100 Subject: [PATCH 50/51] Fixed using help's navigation buttons not resetting the help pane to the top. --- modules/ui/help.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/help.js b/modules/ui/help.js index 29942ca2a..a8b5bbbbd 100644 --- a/modules/ui/help.js +++ b/modules/ui/help.js @@ -318,7 +318,7 @@ export function uiHelp(context) { function clickHelp(d, i) { var rtl = (textDirection === 'rtl'); - pane.property('scrollTop', 0); + content.property('scrollTop', 0); doctitle.html(d.title); body.html(d.html); From 4037d0306d58d592df91fc6dc487fe188029254b Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sat, 27 Oct 2018 10:55:10 -0700 Subject: [PATCH 51/51] Makes the styling of paths with location=underwater the same as tunnels or location=underground --- css/50_misc.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/css/50_misc.css b/css/50_misc.css index 3ebbf2a5b..d78560f26 100644 --- a/css/50_misc.css +++ b/css/50_misc.css @@ -163,11 +163,13 @@ path.casing.tag-highway-bridleway.tag-bridge { /* tunnels */ path.stroke.tag-tunnel, -path.line.stroke.tag-location-underground { +path.line.stroke.tag-location-underground, +path.line.stroke.tag-location-underwater { stroke-opacity: 0.3; } path.casing.tag-tunnel, -path.line.casing.tag-location-underground { +path.line.casing.tag-location-underground, +path.line.stroke.tag-location-underwater { stroke-opacity: 0.5; stroke-linecap: butt; stroke-dasharray: none;