From 5f20218f1fb49be429e2e544c027926a20d7b3d7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 02:15:28 -0500 Subject: [PATCH] Add validation warning for generic names (closes #5590) --- data/core.yaml | 2 + data/presets/presets.json | 4 +- .../presets/amenity/vending_machine/fuel.json | 3 +- data/presets/presets/shop/fuel.json | 3 +- dist/locales/en.json | 2 + modules/ui/commit_warnings.js | 5 +- modules/validations/deprecated_tag.js | 4 +- modules/validations/generic_name.js | 47 +++++++++++++++++++ modules/validations/index.js | 1 + modules/validations/many_deletions.js | 13 ++--- modules/validations/mapcss_checks.js | 2 + modules/validations/missing_tag.js | 8 ++-- modules/validations/tag_suggests_area.js | 6 +-- 13 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 modules/validations/generic_name.js diff --git a/data/core.yaml b/data/core.yaml index c67c8b09c..be05db377 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -629,6 +629,8 @@ en: validations: disconnected_highway: Disconnected highway disconnected_highway_tooltip: "Roads should be connected to other roads or building entrances." + generic_name: Possible generic name + generic_name_tooltip: 'This feature seems to have a generic name "{name}". Please only use the name field to record the official name of a feature.' old_multipolygon: Multipolygon tags on outer way old_multipolygon_tooltip: "This style of multipolygon is deprecated. Please assign the tags to the parent multipolygon instead of the outer way." untagged_point: Untagged point diff --git a/data/presets/presets.json b/data/presets/presets.json index 90024dbd0..7ed15f534 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -191,7 +191,7 @@ "amenity/vending_machine/excrement_bags": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "fee", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["excrement bags", "poop", "dog", "animal"], "tags": {"amenity": "vending_machine", "vending": "excrement_bags"}, "reference": {"key": "vending", "value": "excrement_bags"}, "name": "Excrement Bag Vending Machine"}, "amenity/vending_machine/feminine_hygiene": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["condom", "tampon", "pad", "woman", "women", "menstrual hygiene products", "personal care"], "tags": {"amenity": "vending_machine", "vending": "feminine_hygiene"}, "reference": {"key": "vending", "value": "feminine_hygiene"}, "name": "Feminine Hygiene Vending Machine"}, "amenity/vending_machine/food": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["food"], "tags": {"amenity": "vending_machine", "vending": "food"}, "reference": {"key": "vending", "value": "food"}, "name": "Food Vending Machine"}, - "amenity/vending_machine/fuel": {"icon": "maki-fuel", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["petrol", "fuel", "gasoline", "propane", "diesel", "lng", "cng", "biodiesel"], "tags": {"amenity": "vending_machine", "vending": "fuel"}, "reference": {"key": "vending", "value": "fuel"}, "name": "Gas Pump"}, + "amenity/vending_machine/fuel": {"icon": "maki-fuel", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["petrol", "fuel", "gasoline", "propane", "diesel", "lng", "cng", "biodiesel"], "tags": {"amenity": "vending_machine", "vending": "fuel"}, "reference": {"key": "vending", "value": "fuel"}, "name": "Gas Pump", "matchScore": 0.5}, "amenity/vending_machine/ice_cream": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["chocolate", "ice cream", "frozen", "popsicle", "vanilla"], "tags": {"amenity": "vending_machine", "vending": "ice_cream"}, "reference": {"key": "vending", "value": "ice_cream"}, "name": "Ice Cream Vending Machine"}, "amenity/vending_machine/newspapers": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "fee", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["newspaper"], "tags": {"amenity": "vending_machine", "vending": "newspapers"}, "reference": {"key": "vending", "value": "newspapers"}, "name": "Newspaper Vending Machine"}, "amenity/vending_machine/parcel_pickup_dropoff": {"icon": "temaki-vending_machine", "fields": ["vending", "operator", "payment_multi", "currency_multi"], "geometry": ["point"], "terms": ["mail", "parcel", "pickup"], "tags": {"amenity": "vending_machine", "vending": "parcel_pickup;parcel_mail_in"}, "reference": {"key": "vending", "value": "parcel_pickup;parcel_mail_in"}, "name": "Parcel Pickup/Dropoff Locker"}, @@ -889,7 +889,7 @@ "shop/fishing": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fishing"}, "name": "Fishing Shop"}, "shop/florist": {"icon": "maki-florist", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["flower"], "tags": {"shop": "florist"}, "name": "Florist"}, "shop/frame": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "frame"}, "terms": ["art*", "paint*", "photo*", "frame"], "name": "Framing Shop"}, - "shop/fuel": {"icon": "maki-shop", "fields": ["name", "operator", "address", "fuel_multi", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fuel"}, "name": "Fuel Shop"}, + "shop/fuel": {"icon": "maki-shop", "fields": ["name", "operator", "address", "fuel_multi", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fuel"}, "name": "Fuel Shop", "matchScore": 0.5}, "shop/funeral_directors": {"icon": "maki-cemetery", "fields": ["name", "operator", "address", "building_area", "religion", "denomination"], "geometry": ["point", "area"], "terms": ["undertaker", "memorial home"], "tags": {"shop": "funeral_directors"}, "name": "Funeral Home"}, "shop/furniture": {"icon": "fas-couch", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["chair", "sofa", "table"], "tags": {"shop": "furniture"}, "name": "Furniture Store"}, "shop/garden_centre": {"icon": "maki-garden-centre", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["landscape", "mulch", "shrub", "tree"], "tags": {"shop": "garden_centre"}, "name": "Garden Center"}, diff --git a/data/presets/presets/amenity/vending_machine/fuel.json b/data/presets/presets/amenity/vending_machine/fuel.json index 0c6952f25..febabeec8 100644 --- a/data/presets/presets/amenity/vending_machine/fuel.json +++ b/data/presets/presets/amenity/vending_machine/fuel.json @@ -27,5 +27,6 @@ "key": "vending", "value": "fuel" }, - "name": "Gas Pump" + "name": "Gas Pump", + "matchScore": 0.5 } diff --git a/data/presets/presets/shop/fuel.json b/data/presets/presets/shop/fuel.json index e0a3c7dbb..5a1690d04 100644 --- a/data/presets/presets/shop/fuel.json +++ b/data/presets/presets/shop/fuel.json @@ -16,5 +16,6 @@ "tags": { "shop": "fuel" }, - "name": "Fuel Shop" + "name": "Fuel Shop", + "matchScore": 0.5 } diff --git a/dist/locales/en.json b/dist/locales/en.json index 288a7d483..f9bf8e766 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -767,6 +767,8 @@ "validations": { "disconnected_highway": "Disconnected highway", "disconnected_highway_tooltip": "Roads should be connected to other roads or building entrances.", + "generic_name": "Possible generic name", + "generic_name_tooltip": "This feature seems to have a generic name \"{name}\". Please only use the name field to record the official name of a feature.", "old_multipolygon": "Multipolygon tags on outer way", "old_multipolygon_tooltip": "This style of multipolygon is deprecated. Please assign the tags to the parent multipolygon instead of the outer way.", "untagged_point": "Untagged point", diff --git a/modules/ui/commit_warnings.js b/modules/ui/commit_warnings.js index 8b517c9c3..afaf01860 100644 --- a/modules/ui/commit_warnings.js +++ b/modules/ui/commit_warnings.js @@ -25,7 +25,10 @@ export function uiCommitWarnings(context) { }, {}); _forEach(validations, function(instances, type) { - instances = _uniqBy(instances, function(val) { return val.id + '_' + val.message.replace(/\s+/g,''); }); + instances = _uniqBy(instances, function(val) { + return val.entity || (val.id + '_' + val.message.replace(/\s+/g,'')); + }); + var section = type + '-section'; var instanceItem = type + '-item'; diff --git a/modules/validations/deprecated_tag.js b/modules/validations/deprecated_tag.js index 3690e714e..1ed3f1dcd 100644 --- a/modules/validations/deprecated_tag.js +++ b/modules/validations/deprecated_tag.js @@ -9,8 +9,8 @@ export function validationDeprecatedTag() { var validation = function(changes) { var warnings = []; for (var i = 0; i < changes.created.length; i++) { - var change = changes.created[i], - deprecatedTags = change.deprecatedTags(); + var change = changes.created[i]; + var deprecatedTags = change.deprecatedTags(); if (!_isEmpty(deprecatedTags)) { var tags = utilTagText({ tags: deprecatedTags }); diff --git a/modules/validations/generic_name.js b/modules/validations/generic_name.js new file mode 100644 index 000000000..2e560d577 --- /dev/null +++ b/modules/validations/generic_name.js @@ -0,0 +1,47 @@ +import { t } from '../util/locale'; +import { discardNames } from '../../node_modules/name-suggestion-index/config/filters.json'; + +export function validationGenericName() { + + function isGenericName(entity) { + var name = entity.tags.name; + if (!name) return false; + + if (entity.tags.amenity === name || + entity.tags.leisure === name || + entity.tags.shop === name || + entity.tags.man_made === name || + entity.tags.tourism === name) { + return name; + } + + for (var i = 0; i < discardNames.length; i++) { + var re = new RegExp(discardNames[i], 'i'); + if (re.test(name)) { + return name; + } + } + + return false; + } + + + return function validation(changes) { + var warnings = []; + + for (var i = 0; i < changes.created.length; i++) { + var change = changes.created[i]; + var generic = isGenericName(change); + if (generic) { + warnings.push({ + id: 'generic_name', + message: t('validations.generic_name'), + tooltip: t('validations.generic_name_tooltip', { name: generic }), + entity: change + }); + } + } + + return warnings; + }; +} diff --git a/modules/validations/index.js b/modules/validations/index.js index 112b38e6c..0e562fc80 100644 --- a/modules/validations/index.js +++ b/modules/validations/index.js @@ -1,5 +1,6 @@ export { validationDeprecatedTag } from './deprecated_tag'; export { validationDisconnectedHighway } from './disconnected_highway'; +export { validationGenericName } from './generic_name.js'; export { validationManyDeletions } from './many_deletions'; export { validationMapCSSChecks } from './mapcss_checks'; export { validationMissingTag } from './missing_tag'; diff --git a/modules/validations/many_deletions.js b/modules/validations/many_deletions.js index b0b0c437e..08e01dc96 100644 --- a/modules/validations/many_deletions.js +++ b/modules/validations/many_deletions.js @@ -6,19 +6,20 @@ export function validationManyDeletions() { var validation = function(changes, graph) { var warnings = []; - var nodes=0, ways=0, areas=0, relations=0; + var nodes = 0, ways = 0, areas = 0, relations = 0; changes.deleted.forEach(function(c) { - if (c.type === 'node') {nodes++;} - else if (c.type === 'way' && c.geometry(graph) === 'line') {ways++;} - else if (c.type === 'way' && c.geometry(graph) === 'area') {areas++;} - else if (c.type === 'relation') {relations++;} + if (c.type === 'node') { nodes++; } + else if (c.type === 'way' && c.geometry(graph) === 'line') { ways++; } + else if (c.type === 'way' && c.geometry(graph) === 'area') { areas++; } + else if (c.type === 'relation') { relations++; } }); if (changes.deleted.length > threshold) { warnings.push({ id: 'many_deletions', message: t('validations.many_deletions', - { n: changes.deleted.length, p: nodes, l: ways, a:areas, r: relations }) + { n: changes.deleted.length, p: nodes, l: ways, a:areas, r: relations } + ) }); } diff --git a/modules/validations/mapcss_checks.js b/modules/validations/mapcss_checks.js index 13fa6fe2b..bf87e9cf6 100644 --- a/modules/validations/mapcss_checks.js +++ b/modules/validations/mapcss_checks.js @@ -21,5 +21,7 @@ export function validationMapCSSChecks() { return warnings; }; + + return validation; } diff --git a/modules/validations/missing_tag.js b/modules/validations/missing_tag.js index 6785ab247..0f07af39c 100644 --- a/modules/validations/missing_tag.js +++ b/modules/validations/missing_tag.js @@ -11,12 +11,12 @@ export function validationMissingTag() { } var validation = function(changes, graph) { - var types = ['point', 'line', 'area', 'relation'], - warnings = []; + var types = ['point', 'line', 'area', 'relation']; + var warnings = []; for (var i = 0; i < changes.created.length; i++) { - var change = changes.created[i], - geometry = change.geometry(graph); + var change = changes.created[i]; + var geometry = change.geometry(graph); if (types.indexOf(geometry) !== -1 && !hasTags(change, graph)) { warnings.push({ diff --git a/modules/validations/tag_suggests_area.js b/modules/validations/tag_suggests_area.js index a631f4a79..c6f3d7d6b 100644 --- a/modules/validations/tag_suggests_area.js +++ b/modules/validations/tag_suggests_area.js @@ -27,9 +27,9 @@ export function validationTagSuggestsArea() { var validation = function(changes, graph) { var warnings = []; for (var i = 0; i < changes.created.length; i++) { - var change = changes.created[i], - geometry = change.geometry(graph), - suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined); + var change = changes.created[i]; + var geometry = change.geometry(graph); + var suggestion = (geometry === 'line' ? tagSuggestsArea(change.tags) : undefined); if (suggestion) { warnings.push({