From dd47acd64d901754f28bcb94cb97e5106765a061 Mon Sep 17 00:00:00 2001 From: hikemaniac <31667811+hikemaniac@users.noreply.github.com> Date: Sun, 9 Dec 2018 14:43:14 +0100 Subject: [PATCH 001/114] Added more presets for shops --- data/presets.yaml | 17 ++++++++ data/presets/presets.json | 12 +++--- data/presets/presets/shop/fireplace.json | 24 +++++++++++ data/presets/presets/shop/fishing.json | 19 +++++++++ data/presets/presets/shop/hunting.json | 19 +++++++++ data/presets/presets/shop/water.json | 19 +++++++++ data/taginfo.json | 28 +++++++++++++ dist/locales/en.json | 53 +++++++++++------------- 8 files changed, 157 insertions(+), 34 deletions(-) create mode 100644 data/presets/presets/shop/fireplace.json create mode 100644 data/presets/presets/shop/fishing.json create mode 100644 data/presets/presets/shop/hunting.json create mode 100644 data/presets/presets/shop/water.json diff --git a/data/presets.yaml b/data/presets.yaml index 45a4bae42..d3733f9ad 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -6026,6 +6026,15 @@ en: # shop=fashion name: Fashion Store terms: '' + shop/fireplace: + # shop=fireplace + name: Fireplace Store + # 'terms: fireplace,stove,masonry heater' + terms: '' + shop/fishing: + # shop=fishing + name: Fishing Shop + terms: '' shop/fishmonger: # shop=fishmonger name: Fishmonger @@ -6109,6 +6118,10 @@ en: name: Houseware Store # 'terms: home,household' terms: '' + shop/hunting: + # shop=hunting + name: Hunting Shop + terms: '' shop/interior_decoration: # shop=interior_decoration name: Interior Decoration Store @@ -6362,6 +6375,10 @@ en: # shop=watches name: Watches Shop terms: '' + shop/water: + # shop=water + name: Drinking Water Shop + terms: '' shop/water_sports: # shop=water_sports name: Watersport/Swim Shop diff --git a/data/presets/presets.json b/data/presets/presets.json index b22898f12..4c29da297 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -879,6 +879,8 @@ "shop/fabric": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["sew"], "tags": {"shop": "fabric"}, "name": "Fabric Store"}, "shop/farm": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["farm shop", "farm stand"], "tags": {"shop": "farm"}, "name": "Produce Stand"}, "shop/fashion": {"icon": "maki-shop", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fashion"}, "name": "Fashion Store"}, + "shop/fireplace": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["fireplace", "stove", "masonry heater"], "tags": {"shop": "fireplace"}, "name": "Fireplace Store"}, + "shop/fishing": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "fishing"}, "name": "Fishing Shop"}, "shop/florist": {"icon": "maki-florist", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["flower"], "tags": {"shop": "florist"}, "name": "Florist"}, "shop/frame": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "frame"}, "terms": ["art*", "paint*", "photo*", "frame"], "name": "Framing Shop"}, "shop/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"}, @@ -895,6 +897,7 @@ "shop/herbalist": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "herbalist"}, "name": "Herbalist"}, "shop/hifi": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["stereo", "video"], "tags": {"shop": "hifi"}, "name": "Hifi Store"}, "shop/houseware": {"icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["home", "household"], "tags": {"shop": "houseware"}, "name": "Houseware Store"}, + "shop/hunting": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "hunting"}, "name": "Hunting Shop"}, "shop/interior_decoration": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration"}, "name": "Interior Decoration Store"}, "shop/jewelry": {"icon": "maki-jewelry-store", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["diamond", "gem", "ring"], "tags": {"shop": "jewelry"}, "name": "Jeweler"}, "shop/kiosk": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi", "levels"], "geometry": ["point", "area"], "tags": {"shop": "kiosk"}, "name": "Kiosk"}, @@ -953,6 +956,7 @@ "shop/video": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["DVD"], "tags": {"shop": "video"}, "name": "Video Store"}, "shop/watches": {"icon": "maki-watch", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "watches"}, "name": "Watches Shop"}, "shop/water_sports": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "water_sports"}, "name": "Watersport/Swim Shop"}, + "shop/water": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "water"}, "name": "Drinking Water Shop"}, "shop/weapons": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["ammo", "gun", "knife", "knives"], "tags": {"shop": "weapons"}, "name": "Weapon Shop"}, "shop/wholesale": {"icon": "maki-warehouse", "fields": ["name", "operator", "wholesale", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["warehouse club", "cash and carry"], "tags": {"shop": "wholesale"}, "name": "Wholesale Store"}, "shop/window_blind": {"icon": "temaki-window", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "window_blind"}, "name": "Window Blind Store"}, @@ -1744,7 +1748,6 @@ "amenity/fuel/Slovnaft": {"name": "Slovnaft", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1587563"}, "addTags": {"amenity": "fuel", "brand": "Slovnaft", "brand:wikidata": "Q1587563", "brand:wikipedia": "en:Slovnaft", "name": "Slovnaft"}, "removeTags": {"amenity": "fuel", "brand": "Slovnaft", "brand:wikidata": "Q1587563", "brand:wikipedia": "en:Slovnaft", "name": "Slovnaft"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Socar": {"name": "Socar", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1622293"}, "addTags": {"amenity": "fuel", "brand": "Socar", "brand:wikidata": "Q1622293", "brand:wikipedia": "en:SOCAR", "name": "Socar"}, "removeTags": {"amenity": "fuel", "brand": "Socar", "brand:wikidata": "Q1622293", "brand:wikipedia": "en:SOCAR", "name": "Socar"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Sokimex": {"name": "Sokimex", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1149575"}, "addTags": {"amenity": "fuel", "brand": "Sokimex", "brand:wikidata": "Q1149575", "brand:wikipedia": "en:Sokimex", "name": "Sokimex"}, "removeTags": {"amenity": "fuel", "brand": "Sokimex", "brand:wikidata": "Q1149575", "brand:wikipedia": "en:Sokimex", "name": "Sokimex"}, "matchScore": 2, "suggestion": true}, - "amenity/fuel/Speedway": {"name": "Speedway", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q7575683"}, "addTags": {"amenity": "fuel", "brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway"}, "removeTags": {"amenity": "fuel", "brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Sprint": {"name": "Sprint", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q57588452"}, "addTags": {"amenity": "fuel", "brand": "Sprint", "brand:wikidata": "Q57588452", "name": "Sprint"}, "removeTags": {"amenity": "fuel", "brand": "Sprint", "brand:wikidata": "Q57588452", "name": "Sprint"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/St1": {"name": "St1", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q7592214"}, "addTags": {"amenity": "fuel", "brand": "St1", "brand:wikidata": "Q7592214", "brand:wikipedia": "en:St1", "name": "St1"}, "removeTags": {"amenity": "fuel", "brand": "St1", "brand:wikidata": "Q7592214", "brand:wikipedia": "en:St1", "name": "St1"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Star": {"name": "Star", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q2031095"}, "addTags": {"amenity": "fuel", "brand": "Star", "brand:wikidata": "Q2031095", "brand:wikipedia": "de:Orlen Deutschland", "name": "Star"}, "removeTags": {"amenity": "fuel", "brand": "Star", "brand:wikidata": "Q2031095", "brand:wikipedia": "de:Orlen Deutschland", "name": "Star"}, "matchScore": 2, "suggestion": true}, @@ -2172,7 +2175,7 @@ "shop/clothes/Hot Topic": {"name": "Hot Topic", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q9294032"}, "addTags": {"brand": "Hot Topic", "brand:wikidata": "Q9294032", "brand:wikipedia": "en:Hot Topic", "name": "Hot Topic", "shop": "clothes"}, "removeTags": {"brand": "Hot Topic", "brand:wikidata": "Q9294032", "brand:wikipedia": "en:Hot Topic", "name": "Hot Topic", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Hugo Boss": {"name": "Hugo Boss", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q491627"}, "addTags": {"brand": "Hugo Boss", "brand:wikidata": "Q491627", "brand:wikipedia": "en:Hugo Boss", "name": "Hugo Boss", "shop": "clothes"}, "removeTags": {"brand": "Hugo Boss", "brand:wikidata": "Q491627", "brand:wikipedia": "en:Hugo Boss", "name": "Hugo Boss", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Hunkemöller": {"name": "Hunkemöller", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q2604175"}, "addTags": {"brand": "Hunkemöller", "brand:wikidata": "Q2604175", "brand:wikipedia": "en:Hunkemöller", "name": "Hunkemöller", "shop": "clothes"}, "removeTags": {"brand": "Hunkemöller", "brand:wikidata": "Q2604175", "brand:wikipedia": "en:Hunkemöller", "name": "Hunkemöller", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, - "shop/clothes/Intimissimi": {"name": "Intimissimi", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q305404"}, "addTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "clothes": "underwear", "name": "Intimissimi", "shop": "clothes"}, "removeTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "clothes": "underwear", "name": "Intimissimi", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, + "shop/clothes/Intimissimi": {"name": "Intimissimi", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q305404"}, "addTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "name": "Intimissimi", "shop": "clothes"}, "removeTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "name": "Intimissimi", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Jack & Jones": {"name": "Jack & Jones", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q6077665"}, "addTags": {"brand": "Jack & Jones", "brand:wikidata": "Q6077665", "brand:wikipedia": "en:Jack & Jones", "name": "Jack & Jones", "shop": "clothes"}, "removeTags": {"brand": "Jack & Jones", "brand:wikidata": "Q6077665", "brand:wikipedia": "en:Jack & Jones", "name": "Jack & Jones", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Joules": {"name": "Joules", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q25351738"}, "addTags": {"brand": "Joules", "brand:wikidata": "Q25351738", "brand:wikipedia": "en:Joules (clothing)", "name": "Joules", "shop": "clothes"}, "removeTags": {"brand": "Joules", "brand:wikidata": "Q25351738", "brand:wikipedia": "en:Joules (clothing)", "name": "Joules", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/KiK": {"name": "KiK", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q883965"}, "addTags": {"brand": "KiK", "brand:wikidata": "Q883965", "brand:wikipedia": "en:KiK", "name": "KiK", "shop": "clothes"}, "removeTags": {"brand": "KiK", "brand:wikidata": "Q883965", "brand:wikipedia": "en:KiK", "name": "KiK", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, @@ -2275,7 +2278,6 @@ "shop/convenience/Plaid Pantry": {"name": "Plaid Pantry", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7200591"}, "addTags": {"brand": "Plaid Pantry", "brand:wikidata": "Q7200591", "brand:wikipedia": "en:Plaid Pantry", "name": "Plaid Pantry", "shop": "convenience"}, "removeTags": {"brand": "Plaid Pantry", "brand:wikidata": "Q7200591", "brand:wikipedia": "en:Plaid Pantry", "name": "Plaid Pantry", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Sheetz": {"name": "Sheetz", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7492551"}, "addTags": {"brand": "Sheetz", "brand:wikidata": "Q7492551", "brand:wikipedia": "en:Sheetz", "name": "Sheetz", "shop": "convenience"}, "removeTags": {"brand": "Sheetz", "brand:wikidata": "Q7492551", "brand:wikipedia": "en:Sheetz", "name": "Sheetz", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Shell Select": {"name": "Shell Select", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q154950"}, "addTags": {"brand": "Shell Select", "brand:wikidata": "Q154950", "brand:wikipedia": "en:Royal Dutch Shell", "name": "Shell Select", "shop": "convenience"}, "removeTags": {"brand": "Shell Select", "brand:wikidata": "Q154950", "brand:wikipedia": "en:Royal Dutch Shell", "name": "Shell Select", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, - "shop/convenience/Speedway": {"name": "Speedway", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7575683"}, "addTags": {"brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway", "shop": "convenience"}, "removeTags": {"brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/The Co-operative Food": {"name": "The Co-operative Food", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q3277439"}, "addTags": {"brand": "The Co-operative Food", "brand:wikidata": "Q3277439", "brand:wikipedia": "en:Co-op Food", "name": "The Co-operative Food", "shop": "convenience"}, "removeTags": {"brand": "The Co-operative Food", "brand:wikidata": "Q3277439", "brand:wikipedia": "en:Co-op Food", "name": "The Co-operative Food", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Tiger Mart": {"name": "Tiger Mart", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q57643977"}, "addTags": {"brand": "Tiger Mart", "brand:wikidata": "Q57643977", "name": "Tiger Mart", "shop": "convenience"}, "removeTags": {"brand": "Tiger Mart", "brand:wikidata": "Q57643977", "name": "Tiger Mart", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/United Dairy Farmers": {"name": "United Dairy Farmers", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7887677"}, "addTags": {"brand": "United Dairy Farmers", "brand:wikidata": "Q7887677", "brand:wikipedia": "en:United Dairy Farmers", "name": "United Dairy Farmers", "shop": "convenience"}, "removeTags": {"brand": "United Dairy Farmers", "brand:wikidata": "Q7887677", "brand:wikipedia": "en:United Dairy Farmers", "name": "United Dairy Farmers", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, @@ -2451,7 +2453,6 @@ "shop/houseware/Bed Bath & Beyond": {"name": "Bed Bath & Beyond", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q813782"}, "addTags": {"brand": "Bed Bath & Beyond", "brand:wikidata": "Q813782", "brand:wikipedia": "en:Bed Bath & Beyond", "name": "Bed Bath & Beyond", "shop": "houseware"}, "removeTags": {"brand": "Bed Bath & Beyond", "brand:wikidata": "Q813782", "brand:wikipedia": "en:Bed Bath & Beyond", "name": "Bed Bath & Beyond", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/Blokker": {"name": "Blokker", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q884934"}, "addTags": {"brand": "Blokker", "brand:wikidata": "Q884934", "brand:wikipedia": "en:Blokker Holding", "name": "Blokker", "shop": "houseware"}, "removeTags": {"brand": "Blokker", "brand:wikidata": "Q884934", "brand:wikipedia": "en:Blokker Holding", "name": "Blokker", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/Cervera": {"name": "Cervera", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q10447179"}, "addTags": {"brand": "Cervera", "brand:wikidata": "Q10447179", "brand:wikipedia": "sv:Cervera (företag)", "name": "Cervera", "shop": "houseware"}, "removeTags": {"brand": "Cervera", "brand:wikidata": "Q10447179", "brand:wikipedia": "sv:Cervera (företag)", "name": "Cervera", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, - "shop/houseware/HomeGoods": {"name": "HomeGoods", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q5887941"}, "addTags": {"brand": "HomeGoods", "brand:wikidata": "Q5887941", "brand:wikipedia": "en:HomeGoods", "name": "HomeGoods", "shop": "houseware"}, "removeTags": {"brand": "HomeGoods", "brand:wikidata": "Q5887941", "brand:wikipedia": "en:HomeGoods", "name": "HomeGoods", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/WMF": {"name": "WMF", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q451423"}, "addTags": {"brand": "WMF", "brand:wikidata": "Q451423", "brand:wikipedia": "en:WMF Group", "name": "WMF", "shop": "houseware"}, "removeTags": {"brand": "WMF", "brand:wikidata": "Q451423", "brand:wikipedia": "en:WMF Group", "name": "WMF", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/interior_decoration/Depot": {"name": "Depot", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration", "brand:wikidata": "Q1191740"}, "addTags": {"brand": "Depot", "brand:wikidata": "Q1191740", "brand:wikipedia": "de:Gries Deco Holding", "name": "Depot", "shop": "interior_decoration"}, "removeTags": {"brand": "Depot", "brand:wikidata": "Q1191740", "brand:wikipedia": "de:Gries Deco Holding", "name": "Depot", "shop": "interior_decoration"}, "matchScore": 2, "suggestion": true}, "shop/interior_decoration/Hemtex": {"name": "Hemtex", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration", "brand:wikidata": "Q10521868"}, "addTags": {"brand": "Hemtex", "name": "Hemtex", "brand:wikidata": "Q10521868", "brand:wikipedia": "sv:Hemtex", "shop": "interior_decoration"}, "removeTags": {"brand": "Hemtex", "name": "Hemtex", "brand:wikidata": "Q10521868", "brand:wikipedia": "sv:Hemtex", "shop": "interior_decoration"}, "matchScore": 2, "suggestion": true}, @@ -2496,6 +2497,7 @@ "shop/mobile_phone/Telekom": {"name": "Telekom", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q9396"}, "addTags": {"brand": "Telekom", "brand:wikidata": "Q9396", "brand:wikipedia": "en:Deutsche Telekom", "name": "Telekom", "shop": "mobile_phone"}, "removeTags": {"brand": "Telekom", "brand:wikidata": "Q9396", "brand:wikipedia": "en:Deutsche Telekom", "name": "Telekom", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Telenor": {"name": "Telenor", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q845632"}, "addTags": {"brand": "Telenor", "brand:wikidata": "Q845632", "brand:wikipedia": "en:Telenor", "name": "Telenor", "shop": "mobile_phone"}, "removeTags": {"brand": "Telenor", "brand:wikidata": "Q845632", "brand:wikipedia": "en:Telenor", "name": "Telenor", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Telus": {"name": "Telus", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q165858"}, "addTags": {"brand": "Telus", "brand:wikidata": "Q165858", "brand:wikipedia": "en:Telus", "name": "Telus", "shop": "mobile_phone"}, "removeTags": {"brand": "Telus", "brand:wikidata": "Q165858", "brand:wikipedia": "en:Telus", "name": "Telus", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, + "shop/mobile_phone/The Phone House": {"name": "The Phone House", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q118046"}, "addTags": {"brand": "The Phone House", "brand:wikidata": "Q118046", "brand:wikipedia": "en:Carphone Warehouse", "name": "The Phone House", "shop": "mobile_phone"}, "removeTags": {"brand": "The Phone House", "brand:wikidata": "Q118046", "brand:wikipedia": "en:Carphone Warehouse", "name": "The Phone House", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Turkcell": {"name": "Turkcell", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q283852"}, "addTags": {"brand": "Turkcell", "brand:wikidata": "Q283852", "brand:wikipedia": "en:Turkcell", "name": "Turkcell", "shop": "mobile_phone"}, "removeTags": {"brand": "Turkcell", "brand:wikidata": "Q283852", "brand:wikipedia": "en:Turkcell", "name": "Turkcell", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Télécentre": {"name": "Télécentre", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q180034"}, "addTags": {"brand": "Télécentre", "brand:wikidata": "Q180034", "brand:wikipedia": "en:Telecentre", "name": "Télécentre", "shop": "mobile_phone"}, "removeTags": {"brand": "Télécentre", "brand:wikidata": "Q180034", "brand:wikipedia": "en:Telecentre", "name": "Télécentre", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Verizon Wireless": {"name": "Verizon Wireless", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q919641"}, "addTags": {"brand": "Verizon Wireless", "brand:wikidata": "Q919641", "brand:wikipedia": "en:Verizon Wireless", "name": "Verizon Wireless", "shop": "mobile_phone"}, "removeTags": {"brand": "Verizon Wireless", "brand:wikidata": "Q919641", "brand:wikipedia": "en:Verizon Wireless", "name": "Verizon Wireless", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, @@ -2664,7 +2666,6 @@ "shop/supermarket/City Market": {"name": "City Market", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q5123299"}, "addTags": {"brand": "City Market", "brand:wikidata": "Q5123299", "brand:wikipedia": "en:City Market (US grocery store chain)", "name": "City Market", "shop": "supermarket"}, "removeTags": {"brand": "City Market", "brand:wikidata": "Q5123299", "brand:wikipedia": "en:City Market (US grocery store chain)", "name": "City Market", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Co-op": {"name": "Co-op", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q5440676"}, "addTags": {"brand": "Federated Co-operatives", "brand:wikidata": "Q5440676", "brand:wikipedia": "en:Federated Co-operatives", "name": "Co-op", "shop": "supermarket"}, "removeTags": {"brand": "Federated Co-operatives", "brand:wikidata": "Q5440676", "brand:wikipedia": "en:Federated Co-operatives", "name": "Co-op", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Colruyt": {"name": "Colruyt", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q2363991"}, "addTags": {"brand": "Colruyt", "brand:wikidata": "Q2363991", "brand:wikipedia": "en:Colruyt (supermarket)", "name": "Colruyt", "shop": "supermarket"}, "removeTags": {"brand": "Colruyt", "brand:wikidata": "Q2363991", "brand:wikipedia": "en:Colruyt (supermarket)", "name": "Colruyt", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, - "shop/supermarket/Combi": {"name": "Combi", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q1113618"}, "addTags": {"brand": "Combi", "brand:wikidata": "Q1113618", "brand:wikipedia": "de:Combi (Einkaufsmarkt)", "name": "Combi", "shop": "supermarket"}, "removeTags": {"brand": "Combi", "brand:wikidata": "Q1113618", "brand:wikipedia": "de:Combi (Einkaufsmarkt)", "name": "Combi", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Comercial Mexicana": {"name": "Comercial Mexicana", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q2985173"}, "addTags": {"brand": "Comercial Mexicana", "brand:wikidata": "Q2985173", "brand:wikipedia": "en:Comercial Mexicana", "name": "Comercial Mexicana", "shop": "supermarket"}, "removeTags": {"brand": "Comercial Mexicana", "brand:wikidata": "Q2985173", "brand:wikipedia": "en:Comercial Mexicana", "name": "Comercial Mexicana", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Conad": {"name": "Conad", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q639075"}, "addTags": {"brand": "Conad", "brand:wikidata": "Q639075", "brand:wikipedia": "it:Conad", "name": "Conad", "shop": "supermarket"}, "removeTags": {"brand": "Conad", "brand:wikidata": "Q639075", "brand:wikipedia": "it:Conad", "name": "Conad", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Conad City": {"name": "Conad City", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q57543102"}, "addTags": {"brand": "Conad City", "brand:wikidata": "Q57543102", "name": "Conad City", "shop": "supermarket"}, "removeTags": {"brand": "Conad City", "brand:wikidata": "Q57543102", "name": "Conad City", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, @@ -2827,7 +2828,6 @@ "shop/supermarket/Sobeys": {"name": "Sobeys", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q1143340"}, "addTags": {"brand": "Sobeys", "brand:wikidata": "Q1143340", "brand:wikipedia": "en:Sobeys", "name": "Sobeys", "shop": "supermarket"}, "removeTags": {"brand": "Sobeys", "brand:wikidata": "Q1143340", "brand:wikipedia": "en:Sobeys", "name": "Sobeys", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Soriana": {"name": "Soriana", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q735562"}, "addTags": {"brand": "Soriana", "brand:wikidata": "Q735562", "brand:wikipedia": "en:Soriana", "name": "Soriana", "shop": "supermarket"}, "removeTags": {"brand": "Soriana", "brand:wikidata": "Q735562", "brand:wikipedia": "en:Soriana", "name": "Soriana", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Sprouts Farmers Market": {"name": "Sprouts Farmers Market", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q7581369"}, "addTags": {"brand": "Sprouts Farmers Market", "brand:wikidata": "Q7581369", "brand:wikipedia": "en:Sprouts Farmers Market", "name": "Sprouts Farmers Market", "shop": "supermarket"}, "removeTags": {"brand": "Sprouts Farmers Market", "brand:wikidata": "Q7581369", "brand:wikipedia": "en:Sprouts Farmers Market", "name": "Sprouts Farmers Market", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, - "shop/supermarket/Stater Bros.": {"name": "Stater Bros.", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q7604016"}, "addTags": {"brand": "Stater Bros.", "brand:wikidata": "Q7604016", "brand:wikipedia": "en:Stater Bros.", "name": "Stater Bros.", "shop": "supermarket"}, "removeTags": {"brand": "Stater Bros.", "brand:wikidata": "Q7604016", "brand:wikipedia": "en:Stater Bros.", "name": "Stater Bros.", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Stokrotka": {"name": "Stokrotka", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q9345945"}, "addTags": {"brand": "Stokrotka", "brand:wikidata": "Q9345945", "brand:wikipedia": "en:Stokrotka", "name": "Stokrotka", "shop": "supermarket"}, "removeTags": {"brand": "Stokrotka", "brand:wikidata": "Q9345945", "brand:wikipedia": "en:Stokrotka", "name": "Stokrotka", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Stop & Shop": {"name": "Stop & Shop", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q3658429"}, "addTags": {"brand": "Stop & Shop", "brand:wikidata": "Q3658429", "brand:wikipedia": "en:Stop & Shop", "name": "Stop & Shop", "shop": "supermarket"}, "removeTags": {"brand": "Stop & Shop", "brand:wikidata": "Q3658429", "brand:wikipedia": "en:Stop & Shop", "name": "Stop & Shop", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Suma": {"name": "Suma", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q58012362"}, "addTags": {"brand": "Suma", "brand:wikidata": "Q58012362", "name": "Suma", "shop": "supermarket"}, "removeTags": {"brand": "Suma", "brand:wikidata": "Q58012362", "name": "Suma", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, diff --git a/data/presets/presets/shop/fireplace.json b/data/presets/presets/shop/fireplace.json new file mode 100644 index 000000000..e393c9307 --- /dev/null +++ b/data/presets/presets/shop/fireplace.json @@ -0,0 +1,24 @@ +{ + "icon": "maki-shop", + "fields": [ + "name", + "operator", + "address", + "building_area", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "terms": [ + "fireplace", + "stove", + "masonry heater" + ], + "tags": { + "shop": "fireplace" + }, + "name": "Fireplace Store" +} diff --git a/data/presets/presets/shop/fishing.json b/data/presets/presets/shop/fishing.json new file mode 100644 index 000000000..ccbba74ce --- /dev/null +++ b/data/presets/presets/shop/fishing.json @@ -0,0 +1,19 @@ +{ + "icon": "maki-shop", + "fields": [ + "name", + "operator", + "address", + "building_area", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "shop": "fishing" + }, + "name": "Fishing Shop" +} diff --git a/data/presets/presets/shop/hunting.json b/data/presets/presets/shop/hunting.json new file mode 100644 index 000000000..e92b28612 --- /dev/null +++ b/data/presets/presets/shop/hunting.json @@ -0,0 +1,19 @@ +{ + "icon": "maki-shop", + "fields": [ + "name", + "operator", + "address", + "building_area", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "shop": "hunting" + }, + "name": "Hunting Shop" +} diff --git a/data/presets/presets/shop/water.json b/data/presets/presets/shop/water.json new file mode 100644 index 000000000..3e66135c9 --- /dev/null +++ b/data/presets/presets/shop/water.json @@ -0,0 +1,19 @@ +{ + "icon": "maki-shop", + "fields": [ + "name", + "operator", + "address", + "building_area", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "shop": "water" + }, + "name": "Drinking Water Shop" +} diff --git a/data/taginfo.json b/data/taginfo.json index f94b703b2..4ff721d38 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -5707,6 +5707,20 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" }, + { + "key": "shop", + "value": "fireplace", + "description": "🄿 Fireplace Store", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" + }, + { + "key": "shop", + "value": "fishing", + "description": "🄿 Fishing Shop", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" + }, { "key": "shop", "value": "florist", @@ -5819,6 +5833,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-blender.svg?sanitize=true" }, + { + "key": "shop", + "value": "hunting", + "description": "🄿 Hunting Shop", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" + }, { "key": "shop", "value": "interior_decoration", @@ -6225,6 +6246,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" }, + { + "key": "shop", + "value": "water", + "description": "🄿 Drinking Water Shop", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" + }, { "key": "shop", "value": "weapons", diff --git a/dist/locales/en.json b/dist/locales/en.json index a6fa4d734..3af9e01d3 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6705,6 +6705,14 @@ "name": "Fashion Store", "terms": "" }, + "shop/fireplace": { + "name": "Fireplace Store", + "terms": "fireplace,stove,masonry heater" + }, + "shop/fishing": { + "name": "Fishing Shop", + "terms": "" + }, "shop/florist": { "name": "Florist", "terms": "flower" @@ -6769,6 +6777,10 @@ "name": "Houseware Store", "terms": "home,household" }, + "shop/hunting": { + "name": "Hunting Shop", + "terms": "" + }, "shop/interior_decoration": { "name": "Interior Decoration Store", "terms": "" @@ -7001,6 +7013,10 @@ "name": "Watersport/Swim Shop", "terms": "" }, + "shop/water": { + "name": "Drinking Water Shop", + "terms": "" + }, "shop/weapons": { "name": "Weapon Shop", "terms": "ammo,gun,knife,knives" @@ -7877,48 +7893,24 @@ } } }, - "osm-india-forum": { - "name": "OpenStreetMap India forum", - "description": "OpenStreetMap India web forum" - }, - "osm-india-github": { - "name": "OpenStreetMap India GitHub", - "description": "Code with us: {url}" - }, "OSM-india-mailinglist": { "name": "OpenStreetMap India Mailinglist", "description": "Talk-in is the official Mailinglist for Indian Community" }, - "osm-india-telegram": { - "name": "OpenStreetMap India Telegram", - "description": "Join our family: {url}" + "OSM-India-Puducherry-Mailing-List": { + "name": "Free Software Hardware Movement - Mailing List", + "description": "FSHM Puducherry mailing list to discuss mapping in Puducherry and other things.", + "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap." }, "OSM-india-twitter": { "name": "OpenStreetMap India Twitter", "description": "We are just a tweet away: {url}" }, - "osm-india-website": { - "name": "OpenStreetMap India", - "description": "Mappers and OpenStreetMap users in India" - }, - "osm-india-wiki": { - "name": "OpenStreetMap Wikiproject India", - "description": "Everything you need to know about mapping in India: {url}" - }, - "osm-india-youtube": { - "name": "OpenStreetMap India Youtube", - "description": "Subscribe to our channel: {url}" - }, "OSM-India-Puducherry-Facebook": { "name": "Free Software Hardware Movement - Facebook", "description": "FSHM Facebook page to know about community events, activities", "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap. Its FB page is the best way to keep in contact with its events." }, - "OSM-Puducherry-Mailing-List": { - "name": "Free Software Hardware Movement - Mailing List", - "description": "FSHM Puducherry mailing list to discuss mapping in Puducherry and other things.", - "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap." - }, "OSM-India-Puducherry-Matrix": { "name": "Free Software Hardware Movement - Matrix", "description": "FSHM Riot group to discuss, share and update mapping activities, events in and around Puducherry", @@ -8084,6 +8076,11 @@ "name": "Talk-be Mailing List", "description": "Talk-be is the official mailing list for the Belgian OSM community" }, + "be-maptime": { + "name": "Maptime Belgium", + "description": "Social events organized around mapping - beginners most welcome!", + "extendedDescription": "Maptime is an open learning environment for all levels and degrees of knowledge, offering intentional educational support for the beginner. Maptime is simultaneously flexible and structured, creating space for mapping tutorials, workshops, ongoing projects with a shared goal, and independent/collaborative work time." + }, "be-matrix": { "name": "OpenStreetMap BE Matrix channel", "description": "All mappers are welcome!", From b3cffd78333e38aba150057678b50280df84104d Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Wed, 25 Jul 2018 11:21:15 -0400 Subject: [PATCH 002/114] added simple keepRight button under photoItems --- data/core.yaml | 3 + dist/locales/en.json | 6 +- modules/svg/keepRight.js | 175 +++++++++++++++++++++++++++++++++++++++ modules/svg/layers.js | 6 ++ modules/ui/map_data.js | 2 +- 5 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 modules/svg/keepRight.js diff --git a/data/core.yaml b/data/core.yaml index c74f1a977..ffa4f71b9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -643,6 +643,9 @@ en: out: Zoom out cannot_zoom: "Cannot zoom out further in current mode." full_screen: Toggle Full Screen + keepRight: + tooltip: Q/A data from keepright.at + title: Keep Right streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index da3671d75..c75162e1d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -782,6 +782,10 @@ }, "cannot_zoom": "Cannot zoom out further in current mode.", "full_screen": "Toggle Full Screen", + "keepRight": { + "tooltip": "Q/A data from keepright.at", + "title": "Keep Right" + }, "streetside": { "tooltip": "Streetside photos from Microsoft", "title": "Photo Overlay (Bing Streetside)", @@ -8730,4 +8734,4 @@ } } } -} \ No newline at end of file +} diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js new file mode 100644 index 000000000..aa01546b0 --- /dev/null +++ b/modules/svg/keepRight.js @@ -0,0 +1,175 @@ +import _some from 'lodash-es/some'; +import _throttle from 'lodash-es/throttle'; +import { select as d3_select } from 'd3-selection'; +import { svgPointTransform } from './index'; +import { services } from '../services'; + + +export function svgKeepRight(projection, context, dispatch) { + var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); + var minZoom = 12; + var layer = d3_select(null); + var _keepRight; + + + function init() { + if (svgKeepRight.initialized) return; // run once + svgKeepRight.enabled = false; + svgKeepRight.initialized = true; + } + + + function getService() { + if (services.mapillary && !_keepRight) { + _keepRight = services.mapillary; + _keepRight.event.on('loadedSigns', throttledRedraw); + } else if (!services.mapillary && _keepRight) { + _keepRight = null; + } + return _keepRight; + } + + + function showLayer() { + var service = getService(); + if (!service) return; + + service.loadViewer(context); + editOn(); + } + + + function hideLayer() { + throttledRedraw.cancel(); + editOff(); + } + + + function editOn() { + layer.style('display', 'block'); + } + + + function editOff() { + layer.selectAll('.icon-sign').remove(); + layer.style('display', 'none'); + } + + + function click(d) { + var service = getService(); + if (!service) return; + + context.map().centerEase(d.loc); + + var selected = service.getSelectedImage(); + var selectedImageKey = selected && selected.key; + var imageKey; + + // Pick one of the images the sign was detected in, + // preference given to an image already selected. + d.detections.forEach(function(detection) { + if (!imageKey || selectedImageKey === detection.image_key) { + imageKey = detection.image_key; + } + }); + + service + .selectImage(null, imageKey) + .updateViewer(imageKey, context) + .showViewer(); + } + + + function update() { + var service = getService(); + var data = (service ? service.signs(projection) : []); + var viewer = d3_select('#photoviewer'); + var selected = viewer.empty() ? undefined : viewer.datum(); + var selectedImageKey = selected && selected.key; + var transform = svgPointTransform(projection); + + var signs = layer.selectAll('.icon-sign') + .data(data, function(d) { return d.key; }); + + // exit + signs.exit() + .remove(); + + // enter + var enter = signs.enter() + .append('use') + .attr('class', 'icon-sign') + .attr('width', '24px') + .attr('height', '24px') + .attr('x', '-12px') + .attr('y', '-12px') + .attr('xlink:href', function(d) { return '#' + d.value; }) + .classed('selected', function(d) { + return _some(d.detections, function(detection) { + return detection.image_key === selectedImageKey; + }); + }) + .on('click', click); + + // update + signs + .merge(enter) + .sort(function(a, b) { + return (a === selected) ? 1 + : (b === selected) ? -1 + : b.loc[1] - a.loc[1]; // sort Y + }) + .attr('transform', transform); + } + + + function drawKeepRight(selection) { + var enabled = svgKeepRight.enabled; + var service = getService(); + + layer = selection.selectAll('.layer-keepRight') + .data(service ? [0] : []); + + layer.exit() + .remove(); + + layer = layer.enter() + .append('g') + .attr('class', 'layer-keepRight') + .style('display', enabled ? 'block' : 'none') + .merge(layer); + + if (enabled) { + if (service && ~~context.map().zoom() >= minZoom) { + editOn(); + update(); + service.loadSigns(context, projection); + } else { + editOff(); + } + } + } + + + drawKeepRight.enabled = function(_) { + if (!arguments.length) return svgKeepRight.enabled; + svgKeepRight.enabled = _; + if (svgKeepRight.enabled) { + showLayer(); + } else { + hideLayer(); + } + dispatch.call('change'); + return this; + }; + + + drawKeepRight.supported = function() { + return !!getService(); + }; + + + init(); + return drawKeepRight; +} diff --git a/modules/svg/layers.js b/modules/svg/layers.js index 4d0b9f7d2..b8810733f 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -9,7 +9,12 @@ import { select as d3_select } from 'd3-selection'; import { svgData } from './data'; import { svgDebug } from './debug'; +<<<<<<< HEAD import { svgGeolocate } from './geolocate'; +======= +import { svgGpx } from './gpx'; +import { svgKeepRight } from './keepRight'; +>>>>>>> added simple keepRight button under photoItems import { svgStreetside } from './streetside'; import { svgMapillaryImages } from './mapillary_images'; import { svgMapillarySigns } from './mapillary_signs'; @@ -28,6 +33,7 @@ export function svgLayers(projection, context) { { id: 'osm', layer: svgOsm(projection, context, dispatch) }, { id: 'notes', layer: svgNotes(projection, context, dispatch) }, { id: 'data', layer: svgData(projection, context, dispatch) }, + { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch) }, { id: 'streetside', layer: svgStreetside(projection, context, dispatch)}, { id: 'mapillary-images', layer: svgMapillaryImages(projection, context, dispatch) }, { id: 'mapillary-signs', layer: svgMapillarySigns(projection, context, dispatch) }, diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index e12737025..b6fbe8a4b 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -95,7 +95,7 @@ export function uiMapData(context) { function drawPhotoItems(selection) { - var photoKeys = ['streetside', 'mapillary-images', 'mapillary-signs', 'openstreetcam-images']; + var photoKeys = ['streetside', 'mapillary-images', 'mapillary-signs', 'openstreetcam-images', 'keepRight']; var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; }); var data = photoLayers.filter(function(obj) { return obj.layer.supported(); }); From b96965568f3e426f3f29dcefb982b0ff7c0faf04 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 27 Jul 2018 22:48:14 -0400 Subject: [PATCH 003/114] WIP: added schemas for errors and warnings --- modules/osm/keepRight.js | 471 ++++++++++++++++++++++++++++++++++ modules/services/index.js | 3 + modules/services/keepRight.js | 137 ++++++++++ modules/svg/index.js | 1 + modules/svg/keepRight.js | 17 +- 5 files changed, 622 insertions(+), 7 deletions(-) create mode 100644 modules/osm/keepRight.js create mode 100644 modules/services/keepRight.js diff --git a/modules/osm/keepRight.js b/modules/osm/keepRight.js new file mode 100644 index 000000000..95d8d14bb --- /dev/null +++ b/modules/osm/keepRight.js @@ -0,0 +1,471 @@ +var keepRightSchema = { + 'schema': '', + 'error_id': 0, + 'error_type': 0, + 'error_name': 0, + 'object_type': ['node', +'way', +'relation'], + 'object_id': 0, + 'state': ['new', +'reopened', +'ignore_temporarily', +'ignore'], + 'first_occurrence': new Date(), + 'last_checked': new Date(), + 'object_timestamp': new Date(), + 'user_name': '', + 'lat': 0, + 'lon': 0, + 'comment': '', + 'comment_timestamp': new Date(), + 'msgid': '', + 'txt1': '', + 'txt2': '', + 'txt3': '', + 'txt4': '', + 'txt5': '' + }; + + var errorSchema = { + errors: { + 0: { + errorType: 0, + errorName: '', + message: '', + subTypes: {} + }, + 30: { + errorType: 30, + errorName: 'non_closed_areas', + message: 'This way is tagged with \'$1=$2\' and should be closed-loop', + subTypes: {} + }, + 40: { + errorType: 40, + errorName: 'dead ended oneways', + message: 'The first node (id $1) of this one-way is not connected to any other way', + subTypes: { + 41: { + errorType: 41, + errorName: '', + message: 'The last node (id $1) of this one-way is not connected to any other way' + }, + 42: { + errorType: 42, + errorName: '', + message: 'This node cannot be reached, because one-ways only lead away from here' + }, + 43: { + errorType: 43, + errorName: '', + message: 'You cannot escape from this node, because one-ways only lead to here' + }, + } + }, + 50: { + errorType: 50, + errorName: 'almost junctions', + message: 'This node is very close but not connected to way #$1', + subTypes: {} + }, + 60: { + errorType: 60, + errorName: 'depreciated tags', + message: 'This $1 uses deprecated tag $2 = $3. Please use $4 instead!', + subTypes: {} + }, + 70: { + errorType: 70, + errorName: 'missing tags', + message: 'This $1 has an empty tag: $2', + 71: { + errorType: 71, + errorName: '', + message: 'This way has no tags' + }, + 72: { + errorType: 72, + errorName: '', + message: 'This node is not member of any way and does not have any tags' + } + }, + 90: { + errorType: 90, + errorName: 'motorways without ref', + message: 'This way is tagged as motorway and therefore needs a ref, nat_ref or int_ref tag' + }, + 100: { + errorType: 100, + errorName: 'places of worship without religion', + message: 'This $1 is tagged as place of worship and therefore needs a religion tag' + }, + 110: { + errorType: 110, + errorName: 'point of interest without name', + message: 'This node is tagged as $1 and therefore needs a name tag' + }, + 120: { + errorType: 120, + errorName: 'ways without nodes', + message: 'This way has just one single node' + }, + 130: { + errorType: 130, + errorName: 'floating islands', + message: 'This way is not connected to the rest of the map' + }, + 150: { + errorType: 150, + errorName: 'railway crossing without tag', + message: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + }, + 160: { + errorType: 160, + errorName: 'wrongly used railway tag', + message: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + }, + 170: { + errorType: 0, + errorName: 'FIXME tagged items', + message: '$1' + }, + 180: { + errorType: 180, + errorName: 'relations without type', + message: 'This relation has no type tag, which is mandatory for relations' + }, + 190: { + errorType: 190, + errorName: 'intersections without junctions', + message: 'Finds way crossings on same layer without common node as a junction', + subtypes: { + 191: { + errorType: 191, + errorName: 'highway-highway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 192: { + errorType: 192, + errorName: 'highway-waterway', + message: 'This $1 intersects the $2 #$3' + }, + 193: { + errorType: 193, + errorName: 'highway-riverbank', + message: 'This $1 intersects the $2 #$3' + }, + 194: { + errorType: 194, + errorName: 'waterway-waterway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 195: { + errorType: 195, + errorName: 'cycleway-cycleway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 196: { + errorType: 196, + errorName: 'highway-cycleway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 197: { + errorType: 197, + errorName: 'cycleway-waterway', + message: 'This $1 intersects the $2 #$3' + }, + 198: { + errorType: 198, + errorName: 'cycleway-riverbank', + message: 'This $1 intersects the $2 #$3' + } + } + }, + 200: { + errorType: 200, + errorName: 'intersections without junctions', + message: 'Finds overlapping ways on same layer.', + subtypes: { + 201: { + errorType: 201, + errorName: 'highway-highway', + message: 'This $1 overlaps the $2 #$3' + }, + 202: { + errorType: 202, + errorName: 'highway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 203: { + errorType: 203, + errorName: 'highway-riverbank', + message: 'This $1 overlaps the $2 #$3' + }, + 204: { + errorType: 204, + errorName: 'waterway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 205: { + errorType: 205, + errorName: 'cycleway-cycleway', + message: 'This $1 overlaps the $2 #$3' + }, + 206: { + errorType: 206, + errorName: 'highway-cycleway', + message: 'This $1 overlaps the $2 #$3' + }, + 207: { + errorType: 207, + errorName: 'cycleway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 208: { + errorType: 208, + errorName: 'cycleway-riverbank', + message: 'This $1 overlaps the $2 #$3' + } + } + }, + 210: { + errorType: 210, + errorName: 'loopings', + message: 'These errors contain self intersecting ways', + subTypes: { + 211: { + errorType: 211, + errorName: '', + message: 'This way contains more than one node at least twice. Nodes are $1. This may or may not be an error' + }, + 212: { + errorType: 212, + errorName: '', + message: 'This way has only two different nodes and contains one of them more than once' + }, + } + }, + 220: { + errorType: 220, + errorName: 'misspelled tags', + message: ' This $1 is tagged \'$2=$3\' where $4 looks like $5', + subTypes: { + 221: { + errorType: 221, + errorName: 'misspelled tags', + message: 'The key of this $1\'s tag is \'key\': $2' + } + } + }, + 230: { + errorType: 230, + errorName: 'layer conflicts', + message: '', + subTypes: { + 231: { + errorType: 231, + errorName: 'mixed layers intersection', + message: 'This node is a junction of ways on different layers: $1' + }, + 232: { + errorType: 232, + errorName: 'strange layers', + message: 'This $1 is tagged with layer $2. This need not be an error, but it looks strange' + } + } + }, + 270: { + errorType: 270, + errorName: 'motorways connected directly', + message: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or if it is a service=parking_aisle.' + }, + 280: { + errorType: 280, + errorName: 'boundaries', + message: '', + subTypes: { + 281: { + errorType: 281, + errorName: 'missing name', + message: 'This boundary has no name' + }, + 282: { + errorType: 282, + errorName: 'missing admin level', + message: 'The boundary of $1 has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' + }, + 283: { + errorType: 283, + errorName: 'no closed loop', + message: 'The boundary of $1 is not closed-loop' + }, + 284: { + errorType: 284, + errorName: 'splitting boundary', + message: 'The boundary of $1 splits here' + }, + 285: { + errorType: 285, + errorName: 'admin_level too high', + message: 'This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + }, + } + }, + 290: { + errorType: 290, + errorName: 'faulty restrictions', + message: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv, type=restriction:caravan, type=restriction:motorcar, type=restriction:bus, type=restriction:agricultural, type=restriction:motorcycle, type=restriction:bicycle and type=restriction:hazmat.', + subTypes: { + 291: { + errorType: 291, + errorName: 'missing type', + message: 'This turn-restriction has no known restriction type' + }, + 292: { + errorType: 292, + errorName: 'missing from way', + message: 'A turn-restriction needs exactly one $1 member. This one has $2' + }, + 293: { + errorType: 293, + errorName: 'missing to way', + message: 'A turn-restriction needs exactly one $1 member. This one has $2' + }, + 294: { + errorType: 294, + errorName: 'from or to not a way', + message: 'From- and To-members of turn restrictions need to be ways. $1' + }, + 295: { + errorType: 295, + errorName: 'via is not on the way ends', + message: 'via (node #$1) is not the first or the last member of from (way #$2)' + }, + 296: { + errorType: 296, + errorName: 'wrong restriction angle', + message: 'restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?' + }, + 297: { + errorType: 297, + errorName: 'wrong direction of to member', + message: 'wrong direction of to way $1' + }, + 298: { + errorType: 298, + errorName: 'already restricted by oneway', + message: 'entry already prohibited by oneway tag on $1' + }, + } + }, + 310: { + errorType: 310, + errorName: 'roundabouts', + message: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.', + subTypes: { + 311: { + errorType: 311, + errorName: 'not closed loop', + message: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + }, + 312: { + errorType: 312, + errorName: 'wrong direction', + message: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + }, + 313: { + errorType: 313, + errorName: 'faintly connected', + message: 'This roundabout has only $1 other roads connected. Roundabouts typically have three.' + }, + } + }, + 320: { + errorType: 320, + errorName: '*link connections', + message: 'This way is tagged as highway=$1_link but doesn\'t have a connection to any other $1 or $1_link' + }, + 350: { + errorType: 350, + errorName: 'bridge tags', + message: 'This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: $1' + }, + 370: { + errorType: 370, + errorName: 'doubled places', + message: 'This node has tags in common with the surrounding way #$1 and seems to be redundand | This node has tags in common with the surrounding way #$1 (including the name \'$2\') and seems to be redundand' + }, + 380: { + errorType: 380, + errorName: 'non-physical use of sportage', + message: 'This way is tagged $1 but has no physical tag like e.g. leisure, building, amenity or highway' + }, + 400: { + errorType: 400, + errorName: 'geometry glitches', + message: '', + subTypes: { + 401: { + errorType: 401, + errorName: 'missing turn restrictions', + message: 'ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning' + }, + 402: { + errorType: 402, + errorName: 'impossible angles', + message: 'this way bends in a very sharp angle here' + }, + } + }, + 410: { + errorType: 410, + errorName: 'websites', + message: 'Web pages are analyzed. Web page is defined by any of the following tags website=*, url=*, website:mobile=*, contact:website=*, contact:url=*, image=*, source:website=* or source:url=*.', + subTypes: { + 411: { + errorType: 411, + errorName: 'http error', + message: 'The URL ($1) cannot be opened (HTTP status code $2)' + }, + 412: { + errorType: 412, + errorName: 'domain hijacking', + message: 'Possible domain squatting: $1. Suspicious text is: "$2"' + }, + 413: { + errorType: 413, + errorName: 'non-match', + message: 'Content of the URL ($1) did not contain these keywords: ($2)' + }, + } + } + }, + warnings: { + 20: { + errorType: 20, + errorName: 'multiple nodes on the same spot', + message: ' There is more than one node in this spot. Offending node IDs: $1' + }, + 60: { + errorType: 60, + errorName: '', + message: '' + }, + 300: { + errorType: 300, + errorName: 'missing maxspeed', + message: 'missing maxspeed tag' + }, + 360: { + errorType: 360, + errorName: 'language unknown', + message: 'It would be nice if this $1 had an additional tag \'name:XX=$2\' where XX shows the language of its name \'$2\'.' + }, + 390: { + errorType: 390, + errorName: 'missing tracktype', + message: 'This track doesn\'t have a tracktype' + }, + }, + }; \ No newline at end of file diff --git a/modules/services/index.js b/modules/services/index.js index 59c9d9524..d2d2cf96b 100644 --- a/modules/services/index.js +++ b/modules/services/index.js @@ -1,3 +1,4 @@ +import serviceKeepRight from './keepRight'; import serviceMapillary from './mapillary'; import serviceMapRules from './maprules'; import serviceNominatim from './nominatim'; @@ -12,6 +13,7 @@ import serviceWikipedia from './wikipedia'; export var services = { geocoder: serviceNominatim, + keepRight: serviceKeepRight, mapillary: serviceMapillary, openstreetcam: serviceOpenstreetcam, osm: serviceOsm, @@ -24,6 +26,7 @@ export var services = { }; export { + serviceKeepRight, serviceMapillary, serviceMapRules, serviceNominatim, diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js new file mode 100644 index 000000000..01d80344f --- /dev/null +++ b/modules/services/keepRight.js @@ -0,0 +1,137 @@ +import _extend from 'lodash-es/extend'; +import _find from 'lodash-es/find'; +import _forEach from 'lodash-es/forEach'; +import _isEmpty from 'lodash-es/isEmpty'; + +import rbush from 'rbush'; + +import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { request as d3_request } from 'd3-request'; + +import { + utilRebind, + utilTiler, + utilQsString +} from '../util'; + + +var tiler = utilTiler(); +var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedKeepRight'); + +var _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; +var _off; +var _keepRightZoom = 16; + +var apiBase = 'https://www.keepright.at/export.php?'; + +// TODO: remove this + var schema = { + 'error_type': '', + 'object_type': '', + 'object_id': '', + 'comment': '', + 'error_id':'', + 'schema': '', + 'description': '', + 'title': '' + }; + + +function abortRequest(i) { + if (i) { + i.abort(); + } +} + + +function abortUnwantedRequests(cache, tiles) { + _forEach(cache.inflight, function(v, k) { + var wanted = _find(tiles, function(tile) { return k === tile.id; }); + if (!wanted) { + abortRequest(v); + delete cache.inflight[k]; + } + }); +} + + +export default { + init: function() { + if (!_keepRightCache) { + this.reset(); + } + + this.event = utilRebind(this, dispatch, 'on'); + }, + + reset: function() { + _forEach(_keepRightCache.inflight, abortRequest); + + _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; + }, + + loadKeepRight: function(context, projection, keepRightOptions) { + keepRightOptions = _extend({ 'format': 'geojson' }); + if (_off) return; + + var that = this; + var path = apiBase + + 'format=' + keepRightOptions.format + + '&ch=' + keepRightOptions.ch.join() + '&'; + + // determine the needed tiles to cover the view + var tiles = tiler.zoomExtent([_keepRightZoom, _keepRightZoom]).getTiles(projection); + + // abort inflight requests that are no longer needed + var hadRequests = !_isEmpty(_keepRightCache.inflight); + abortUnwantedRequests(_keepRightCache, tiles); + if (hadRequests && _isEmpty(_keepRightCache.inflight)) { + dispatch.call('loaded'); // stop the spinner + } + + // issue new requests.. + tiles.forEach(function(tile) { + if (_keepRightCache.loaded[tile.id] || _keepRightCache.inflight[tile.id]) return; + if (_isEmpty(_keepRightCache.inflight)) { + dispatch.call('loading'); // start the spinner + } + + var cache = _keepRightCache; + var rect = tile.extent.rectangle(); + var nextPath = path + + utilQsString({ + left: rect[0], + bottom: [3], + right: rect[2], + top: rect[1] + }); + + + function callbackExample() { + // TODO: implement + } + + var exampleOptions = {}; // TODO: implement + + _keepRightCache.inflight[tile.id] = that.loadFromAPI( + nextPath, + callbackExample, + exampleOptions + ); + }); + }, + + loadFromAPI: function(path, callback, options) { + var result = d3_request(path) // TODO: rturn or somethign, dont save to var + .mimeType('application/json') // TODO: only have this as a response if the input format is json + .header('Content-type', 'application/x-www-form-urlencoded') + .response(function(xhr) { + console.log('xhr: ', xhr); + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + console.log(data); + }); + console.log('result: ', result); + } +}; \ No newline at end of file diff --git a/modules/svg/index.js b/modules/svg/index.js index b444bd7a0..82d6bfe9a 100644 --- a/modules/svg/index.js +++ b/modules/svg/index.js @@ -2,6 +2,7 @@ export { svgAreas } from './areas.js'; export { svgData } from './data.js'; export { svgDebug } from './debug.js'; export { svgDefs } from './defs.js'; +export { svgKeepRight } from './keepRight'; export { svgIcon } from './icon.js'; export { svgGeolocate } from './geolocate'; export { svgLabels } from './labels.js'; diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index aa01546b0..6352a8cc0 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -20,10 +20,10 @@ export function svgKeepRight(projection, context, dispatch) { function getService() { - if (services.mapillary && !_keepRight) { - _keepRight = services.mapillary; - _keepRight.event.on('loadedSigns', throttledRedraw); - } else if (!services.mapillary && _keepRight) { + if (services.keepRight && !_keepRight) { + _keepRight = services.keepRight; + _keepRight.event.on('loadedKeepRight', throttledRedraw); + } else if (!services.keepRight && _keepRight) { _keepRight = null; } return _keepRight; @@ -33,8 +33,6 @@ export function svgKeepRight(projection, context, dispatch) { function showLayer() { var service = getService(); if (!service) return; - - service.loadViewer(context); editOn(); } @@ -82,6 +80,8 @@ export function svgKeepRight(projection, context, dispatch) { function update() { + console.log('TAH - keepRight.update()'); + return; var service = getService(); var data = (service ? service.signs(projection) : []); var viewer = d3_select('#photoviewer'); @@ -144,7 +144,10 @@ export function svgKeepRight(projection, context, dispatch) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); update(); - service.loadSigns(context, projection); + var options = { + ch: ['30', ] + }; + service.loadKeepRight(context, projection, options); } else { editOff(); } From 75cff00a2a4fa599b8fcb45bd0661cc8cf93d2fa Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Sat, 28 Jul 2018 13:55:33 -0400 Subject: [PATCH 004/114] displaying keep right (currently as notes) --- css/65_data.css | 64 +- modules/osm/keepRight.js | 892 +++++++++++++------------ modules/services/keepRight.js | 108 ++- modules/svg/keepRight.js | 71 +- modules/svg/layers.js | 4 - svg/iD-sprite/icons/icon-keepRight.svg | 10 + test/spec/svg/layers.js | 13 + 7 files changed, 645 insertions(+), 517 deletions(-) create mode 100644 svg/iD-sprite/icons/icon-keepRight.svg diff --git a/css/65_data.css b/css/65_data.css index 24af906c4..d15ece1bd 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -1,26 +1,34 @@ /* OSM Notes Layer */ +.layer-keepRight, .layer-notes { pointer-events: none; } +.layer-keepRight .kr_error, .layer-notes .note * { pointer-events: none; } .mode-browse .layer-notes .note .note-fill, .mode-select .layer-notes .note .note-fill, .mode-select-data .layer-notes .note .note-fill, -.mode-select-note .layer-notes .note .note-fill { +.mode-select-note .layer-notes .note .note-fill, +.layer-keepRight .kr_error .kr_error-fill, +.layer-notes .note .note-fill { pointer-events: visible; cursor: pointer; /* Opera */ cursor: url(img/cursor-select-point.png), pointer; /* FF */ } .note-header-icon .note-shadow, -.layer-notes .note .note-shadow { +.layer-notes .note .note-shadow, +.kr_error-header-icon .kr_error-shadow, +.layer-keepRight .kr_error .kr_error-shadow { color: #000; } .note-header-icon .note-fill, -.layer-notes .note .note-fill { +.layer-notes .note .note-fill, +.kr_error-header-icon .kr_error-fill, +.layer-keepRight .kr_error .kr_error-fill { color: #ff3300; stroke: #333; stroke-width: 40px; @@ -55,7 +63,6 @@ /* Custom Map Data (geojson, gpx, kml, vector tile) */ - .layer-mapdata { pointer-events: none; } @@ -115,3 +122,52 @@ stroke-miterlimit: 1; } + +/* OSM Note UI */ +.note-header, +.kr_error-header { + background-color: #f6f6f6; + border-radius: 5px; + border: 1px solid #ccc; + display: flex; + flex-flow: row nowrap; + align-items: center; +} + +.note-header-icon, +.kr_error-header-icon { + background-color: #fff; + padding: 10px; + flex: 0 0 62px; + position: relative; + width: 60px; + height: 60px; + border-right: 1px solid #ccc; + border-radius: 5px 0 0 5px; +} +[dir='rtl'] .note-header-icon, +[dir='rtl'] .kr_error-header-icon { + border-right: unset; + border-left: 1px solid #ccc; + border-radius: 0 5px 5px 0; +} + +.note-header-icon .icon-wrap, +.kr_error-header-icon .icon-wrap { + position: absolute; + top: 0px; +} + +.note-header-label, +.kr_error-header-label { + background-color: #f6f6f6; + padding: 0 15px; + flex: 1 1 100%; + font-size: 14px; + font-weight: bold; + border-radius: 0 5px 5px 0; +} +[dir='rtl'] .note-header-label, +[dir='rtl'] .kr_error-header-label { + border-radius: 5px 0 0 5px; +} diff --git a/modules/osm/keepRight.js b/modules/osm/keepRight.js index 95d8d14bb..45a422aad 100644 --- a/modules/osm/keepRight.js +++ b/modules/osm/keepRight.js @@ -3,14 +3,18 @@ var keepRightSchema = { 'error_id': 0, 'error_type': 0, 'error_name': 0, - 'object_type': ['node', -'way', -'relation'], + 'object_type': [ + 'node', + 'way', + 'relation' + ], 'object_id': 0, - 'state': ['new', -'reopened', -'ignore_temporarily', -'ignore'], + 'state': [ + 'new', + 'reopened', + 'ignore_temporarily', + 'ignore' + ], 'first_occurrence': new Date(), 'last_checked': new Date(), 'object_timestamp': new Date(), @@ -27,445 +31,445 @@ var keepRightSchema = { 'txt5': '' }; - var errorSchema = { - errors: { - 0: { - errorType: 0, - errorName: '', - message: '', - subTypes: {} - }, - 30: { - errorType: 30, - errorName: 'non_closed_areas', - message: 'This way is tagged with \'$1=$2\' and should be closed-loop', - subTypes: {} - }, - 40: { - errorType: 40, - errorName: 'dead ended oneways', - message: 'The first node (id $1) of this one-way is not connected to any other way', - subTypes: { - 41: { - errorType: 41, - errorName: '', - message: 'The last node (id $1) of this one-way is not connected to any other way' - }, - 42: { - errorType: 42, - errorName: '', - message: 'This node cannot be reached, because one-ways only lead away from here' - }, - 43: { - errorType: 43, - errorName: '', - message: 'You cannot escape from this node, because one-ways only lead to here' - }, +var errorSchema = { + errors: { + 0: { + errorType: 0, + errorName: '', + message: '', + subTypes: {} + }, + 30: { + errorType: 30, + errorName: 'non_closed_areas', + message: 'This way is tagged with \'$1=$2\' and should be closed-loop', + subTypes: {} + }, + 40: { + errorType: 40, + errorName: 'dead ended oneways', + message: 'The first node (id $1) of this one-way is not connected to any other way', + subTypes: { + 41: { + errorType: 41, + errorName: '', + message: 'The last node (id $1) of this one-way is not connected to any other way' + }, + 42: { + errorType: 42, + errorName: '', + message: 'This node cannot be reached, because one-ways only lead away from here' + }, + 43: { + errorType: 43, + errorName: '', + message: 'You cannot escape from this node, because one-ways only lead to here' + }, + } + }, + 50: { + errorType: 50, + errorName: 'almost junctions', + message: 'This node is very close but not connected to way #$1', + subTypes: {} + }, + 60: { + errorType: 60, + errorName: 'depreciated tags', + message: 'This $1 uses deprecated tag $2 = $3. Please use $4 instead!', + subTypes: {} + }, + 70: { + errorType: 70, + errorName: 'missing tags', + message: 'This $1 has an empty tag: $2', + 71: { + errorType: 71, + errorName: '', + message: 'This way has no tags' + }, + 72: { + errorType: 72, + errorName: '', + message: 'This node is not member of any way and does not have any tags' } - }, - 50: { - errorType: 50, - errorName: 'almost junctions', - message: 'This node is very close but not connected to way #$1', - subTypes: {} - }, - 60: { - errorType: 60, - errorName: 'depreciated tags', - message: 'This $1 uses deprecated tag $2 = $3. Please use $4 instead!', - subTypes: {} - }, - 70: { - errorType: 70, - errorName: 'missing tags', - message: 'This $1 has an empty tag: $2', - 71: { - errorType: 71, - errorName: '', - message: 'This way has no tags' - }, - 72: { - errorType: 72, - errorName: '', - message: 'This node is not member of any way and does not have any tags' - } - }, - 90: { - errorType: 90, - errorName: 'motorways without ref', - message: 'This way is tagged as motorway and therefore needs a ref, nat_ref or int_ref tag' - }, - 100: { - errorType: 100, - errorName: 'places of worship without religion', - message: 'This $1 is tagged as place of worship and therefore needs a religion tag' - }, - 110: { - errorType: 110, - errorName: 'point of interest without name', - message: 'This node is tagged as $1 and therefore needs a name tag' - }, - 120: { - errorType: 120, - errorName: 'ways without nodes', - message: 'This way has just one single node' - }, - 130: { - errorType: 130, - errorName: 'floating islands', - message: 'This way is not connected to the rest of the map' - }, - 150: { - errorType: 150, - errorName: 'railway crossing without tag', - message: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' - }, - 160: { - errorType: 160, - errorName: 'wrongly used railway tag', - message: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' - }, - 170: { - errorType: 0, - errorName: 'FIXME tagged items', - message: '$1' - }, - 180: { - errorType: 180, - errorName: 'relations without type', - message: 'This relation has no type tag, which is mandatory for relations' - }, - 190: { - errorType: 190, - errorName: 'intersections without junctions', - message: 'Finds way crossings on same layer without common node as a junction', - subtypes: { - 191: { - errorType: 191, - errorName: 'highway-highway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 192: { - errorType: 192, - errorName: 'highway-waterway', - message: 'This $1 intersects the $2 #$3' - }, - 193: { - errorType: 193, - errorName: 'highway-riverbank', - message: 'This $1 intersects the $2 #$3' - }, - 194: { - errorType: 194, - errorName: 'waterway-waterway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 195: { - errorType: 195, - errorName: 'cycleway-cycleway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 196: { - errorType: 196, - errorName: 'highway-cycleway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 197: { - errorType: 197, - errorName: 'cycleway-waterway', - message: 'This $1 intersects the $2 #$3' - }, - 198: { - errorType: 198, - errorName: 'cycleway-riverbank', - message: 'This $1 intersects the $2 #$3' - } - } - }, - 200: { - errorType: 200, - errorName: 'intersections without junctions', - message: 'Finds overlapping ways on same layer.', - subtypes: { - 201: { - errorType: 201, - errorName: 'highway-highway', - message: 'This $1 overlaps the $2 #$3' - }, - 202: { - errorType: 202, - errorName: 'highway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 203: { - errorType: 203, - errorName: 'highway-riverbank', - message: 'This $1 overlaps the $2 #$3' - }, - 204: { - errorType: 204, - errorName: 'waterway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 205: { - errorType: 205, - errorName: 'cycleway-cycleway', - message: 'This $1 overlaps the $2 #$3' - }, - 206: { - errorType: 206, - errorName: 'highway-cycleway', - message: 'This $1 overlaps the $2 #$3' - }, - 207: { - errorType: 207, - errorName: 'cycleway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 208: { - errorType: 208, - errorName: 'cycleway-riverbank', - message: 'This $1 overlaps the $2 #$3' - } - } - }, - 210: { - errorType: 210, - errorName: 'loopings', - message: 'These errors contain self intersecting ways', - subTypes: { - 211: { - errorType: 211, - errorName: '', - message: 'This way contains more than one node at least twice. Nodes are $1. This may or may not be an error' - }, - 212: { - errorType: 212, - errorName: '', - message: 'This way has only two different nodes and contains one of them more than once' - }, - } - }, - 220: { - errorType: 220, - errorName: 'misspelled tags', - message: ' This $1 is tagged \'$2=$3\' where $4 looks like $5', - subTypes: { - 221: { - errorType: 221, - errorName: 'misspelled tags', - message: 'The key of this $1\'s tag is \'key\': $2' - } - } - }, - 230: { - errorType: 230, - errorName: 'layer conflicts', - message: '', - subTypes: { - 231: { - errorType: 231, - errorName: 'mixed layers intersection', - message: 'This node is a junction of ways on different layers: $1' - }, - 232: { - errorType: 232, - errorName: 'strange layers', - message: 'This $1 is tagged with layer $2. This need not be an error, but it looks strange' - } - } - }, - 270: { - errorType: 270, - errorName: 'motorways connected directly', - message: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or if it is a service=parking_aisle.' - }, - 280: { - errorType: 280, - errorName: 'boundaries', - message: '', - subTypes: { - 281: { - errorType: 281, - errorName: 'missing name', - message: 'This boundary has no name' - }, - 282: { - errorType: 282, - errorName: 'missing admin level', - message: 'The boundary of $1 has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' - }, - 283: { - errorType: 283, - errorName: 'no closed loop', - message: 'The boundary of $1 is not closed-loop' - }, - 284: { - errorType: 284, - errorName: 'splitting boundary', - message: 'The boundary of $1 splits here' - }, - 285: { - errorType: 285, - errorName: 'admin_level too high', - message: 'This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' - }, - } - }, - 290: { - errorType: 290, - errorName: 'faulty restrictions', - message: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv, type=restriction:caravan, type=restriction:motorcar, type=restriction:bus, type=restriction:agricultural, type=restriction:motorcycle, type=restriction:bicycle and type=restriction:hazmat.', - subTypes: { - 291: { - errorType: 291, - errorName: 'missing type', - message: 'This turn-restriction has no known restriction type' - }, - 292: { - errorType: 292, - errorName: 'missing from way', - message: 'A turn-restriction needs exactly one $1 member. This one has $2' - }, - 293: { - errorType: 293, - errorName: 'missing to way', - message: 'A turn-restriction needs exactly one $1 member. This one has $2' - }, - 294: { - errorType: 294, - errorName: 'from or to not a way', - message: 'From- and To-members of turn restrictions need to be ways. $1' - }, - 295: { - errorType: 295, - errorName: 'via is not on the way ends', - message: 'via (node #$1) is not the first or the last member of from (way #$2)' - }, - 296: { - errorType: 296, - errorName: 'wrong restriction angle', - message: 'restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?' - }, - 297: { - errorType: 297, - errorName: 'wrong direction of to member', - message: 'wrong direction of to way $1' - }, - 298: { - errorType: 298, - errorName: 'already restricted by oneway', - message: 'entry already prohibited by oneway tag on $1' - }, - } - }, - 310: { - errorType: 310, - errorName: 'roundabouts', - message: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.', - subTypes: { - 311: { - errorType: 311, - errorName: 'not closed loop', - message: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' - }, - 312: { - errorType: 312, - errorName: 'wrong direction', - message: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' - }, - 313: { - errorType: 313, - errorName: 'faintly connected', - message: 'This roundabout has only $1 other roads connected. Roundabouts typically have three.' - }, - } - }, - 320: { - errorType: 320, - errorName: '*link connections', - message: 'This way is tagged as highway=$1_link but doesn\'t have a connection to any other $1 or $1_link' - }, - 350: { - errorType: 350, - errorName: 'bridge tags', - message: 'This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: $1' - }, - 370: { - errorType: 370, - errorName: 'doubled places', - message: 'This node has tags in common with the surrounding way #$1 and seems to be redundand | This node has tags in common with the surrounding way #$1 (including the name \'$2\') and seems to be redundand' - }, - 380: { - errorType: 380, - errorName: 'non-physical use of sportage', - message: 'This way is tagged $1 but has no physical tag like e.g. leisure, building, amenity or highway' - }, - 400: { - errorType: 400, - errorName: 'geometry glitches', - message: '', - subTypes: { - 401: { - errorType: 401, - errorName: 'missing turn restrictions', - message: 'ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning' - }, - 402: { - errorType: 402, - errorName: 'impossible angles', - message: 'this way bends in a very sharp angle here' - }, - } - }, - 410: { - errorType: 410, - errorName: 'websites', - message: 'Web pages are analyzed. Web page is defined by any of the following tags website=*, url=*, website:mobile=*, contact:website=*, contact:url=*, image=*, source:website=* or source:url=*.', - subTypes: { - 411: { - errorType: 411, - errorName: 'http error', - message: 'The URL ($1) cannot be opened (HTTP status code $2)' - }, - 412: { - errorType: 412, - errorName: 'domain hijacking', - message: 'Possible domain squatting: $1. Suspicious text is: "$2"' - }, - 413: { - errorType: 413, - errorName: 'non-match', - message: 'Content of the URL ($1) did not contain these keywords: ($2)' - }, + }, + 90: { + errorType: 90, + errorName: 'motorways without ref', + message: 'This way is tagged as motorway and therefore needs a ref, nat_ref or int_ref tag' + }, + 100: { + errorType: 100, + errorName: 'places of worship without religion', + message: 'This $1 is tagged as place of worship and therefore needs a religion tag' + }, + 110: { + errorType: 110, + errorName: 'point of interest without name', + message: 'This node is tagged as $1 and therefore needs a name tag' + }, + 120: { + errorType: 120, + errorName: 'ways without nodes', + message: 'This way has just one single node' + }, + 130: { + errorType: 130, + errorName: 'floating islands', + message: 'This way is not connected to the rest of the map' + }, + 150: { + errorType: 150, + errorName: 'railway crossing without tag', + message: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + }, + 160: { + errorType: 160, + errorName: 'wrongly used railway tag', + message: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + }, + 170: { + errorType: 0, + errorName: 'FIXME tagged items', + message: '$1' + }, + 180: { + errorType: 180, + errorName: 'relations without type', + message: 'This relation has no type tag, which is mandatory for relations' + }, + 190: { + errorType: 190, + errorName: 'intersections without junctions', + message: 'Finds way crossings on same layer without common node as a junction', + subtypes: { + 191: { + errorType: 191, + errorName: 'highway-highway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 192: { + errorType: 192, + errorName: 'highway-waterway', + message: 'This $1 intersects the $2 #$3' + }, + 193: { + errorType: 193, + errorName: 'highway-riverbank', + message: 'This $1 intersects the $2 #$3' + }, + 194: { + errorType: 194, + errorName: 'waterway-waterway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 195: { + errorType: 195, + errorName: 'cycleway-cycleway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 196: { + errorType: 196, + errorName: 'highway-cycleway', + message: 'This $1 intersects the $2 #$3 but there is no junction node' + }, + 197: { + errorType: 197, + errorName: 'cycleway-waterway', + message: 'This $1 intersects the $2 #$3' + }, + 198: { + errorType: 198, + errorName: 'cycleway-riverbank', + message: 'This $1 intersects the $2 #$3' } } }, - warnings: { - 20: { - errorType: 20, - errorName: 'multiple nodes on the same spot', - message: ' There is more than one node in this spot. Offending node IDs: $1' - }, - 60: { - errorType: 60, - errorName: '', - message: '' - }, - 300: { - errorType: 300, - errorName: 'missing maxspeed', - message: 'missing maxspeed tag' - }, - 360: { - errorType: 360, - errorName: 'language unknown', - message: 'It would be nice if this $1 had an additional tag \'name:XX=$2\' where XX shows the language of its name \'$2\'.' - }, - 390: { - errorType: 390, - errorName: 'missing tracktype', - message: 'This track doesn\'t have a tracktype' - }, + 200: { + errorType: 200, + errorName: 'intersections without junctions', + message: 'Finds overlapping ways on same layer.', + subtypes: { + 201: { + errorType: 201, + errorName: 'highway-highway', + message: 'This $1 overlaps the $2 #$3' + }, + 202: { + errorType: 202, + errorName: 'highway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 203: { + errorType: 203, + errorName: 'highway-riverbank', + message: 'This $1 overlaps the $2 #$3' + }, + 204: { + errorType: 204, + errorName: 'waterway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 205: { + errorType: 205, + errorName: 'cycleway-cycleway', + message: 'This $1 overlaps the $2 #$3' + }, + 206: { + errorType: 206, + errorName: 'highway-cycleway', + message: 'This $1 overlaps the $2 #$3' + }, + 207: { + errorType: 207, + errorName: 'cycleway-waterway', + message: 'This $1 overlaps the $2 #$3' + }, + 208: { + errorType: 208, + errorName: 'cycleway-riverbank', + message: 'This $1 overlaps the $2 #$3' + } + } }, - }; \ No newline at end of file + 210: { + errorType: 210, + errorName: 'loopings', + message: 'These errors contain self intersecting ways', + subTypes: { + 211: { + errorType: 211, + errorName: '', + message: 'This way contains more than one node at least twice. Nodes are $1. This may or may not be an error' + }, + 212: { + errorType: 212, + errorName: '', + message: 'This way has only two different nodes and contains one of them more than once' + }, + } + }, + 220: { + errorType: 220, + errorName: 'misspelled tags', + message: ' This $1 is tagged \'$2=$3\' where $4 looks like $5', + subTypes: { + 221: { + errorType: 221, + errorName: 'misspelled tags', + message: 'The key of this $1\'s tag is \'key\': $2' + } + } + }, + 230: { + errorType: 230, + errorName: 'layer conflicts', + message: '', + subTypes: { + 231: { + errorType: 231, + errorName: 'mixed layers intersection', + message: 'This node is a junction of ways on different layers: $1' + }, + 232: { + errorType: 232, + errorName: 'strange layers', + message: 'This $1 is tagged with layer $2. This need not be an error, but it looks strange' + } + } + }, + 270: { + errorType: 270, + errorName: 'motorways connected directly', + message: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or if it is a service=parking_aisle.' + }, + 280: { + errorType: 280, + errorName: 'boundaries', + message: '', + subTypes: { + 281: { + errorType: 281, + errorName: 'missing name', + message: 'This boundary has no name' + }, + 282: { + errorType: 282, + errorName: 'missing admin level', + message: 'The boundary of $1 has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' + }, + 283: { + errorType: 283, + errorName: 'no closed loop', + message: 'The boundary of $1 is not closed-loop' + }, + 284: { + errorType: 284, + errorName: 'splitting boundary', + message: 'The boundary of $1 splits here' + }, + 285: { + errorType: 285, + errorName: 'admin_level too high', + message: 'This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + }, + } + }, + 290: { + errorType: 290, + errorName: 'faulty restrictions', + message: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv, type=restriction:caravan, type=restriction:motorcar, type=restriction:bus, type=restriction:agricultural, type=restriction:motorcycle, type=restriction:bicycle and type=restriction:hazmat.', + subTypes: { + 291: { + errorType: 291, + errorName: 'missing type', + message: 'This turn-restriction has no known restriction type' + }, + 292: { + errorType: 292, + errorName: 'missing from way', + message: 'A turn-restriction needs exactly one $1 member. This one has $2' + }, + 293: { + errorType: 293, + errorName: 'missing to way', + message: 'A turn-restriction needs exactly one $1 member. This one has $2' + }, + 294: { + errorType: 294, + errorName: 'from or to not a way', + message: 'From- and To-members of turn restrictions need to be ways. $1' + }, + 295: { + errorType: 295, + errorName: 'via is not on the way ends', + message: 'via (node #$1) is not the first or the last member of from (way #$2)' + }, + 296: { + errorType: 296, + errorName: 'wrong restriction angle', + message: 'restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?' + }, + 297: { + errorType: 297, + errorName: 'wrong direction of to member', + message: 'wrong direction of to way $1' + }, + 298: { + errorType: 298, + errorName: 'already restricted by oneway', + message: 'entry already prohibited by oneway tag on $1' + }, + } + }, + 310: { + errorType: 310, + errorName: 'roundabouts', + message: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.', + subTypes: { + 311: { + errorType: 311, + errorName: 'not closed loop', + message: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + }, + 312: { + errorType: 312, + errorName: 'wrong direction', + message: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + }, + 313: { + errorType: 313, + errorName: 'faintly connected', + message: 'This roundabout has only $1 other roads connected. Roundabouts typically have three.' + }, + } + }, + 320: { + errorType: 320, + errorName: '*link connections', + message: 'This way is tagged as highway=$1_link but doesn\'t have a connection to any other $1 or $1_link' + }, + 350: { + errorType: 350, + errorName: 'bridge tags', + message: 'This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: $1' + }, + 370: { + errorType: 370, + errorName: 'doubled places', + message: 'This node has tags in common with the surrounding way #$1 and seems to be redundand | This node has tags in common with the surrounding way #$1 (including the name \'$2\') and seems to be redundand' + }, + 380: { + errorType: 380, + errorName: 'non-physical use of sportage', + message: 'This way is tagged $1 but has no physical tag like e.g. leisure, building, amenity or highway' + }, + 400: { + errorType: 400, + errorName: 'geometry glitches', + message: '', + subTypes: { + 401: { + errorType: 401, + errorName: 'missing turn restrictions', + message: 'ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning' + }, + 402: { + errorType: 402, + errorName: 'impossible angles', + message: 'this way bends in a very sharp angle here' + }, + } + }, + 410: { + errorType: 410, + errorName: 'websites', + message: 'Web pages are analyzed. Web page is defined by any of the following tags website=*, url=*, website:mobile=*, contact:website=*, contact:url=*, image=*, source:website=* or source:url=*.', + subTypes: { + 411: { + errorType: 411, + errorName: 'http error', + message: 'The URL ($1) cannot be opened (HTTP status code $2)' + }, + 412: { + errorType: 412, + errorName: 'domain hijacking', + message: 'Possible domain squatting: $1. Suspicious text is: "$2"' + }, + 413: { + errorType: 413, + errorName: 'non-match', + message: 'Content of the URL ($1) did not contain these keywords: ($2)' + }, + } + } + }, + warnings: { + 20: { + errorType: 20, + errorName: 'multiple nodes on the same spot', + message: ' There is more than one node in this spot. Offending node IDs: $1' + }, + 60: { + errorType: 60, + errorName: '', + message: '' + }, + 300: { + errorType: 300, + errorName: 'missing maxspeed', + message: 'missing maxspeed tag' + }, + 360: { + errorType: 360, + errorName: 'language unknown', + message: 'It would be nice if this $1 had an additional tag \'name:XX=$2\' where XX shows the language of its name \'$2\'.' + }, + 390: { + errorType: 390, + errorName: 'missing tracktype', + message: 'This track doesn\'t have a tracktype' + }, + }, +}; \ No newline at end of file diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 01d80344f..20b62f2f5 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -8,6 +8,8 @@ import rbush from 'rbush'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { request as d3_request } from 'd3-request'; +import { geoExtent } from '../geo'; + import { utilRebind, utilTiler, @@ -24,18 +26,6 @@ var _keepRightZoom = 16; var apiBase = 'https://www.keepright.at/export.php?'; -// TODO: remove this - var schema = { - 'error_type': '', - 'object_type': '', - 'object_id': '', - 'comment': '', - 'error_id':'', - 'schema': '', - 'description': '', - 'title': '' - }; - function abortRequest(i) { if (i) { @@ -70,33 +60,34 @@ export default { _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; }, - loadKeepRight: function(context, projection, keepRightOptions) { - keepRightOptions = _extend({ 'format': 'geojson' }); + loadKeepRight: function(context, projection, options, callback) { + options = _extend({ 'format': 'geojson' }, options); if (_off) return; + var cache = _keepRightCache; + var that = this; var path = apiBase + - 'format=' + keepRightOptions.format + - '&ch=' + keepRightOptions.ch.join() + '&'; + 'format=' + options.format + + '&ch=' + options.ch.join() + '&'; // determine the needed tiles to cover the view var tiles = tiler.zoomExtent([_keepRightZoom, _keepRightZoom]).getTiles(projection); // abort inflight requests that are no longer needed - var hadRequests = !_isEmpty(_keepRightCache.inflight); - abortUnwantedRequests(_keepRightCache, tiles); - if (hadRequests && _isEmpty(_keepRightCache.inflight)) { + var hadRequests = !_isEmpty(cache.inflight); + abortUnwantedRequests(cache, tiles); + if (hadRequests && _isEmpty(cache.inflight)) { dispatch.call('loaded'); // stop the spinner } // issue new requests.. tiles.forEach(function(tile) { - if (_keepRightCache.loaded[tile.id] || _keepRightCache.inflight[tile.id]) return; - if (_isEmpty(_keepRightCache.inflight)) { + if (cache.loaded[tile.id] || cache.inflight[tile.id]) return; + if (_isEmpty(cache.inflight)) { dispatch.call('loading'); // start the spinner } - var cache = _keepRightCache; var rect = tile.extent.rectangle(); var nextPath = path + utilQsString({ @@ -107,31 +98,78 @@ export default { }); - function callbackExample() { - // TODO: implement - } + var options = {}; // TODO: implement - var exampleOptions = {}; // TODO: implement - - _keepRightCache.inflight[tile.id] = that.loadFromAPI( + cache.inflight[tile.id] = that.loadFromAPI( nextPath, - callbackExample, - exampleOptions + function(err, data) { + if (err || !data.features || !data.features.length) return; + + cache.loaded[tile.id] = true; + delete cache.inflight[tile.id]; + + if (callback) { + callback(err, _extend({ data: data }, tile)); + } + if (_isEmpty(cache.inflight)) { + dispatch.call('loaded'); // stop the spinner + } + }, + options ); }); }, loadFromAPI: function(path, callback, options) { - var result = d3_request(path) // TODO: rturn or somethign, dont save to var + var cache = _keepRightCache; + + return d3_request(path) .mimeType('application/json') // TODO: only have this as a response if the input format is json .header('Content-type', 'application/x-www-form-urlencoded') .response(function(xhr) { - console.log('xhr: ', xhr); return JSON.parse(xhr.responseText); }) .get(function(err, data) { - console.log(data); + + var features = data.features.map(function(feature) { + var loc = feature.geometry.coordinates; + var props = feature.properties; + var d = { + loc: loc, + comment: props.comment || null, + description: props.description || '', + error_id: props.error_id, + error_type: props.error_type, + object_id: props.object_id, + object_type: props.object_type, + schema: props.schema, + title: props.title + }; + + cache.keepRight[d.error_id] = d; + + return { + minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d + }; + + }).filter(Boolean); + + cache.rtree.load(features); + dispatch.call('loadedKeepRight'); + + callback(err, data); }); - console.log('result: ', result); - } + }, + + + // get all cached notes covering the viewport + keepRight: function(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); + + return _keepRightCache.rtree.search(bbox) + .map(function(d) { return d.data; }); + }, }; \ No newline at end of file diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 6352a8cc0..d75f1b104 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -80,46 +80,53 @@ export function svgKeepRight(projection, context, dispatch) { function update() { - console.log('TAH - keepRight.update()'); - return; var service = getService(); - var data = (service ? service.signs(projection) : []); - var viewer = d3_select('#photoviewer'); - var selected = viewer.empty() ? undefined : viewer.datum(); - var selectedImageKey = selected && selected.key; + var selectedID = context.selectedNoteID(); // TODO: update with selectedErrorID + var data = (service ? service.keepRight(projection) : []); var transform = svgPointTransform(projection); - - var signs = layer.selectAll('.icon-sign') - .data(data, function(d) { return d.key; }); + var kr_errors = layer.selectAll('.kr_error') + .data(data, function(d) { return d.error_id; }); // exit - signs.exit() + kr_errors.exit() .remove(); // enter - var enter = signs.enter() + var kr_errorsEnter = kr_errors.enter() + .append('g') + .attr('class', function(d) { return 'kr_error kr_error-' + d.error_id; }) + .classed('new', function(d) { return d.id < 0; }); + + kr_errorsEnter + .append('ellipse') + .attr('cx', 0.5) + .attr('cy', 1) + .attr('rx', 6.5) + .attr('ry', 3) + .attr('class', 'stroke'); + + // kr_errorsEnter + // .append('path') + // .call(markerPath, 'kr_error-shadow'); + + kr_errorsEnter .append('use') - .attr('class', 'icon-sign') - .attr('width', '24px') - .attr('height', '24px') - .attr('x', '-12px') - .attr('y', '-12px') - .attr('xlink:href', function(d) { return '#' + d.value; }) - .classed('selected', function(d) { - return _some(d.detections, function(detection) { - return detection.image_key === selectedImageKey; - }); - }) - .on('click', click); + .attr('class', 'kr_error-fill') + .attr('width', '20px') + .attr('height', '20px') + .attr('x', '-8px') + .attr('y', '-22px') + .attr('xlink:href', '#iD-icon-note'); // TODO: update icon // update - signs - .merge(enter) + kr_errors + .merge(kr_errorsEnter) .sort(function(a, b) { - return (a === selected) ? 1 - : (b === selected) ? -1 + return (a.id === selectedID) ? 1 + : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1]; // sort Y }) + .classed('selected', function(d) { return d.id === selectedID; }) .attr('transform', transform); } @@ -140,14 +147,18 @@ export function svgKeepRight(projection, context, dispatch) { .style('display', enabled ? 'block' : 'none') .merge(layer); + function exampleCallback(value1, value2, value3) { // TODO: rename, possibly remove function + } + if (enabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); update(); - var options = { - ch: ['30', ] + var options = { // TODO: change out these options and place as default + ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413] }; - service.loadKeepRight(context, projection, options); + + service.loadKeepRight(context, projection, options, exampleCallback); } else { editOff(); } diff --git a/modules/svg/layers.js b/modules/svg/layers.js index b8810733f..59c9ab22c 100644 --- a/modules/svg/layers.js +++ b/modules/svg/layers.js @@ -9,12 +9,8 @@ import { select as d3_select } from 'd3-selection'; import { svgData } from './data'; import { svgDebug } from './debug'; -<<<<<<< HEAD import { svgGeolocate } from './geolocate'; -======= -import { svgGpx } from './gpx'; import { svgKeepRight } from './keepRight'; ->>>>>>> added simple keepRight button under photoItems import { svgStreetside } from './streetside'; import { svgMapillaryImages } from './mapillary_images'; import { svgMapillarySigns } from './mapillary_signs'; diff --git a/svg/iD-sprite/icons/icon-keepRight.svg b/svg/iD-sprite/icons/icon-keepRight.svg new file mode 100644 index 000000000..0498225bb --- /dev/null +++ b/svg/iD-sprite/icons/icon-keepRight.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js index f2277229c..16aeed0ed 100644 --- a/test/spec/svg/layers.js +++ b/test/spec/svg/layers.js @@ -27,6 +27,7 @@ describe('iD.svgLayers', function () { container.call(iD.svgLayers(projection, context)); var nodes = container.selectAll('svg .data-layer').nodes(); expect(nodes.length).to.eql(10); +<<<<<<< HEAD expect(d3.select(nodes[0]).classed('osm')).to.be.true; expect(d3.select(nodes[1]).classed('notes')).to.be.true; expect(d3.select(nodes[2]).classed('data')).to.be.true; @@ -37,6 +38,18 @@ describe('iD.svgLayers', function () { expect(d3.select(nodes[7]).classed('debug')).to.be.true; expect(d3.select(nodes[8]).classed('geolocate')).to.be.true; expect(d3.select(nodes[9]).classed('touch')).to.be.true; +======= + expect(d3.select(nodes[0]).classed('data-layer-osm')).to.be.true; + expect(d3.select(nodes[1]).classed('data-layer-notes')).to.be.true; + expect(d3.select(nodes[2]).classed('data-layer-keepRight')).to.be.true; + expect(d3.select(nodes[3]).classed('data-layer-gpx')).to.be.true; + expect(d3.select(nodes[4]).classed('data-layer-mvt')).to.be.true; + expect(d3.select(nodes[5]).classed('data-layer-streetside')).to.be.true; + expect(d3.select(nodes[6]).classed('data-layer-mapillary-images')).to.be.true; + expect(d3.select(nodes[7]).classed('data-layer-mapillary-signs')).to.be.true; + expect(d3.select(nodes[8]).classed('data-layer-openstreetcam-images')).to.be.true; + expect(d3.select(nodes[9]).classed('data-layer-debug')).to.be.true; +>>>>>>> displaying keep right (currently as notes) }); }); From 31f35b03894b24a4a87294b191ecd051c3c28ed1 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Sat, 28 Jul 2018 16:25:37 -0400 Subject: [PATCH 005/114] added basics to sidebar --- data/core.yaml | 4 +- dist/locales/en.json | 4 +- modules/behavior/hover.js | 4 +- modules/behavior/select.js | 13 +- modules/core/context.js | 7 + modules/modes/index.js | 1 + modules/modes/select_error.js | 126 ++++++++++++ modules/osm/index.js | 1 + modules/osm/keepRight.js | 48 ++++- modules/renderer/map.js | 3 +- modules/services/keepRight.js | 14 +- modules/services/osm.js | 4 + modules/svg/keepRight.js | 4 +- modules/ui/index.js | 4 + modules/ui/keepRight_comment.js | 118 ++++++++++++ modules/ui/keepRight_editor.js | 331 ++++++++++++++++++++++++++++++++ modules/ui/keepRight_header.js | 50 +++++ modules/ui/sidebar.js | 32 +-- modules/ui/view_on_keepRight.js | 45 +++++ 19 files changed, 787 insertions(+), 26 deletions(-) create mode 100644 modules/modes/select_error.js create mode 100644 modules/ui/keepRight_comment.js create mode 100644 modules/ui/keepRight_editor.js create mode 100644 modules/ui/keepRight_header.js create mode 100644 modules/ui/view_on_keepRight.js diff --git a/data/core.yaml b/data/core.yaml index ffa4f71b9..6c7176e1f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -408,6 +408,7 @@ en: documentation_redirect: This documentation has been redirected to a new page show_more: Show More view_on_osm: View on openstreetmap.org + view_on_keepRight: View on keepright.at all_fields: All fields all_tags: All tags all_members: All members @@ -644,8 +645,9 @@ en: cannot_zoom: "Cannot zoom out further in current mode." full_screen: Toggle Full Screen keepRight: + keepRight: Error - tooltip: Q/A data from keepright.at - title: Keep Right + title: Edit Error streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index c75162e1d..47a566277 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -502,6 +502,7 @@ "documentation_redirect": "This documentation has been redirected to a new page", "show_more": "Show More", "view_on_osm": "View on openstreetmap.org", + "view_on_keepRight": "View on keepright.at", "all_fields": "All fields", "all_tags": "All tags", "all_members": "All members", @@ -783,8 +784,9 @@ "cannot_zoom": "Cannot zoom out further in current mode.", "full_screen": "Toggle Full Screen", "keepRight": { + "keepRight": "Error -", "tooltip": "Q/A data from keepright.at", - "title": "Keep Right" + "title": "Edit Error" }, "streetside": { "tooltip": "Streetside photos from Microsoft", diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index b7bd503de..10ba3a3bd 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -5,7 +5,7 @@ import { select as d3_select } from 'd3-selection'; -import { osmEntity, osmNote } from '../osm'; +import { osmEntity, osmNote, krError } from '../osm'; import { utilKeybinding, utilRebind } from '../util'; @@ -112,7 +112,7 @@ export function behaviorHover(context) { entity = datum; selector = '.data' + datum.__featurehash__; - } else if (datum instanceof osmNote) { + } else if (datum instanceof osmNote || datum instanceof krError) { entity = datum; selector = '.note-' + datum.id; diff --git a/modules/behavior/select.js b/modules/behavior/select.js index bbb6df526..8c445930d 100644 --- a/modules/behavior/select.js +++ b/modules/behavior/select.js @@ -12,12 +12,14 @@ import { modeBrowse, modeSelect, modeSelectData, - modeSelectNote + modeSelectNote, + modeSelectError } from '../modes'; import { osmEntity, - osmNote + osmNote, + krError } from '../osm'; @@ -130,6 +132,7 @@ export function behaviorSelect(context) { if (datum instanceof osmEntity) { // clicked an entity.. var selectedIDs = context.selectedIDs(); context.selectedNoteID(null); + context.selectedErrorID(null); if (!isMultiselect) { if (selectedIDs.length > 1 && (!suppressMenu && !isShowAlways)) { @@ -167,9 +170,13 @@ export function behaviorSelect(context) { context .selectedNoteID(datum.id) .enter(modeSelectNote(context, datum.id)); - + } else if (datum instanceof krError & !isMultiselect) { // clicked a krError error + context + .selectedErrorID(datum.id) + .enter(modeSelectError(context, datum.id)); } else { // clicked nothing.. context.selectedNoteID(null); + context.selectedErrorID(null); if (!isMultiselect && mode.id !== 'browse') { context.enter(modeBrowse(context)); } diff --git a/modules/core/context.js b/modules/core/context.js index 53385070d..341c24901 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -263,6 +263,13 @@ export function coreContext() { return context; }; + var _selectedErrorID; + context.selectedErrorID = function(errorID) { + if (!arguments.length) return _selectedErrorID; + _selectedErrorID = errorID; + return context; + }; + /* Behaviors */ context.install = function(behavior) { diff --git a/modules/modes/index.js b/modules/modes/index.js index af440c4c2..f7f4d985c 100644 --- a/modules/modes/index.js +++ b/modules/modes/index.js @@ -12,4 +12,5 @@ export { modeRotate } from './rotate'; export { modeSave } from './save'; export { modeSelect } from './select'; export { modeSelectData } from './select_data'; +export { modeSelectError} from './select_error'; export { modeSelectNote } from './select_note'; diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js new file mode 100644 index 000000000..8af12b722 --- /dev/null +++ b/modules/modes/select_error.js @@ -0,0 +1,126 @@ +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; + +import { + behaviorBreathe, + behaviorHover, + behaviorLasso, + behaviorSelect +} from '../behavior'; + +import { services } from '../services'; +import { modeBrowse } from './browse'; +import { uiKeepRightEditor } from '../ui'; + + +export function modeSelectError(context, selectedErrorID) { + var mode = { + id: 'select-error', + button: 'browse' + }; + + var keepRight = services.keepRight; + var keybinding = d3_keybinding('select-error'); + var keepRightEditor = uiKeepRightEditor(context) + .on('change', function() { + context.map().pan([0,0]); // trigger a redraw + var error = checkSelectedID(); + if (!error) return; + context.ui().sidebar + .show(keepRightEditor.error(error)); + }); + + var behaviors = [ + behaviorBreathe(context), + behaviorHover(context), + behaviorSelect(context), + behaviorLasso(context), + ]; + + + function checkSelectedID() { + if (!keepRight) return; + var error = keepRight.getError(selectedErrorID); + if (!error) { + context.enter(modeBrowse(context)); + } + return error; + } + + mode.enter = function() { + + // class the error as selected, or return to browse mode if the error is gone + function selectError(drawn) { + if (!checkSelectedID()) return; + + var selection = context.surface() + .selectAll('.kr_error-' + selectedErrorID); + + if (selection.empty()) { + // Return to browse mode if selected DOM elements have + // disappeared because the user moved them out of view.. + var source = d3_event && d3_event.type === 'zoom' && d3_event.sourceEvent; + if (drawn && source && (source.type === 'mousemove' || source.type === 'touchmove')) { + context.enter(modeBrowse(context)); + } + + } else { + selection + .classed('selected', true); + context.selectedErrorID(selectedErrorID); + } + } + + function esc() { + context.enter(modeBrowse(context)); + } + + var error = checkSelectedID(); + if (!error) return; + + behaviors.forEach(function(behavior) { + context.install(behavior); + }); + + keybinding + .on('⎋', esc, true); + + d3_select(document) + .call(keybinding); + + selectError(); + + context.ui().sidebar + .show(keepRightEditor.error(error)); + + context.map() + .on('drawn.select', selectError); + }; + + + mode.exit = function() { + behaviors.forEach(function(behavior) { + context.uninstall(behavior); + }); + + keybinding.off(); + + context.surface() + .selectAll('.kr_error.selected') + .classed('selected hover', false); + + context.map() + .on('drawn.select', null); + + context.ui().sidebar + .hide(); + context.selectedErrorID(null); + }; + + + return mode; +} diff --git a/modules/osm/index.js b/modules/osm/index.js index bbbb4835a..f8d7addc1 100644 --- a/modules/osm/index.js +++ b/modules/osm/index.js @@ -1,5 +1,6 @@ export { osmChangeset } from './changeset'; export { osmEntity } from './entity'; +export { krError } from './keepRight'; export { osmNode } from './node'; export { osmNote } from './note'; export { osmRelation } from './relation'; diff --git a/modules/osm/keepRight.js b/modules/osm/keepRight.js index 45a422aad..76710deba 100644 --- a/modules/osm/keepRight.js +++ b/modules/osm/keepRight.js @@ -1,6 +1,52 @@ +import _extend from 'lodash-es/extend'; + + +export function krError() { + if (!(this instanceof krError)) { + return (new krError()).initialize(arguments); + } else if (arguments.length) { + this.initialize(arguments); + } +} + + +krError.id = function() { + return krError.id.next--; +}; + + +krError.id.next = -1; + + +_extend(krError.prototype, { + + type: 'krError', + + initialize: function(sources) { + for (var i = 0; i < sources.length; ++i) { + var source = sources[i]; + for (var prop in source) { + if (Object.prototype.hasOwnProperty.call(source, prop)) { + if (source[prop] === undefined) { + delete this[prop]; + } else { + this[prop] = source[prop]; + } + } + } + } + + if (!this.id) { + this.id = krError.id() + ''; // as string + } + + return this; + } +}); + var keepRightSchema = { 'schema': '', - 'error_id': 0, + 'id': 0, 'error_type': 0, 'error_name': 0, 'object_type': [ diff --git a/modules/renderer/map.js b/modules/renderer/map.js index ce737f891..170d4a90e 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -354,7 +354,8 @@ export function rendererMap(context) { surface.selectAll('.layer-touch *').remove(); var mode = context.mode(); - if (mode && mode.id !== 'save' && mode.id !== 'select-note' && mode.id !== 'select-data') { + if (mode && mode.id !== 'save' && mode.id !== 'select-note' && + mode.id !== 'select-data' && && mode.id !== 'select-error') { context.enter(modeBrowse(context)); } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 20b62f2f5..f49b48a8a 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -10,6 +10,8 @@ import { request as d3_request } from 'd3-request'; import { geoExtent } from '../geo'; +import { krError } from '../osm'; + import { utilRebind, utilTiler, @@ -134,8 +136,9 @@ export default { var features = data.features.map(function(feature) { var loc = feature.geometry.coordinates; var props = feature.properties; - var d = { + var d = new krError ({ loc: loc, + id: props.error_id, comment: props.comment || null, description: props.description || '', error_id: props.error_id, @@ -144,9 +147,9 @@ export default { object_type: props.object_type, schema: props.schema, title: props.title - }; + }); - cache.keepRight[d.error_id] = d; + cache.keepRight[d.id] = d; return { minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d @@ -172,4 +175,9 @@ export default { return _keepRightCache.rtree.search(bbox) .map(function(d) { return d.data; }); }, + + // get a single error from the cache + getError: function(id) { + return _keepRightCache.keepRight[id]; + }, }; \ No newline at end of file diff --git a/modules/services/osm.js b/modules/services/osm.js index 9e4f355a8..de3a91f4d 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -431,6 +431,10 @@ export default { return urlroot + '/note/' + note.id; }, + keepRightURL: function(error) { + return 'https://www.keepright.at/report_map.php?schema=' + error.schema + '&error=' + error.id; + }, + // Generic method to load data from the OSM API // Can handle either auth or unauth calls. diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index d75f1b104..fb0c76603 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -85,7 +85,7 @@ export function svgKeepRight(projection, context, dispatch) { var data = (service ? service.keepRight(projection) : []); var transform = svgPointTransform(projection); var kr_errors = layer.selectAll('.kr_error') - .data(data, function(d) { return d.error_id; }); + .data(data, function(d) { return d.id; }); // exit kr_errors.exit() @@ -94,7 +94,7 @@ export function svgKeepRight(projection, context, dispatch) { // enter var kr_errorsEnter = kr_errors.enter() .append('g') - .attr('class', function(d) { return 'kr_error kr_error-' + d.error_id; }) + .attr('class', function(d) { return 'kr_error kr_error-' + d.id; }) .classed('new', function(d) { return d.id < 0; }); kr_errorsEnter diff --git a/modules/ui/index.js b/modules/ui/index.js index 3f65819cc..f59aa1657 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -30,6 +30,9 @@ export { uiGeolocate } from './geolocate'; export { uiHelp } from './help'; export { uiInfo } from './info'; export { uiInspector } from './inspector'; +export { uiKeepRightComment } from './keepRight_comment'; +export { uiKeepRightEditor } from './keepRight_editor'; +export { uiKeepRightHeader } from './keepRight_header'; export { uiLasso } from './lasso'; export { uiLoading } from './loading'; export { uiMapData } from './map_data'; @@ -64,4 +67,5 @@ export { uiTooltipHtml } from './tooltipHtml'; export { uiUndoRedo } from './undo_redo'; export { uiVersion } from './version'; export { uiViewOnOSM } from './view_on_osm'; +export { uiViewOnKeepRight } from './view_on_keepRight'; export { uiZoom } from './zoom'; diff --git a/modules/ui/keepRight_comment.js b/modules/ui/keepRight_comment.js new file mode 100644 index 000000000..7977d8fdc --- /dev/null +++ b/modules/ui/keepRight_comment.js @@ -0,0 +1,118 @@ +import { select as d3_select } from 'd3-selection'; + +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { services } from '../services'; +import { utilDetect } from '../util/detect'; + + +export function uiKeepRightComment() { + var _error; + + + function keepRightComment(selection) { + if (!_error.comment) return; + var comment = selection.selectAll('.comments-container') + .data([0]); + + comment = comment.enter() + .append('div') + .attr('class', 'comments-container') + .merge(comment); + + var commentEnter = comment.selectAll('.comment') + .data(_error.comment) + .enter() + .append('div') + .attr('class', 'comment'); + + commentEnter + .append('div') + .attr('class', function(d) { return 'comment-avatar user-' + d.uid; }) + .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); + + var mainEnter = commentEnter + .append('div') + .attr('class', 'comment-main'); + + var metadataEnter = mainEnter + .append('div') + .attr('class', 'comment-metadata'); + + metadataEnter + .append('div') + .attr('class', 'comment-author') + .each(function(d) { + var selection = d3_select(this); + var osm = services.osm; + if (osm && d.user) { + selection = selection + .append('a') + .attr('class', 'comment-author-link') + .attr('href', osm.userURL(d.user)) + .attr('tabindex', -1) + .attr('target', '_blank'); + } + selection + .text(function(d) { return d.user || t('note.anonymous'); }); + }); + + metadataEnter + .append('div') + .attr('class', 'comment-date') + .text(function(d) { return d.action + ' ' + localeDateString(d.date); }); + + mainEnter + .append('div') + .attr('class', 'comment-text') + .html(function(d) { return d.html; }); + + comment + .call(replaceAvatars); + } + + + function replaceAvatars(selection) { + var osm = services.osm; + if (!osm) return; + + var uids = {}; // gather uids in the comment thread + _error.comment.forEach(function(d) { + if (d.uid) uids[d.uid] = true; + }); + + Object.keys(uids).forEach(function(uid) { + osm.loadUser(uid, function(err, user) { + if (!user || !user.image_url) return; + + selection.selectAll('.comment-avatar.user-' + uid) + .html('') + .append('img') + .attr('class', 'icon comment-avatar-icon') + .attr('src', user.image_url) + .attr('alt', user.display_name); + }); + }); + } + + + function localeDateString(s) { + if (!s) return null; + var detected = utilDetect(); + var options = { day: 'numeric', month: 'short', year: 'numeric' }; + s = s.replace(/-/g, '/'); // fix browser-specific Date() issues + var d = new Date(s); + if (isNaN(d.getTime())) return null; + return d.toLocaleDateString(detected.locale, options); + } + + + keepRightComment.error = function(_) { + if (!arguments.length) return _error; + _error = _; + return keepRightComment; + }; + + + return keepRightComment; +} diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js new file mode 100644 index 000000000..6058e98a6 --- /dev/null +++ b/modules/ui/keepRight_editor.js @@ -0,0 +1,331 @@ +import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { t } from '../util/locale'; +import { services } from '../services'; +import { modeBrowse } from '../modes'; +import { svgIcon } from '../svg'; + +// import { uiField } from './field'; +// import { uiFormFields } from './form_fields'; + +import { + uiKeepRightComment, + uiKeepRightHeader, + uiViewOnKeepRight, +} from './index'; + +import { + utilNoAuto, + utilRebind +} from '../util'; + + +export function uiKeepRightEditor(context) { + var dispatch = d3_dispatch('change'); + var keepRightComment = uiKeepRightComment(); + var keepRightHeader = uiKeepRightHeader(); + + var _error; + + + function keepRightEditor(selection) { + var header = selection.selectAll('.header') + .data([0]); + + var headerEnter = header.enter() + .append('div') + .attr('class', 'header fillL'); + + headerEnter + .append('button') + .attr('class', 'fr note-editor-close') + .on('click', function() { + context.enter(modeBrowse(context)); + }) + .call(svgIcon('#iD-icon-close')); + + headerEnter + .append('h3') + .text(t('keepRight.title')); + + + var body = selection.selectAll('.body') + .data([0]); + + body = body.enter() + .append('div') + .attr('class', 'body') + .merge(body); + + var editor = body.selectAll('.error-editor') + .data([0]); + + editor.enter() + .append('div') + .attr('class', 'modal-section note-editor') + .merge(editor) + .call(keepRightHeader.error(_error)) + // .call(keepRightComment.error(_error)) + .call(errorSaveSection); + + + var footer = selection.selectAll('.footer') + .data([0]); + + footer.enter() + .append('div') + .attr('class', 'footer') + .merge(footer) + .call(uiViewOnKeepRight(context).what(_error)); + } + + + function errorSaveSection(selection) { + var isSelected = (_error && _error.id === context.selectedNoteID()); + var errorSave = selection.selectAll('.error-save') + .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); + + // exit + errorSave.exit() + .remove(); + + // enter + var errorSaveEnter = errorSave.enter() + .append('div') + .attr('class', 'note-save save-section cf'); + + errorSaveEnter + .append('h4') + .attr('class', '.error-save-header') + .text(function() { + return t('note.newComment'); + }); + + errorSaveEnter + .append('textarea') + .attr('id', 'new-comment-input') + .attr('placeholder', t('note.inputPlaceholder')) + .attr('maxlength', 1000) + .property('value', function(d) { return d.newComment; }) + .call(utilNoAuto) + .on('input', changeInput) + .on('blur', changeInput); + + // update + errorSave = errorSaveEnter + .merge(errorSave) + .call(userDetails) + .call(errorSaveButtons); + + + function changeInput() { + var input = d3_select(this); + var val = input.property('value').trim() || undefined; + + // store the unsaved comment with the note itself + _error = _error.update({ newComment: val }); + + var osm = services.osm; + if (osm) { + osm.replaceNote(_error); // update note cache + } + + errorSave + .call(errorSaveButtons); + } + } + + + function userDetails(selection) { + var detailSection = selection.selectAll('.detail-section') + .data([0]); + + detailSection = detailSection.enter() + .append('div') + .attr('class', 'detail-section') + .merge(detailSection); + + var osm = services.osm; + if (!osm) return; + + // Add warning if user is not logged in + var hasAuth = osm.authenticated(); + var authWarning = detailSection.selectAll('.auth-warning') + .data(hasAuth ? [] : [0]); + + authWarning.exit() + .transition() + .duration(200) + .style('opacity', 0) + .remove(); + + var authEnter = authWarning.enter() + .insert('div', '.tag-reference-body') + .attr('class', 'field-warning auth-warning') + .style('opacity', 0); + + authEnter + .call(svgIcon('#iD-icon-alert', 'inline')); + + authEnter + .append('span') + .text(t('note.login')); + + authEnter + .append('a') + .attr('target', '_blank') + .call(svgIcon('#iD-icon-out-link', 'inline')) + .append('span') + .text(t('login')) + .on('click.error-login', function() { + d3_event.preventDefault(); + osm.authenticate(); + }); + + authEnter + .transition() + .duration(200) + .style('opacity', 1); + + + var prose = detailSection.selectAll('.error-save-prose') + .data(hasAuth ? [0] : []); + + prose.exit() + .remove(); + + prose = prose.enter() + .append('p') + .attr('class', 'note-save-prose') + .text(t('note.upload_explanation')) + .merge(prose); + + osm.userDetails(function(err, user) { + if (err) return; + + var userLink = d3_select(document.createElement('div')); + + if (user.image_url) { + userLink + .append('img') + .attr('src', user.image_url) + .attr('class', 'icon pre-text user-icon'); + } + + userLink + .append('a') + .attr('class', 'user-info') + .text(user.display_name) + .attr('href', osm.userURL(user.display_name)) + .attr('tabindex', -1) + .attr('target', '_blank'); + + prose + .html(t('note.upload_explanation_with_user', { user: userLink.html() })); + }); + } + + + function errorSaveButtons(selection) { + var osm = services.osm; + var hasAuth = osm && osm.authenticated(); + + var isSelected = (_error && _error.id === context.selectedNoteID()); + var buttonSection = selection.selectAll('.buttons') + .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); + + // exit + buttonSection.exit() + .remove(); + + // enter + var buttonEnter = buttonSection.enter() + .append('div') + .attr('class', 'buttons'); + + buttonEnter + .append('button') + .attr('class', 'button status-button action'); + + buttonEnter + .append('button') + .attr('class', 'button comment-button action') + .text(t('note.comment')); + + + // update + buttonSection = buttonSection + .merge(buttonEnter); + + buttonSection.select('.cancel-button') // select and propagate data + .on('click.cancel', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var osm = services.osm; + if (osm) { + osm.removeNote(d); + } + context.enter(modeBrowse(context)); + dispatch.call('change'); + }); + + buttonSection.select('.save-button') // select and propagate data + .attr('disabled', function(d) { + return (hasAuth && d.status === 'open' && d.newComment) ? null : true; + }) + .on('click.save', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var osm = services.osm; + if (osm) { + osm.postNoteCreate(d, function(err, note) { + dispatch.call('change', note); + }); + } + }); + + buttonSection.select('.status-button') // select and propagate data + .attr('disabled', (hasAuth ? null : true)) + .text(function(d) { + var action = (d.status === 'open' ? 'close' : 'open'); + var andComment = (d.newComment ? '_comment' : ''); + return t('note.' + action + andComment); + }) + .on('click.status', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var osm = services.osm; + if (osm) { + var setStatus = (d.status === 'open' ? 'closed' : 'open'); + osm.postNoteUpdate(d, setStatus, function(err, note) { + dispatch.call('change', note); + }); + } + }); + + buttonSection.select('.comment-button') // select and propagate data + .attr('disabled', function(d) { + return (hasAuth && d.status === 'open' && d.newComment) ? null : true; + }) + .on('click.comment', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var osm = services.osm; + if (osm) { + osm.postNoteUpdate(d, d.status, function(err, note) { + dispatch.call('change', note); + }); + } + }); + } + + + keepRightEditor.error = function(_) { + if (!arguments.length) return _error; + _error = _; + return keepRightEditor; + }; + + + return utilRebind(keepRightEditor, dispatch, 'on'); +} diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js new file mode 100644 index 000000000..6fbbfdd4a --- /dev/null +++ b/modules/ui/keepRight_header.js @@ -0,0 +1,50 @@ +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; + + +export function uiKeepRightHeader() { + var _error; + + + function keepRightHeader(selection) { + var header = selection.selectAll('.kr_error-header') + .data( + (_error ? [_error] : []), + function(d) { return d.status + d.id; } + ); + + header.exit() + .remove(); + + var headerEnter = header.enter() + .append('div') + .attr('class', 'kr_error-header'); + + var iconEnter = headerEnter + .append('div') + .attr('class', function(d) { return 'kr_error-header-icon ' + d.status; }) + .classed('new', function(d) { return d.id < 0; }); + + iconEnter + .append('div') + .attr('class', 'preset-icon-28') + .call(svgIcon('#iD-icon-note', 'note-fill')); // TODO: change classes + + headerEnter + .append('div') + .attr('class', 'kr_error-header-label') + .text(function(d) { + return t('keepRight.keepRight') + ' ' + d.object_type + ' ' + ' ' + d.error_id; + }); + } + + + keepRightHeader.error = function(_) { + if (!arguments.length) return _error; + _error = _; + return keepRightHeader; + }; + + + return keepRightHeader; +} diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index ddc92bd8d..c0b6a9a98 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -9,18 +9,8 @@ import { selectAll as d3_selectAll } from 'd3-selection'; -import { - osmEntity, - osmNote -} from '../osm'; - -import { - uiDataEditor, - uiFeatureList, - uiInspector, - uiNoteEditor -} from './index'; - +import { osmEntity, osmNote, krError } from '../osm'; +import { uiDataEditor, uiFeatureList, uiInspector, uiNoteEditor, uiKeepRightEditor } from './index'; import { textDirection } from '../util/locale'; @@ -28,9 +18,11 @@ export function uiSidebar(context) { var inspector = uiInspector(context); var dataEditor = uiDataEditor(context); var noteEditor = uiNoteEditor(context); + var keepRightEditor = uiKeepRightEditor(context); var _current; var _wasData = false; var _wasNote = false; + var _was_krError = false; function sidebar(selection) { @@ -133,6 +125,18 @@ export function uiSidebar(context) { selection.selectAll('.sidebar-component') .classed('inspector-hover', true); + } else if (what instanceof krError) { + _was_krError = true; + var kr_errors = d3_selectAll('.kr_error'); + kr_errors + .classed('hover', function(d) { return d === what; }); + + sidebar + .show(keepRightEditor.error(what)); + + selection.selectAll('.sidebar-component') + .classed('inspector-hover', true); + } else if (!_current && (datum instanceof osmEntity)) { featureListWrap .classed('inspector-hidden', true); @@ -163,6 +167,10 @@ export function uiSidebar(context) { _wasData = false; d3_selectAll('.note').classed('hover', false); sidebar.hide(); + } else if (_was_krError) { + d3_selectAll('.kr_error') + .classed('hover', false); + sidebar.hide(); } } diff --git a/modules/ui/view_on_keepRight.js b/modules/ui/view_on_keepRight.js new file mode 100644 index 000000000..0574a3e57 --- /dev/null +++ b/modules/ui/view_on_keepRight.js @@ -0,0 +1,45 @@ +import { t } from '../util/locale'; +import { svgIcon } from '../svg'; +import { krError } from '../osm'; + + +export function uiViewOnKeepRight(context) { + var _error; // a keepright error + + + function viewOnKeepRight(selection) { + var url; + if (_error instanceof krError) { + url = context.connection().keepRightURL(_error); + } + + var data = ((!_error) ? [] : [_error]); + var link = selection.selectAll('.view-on-keepRight') + .data(data, function(d) { return d.id; }); + + // exit + link.exit() + .remove(); + + // enter + var linkEnter = link.enter() + .append('a') + .attr('class', 'view-on-keepRight') + .attr('target', '_blank') + .attr('href', url) + .call(svgIcon('#iD-icon-out-link', 'inline')); + + linkEnter + .append('span') + .text(t('inspector.view_on_keepRight')); + } + + + viewOnKeepRight.what = function(_) { + if (!arguments.length) return _error; + _error = _; + return viewOnKeepRight; + }; + + return viewOnKeepRight; +} From 18278371aadb185b53f981529f3010fd7ee63928 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Tue, 31 Jul 2018 00:03:08 -0400 Subject: [PATCH 006/114] WIP: identifying variables in errors --- css/65_data.css | 1 + css/80_app.css | 2 + data/core.yaml | 258 ++++++++++++ dist/locales/en.json | 344 ++++++++++++++- modules/osm/keepRight.js | 482 +--------------------- modules/services/keepRight.js | 38 ++ modules/ui/index.js | 1 + modules/ui/keepRight_details.js | 41 ++ modules/ui/keepRight_editor.js | 80 ++-- modules/ui/note_editor.js | 2 +- modules/util/index.js | 1 + modules/util/keepRight/errorSchema.json | 329 +++++++++++++++ modules/util/keepRight/index.js | 2 + modules/util/keepRight/keepRight_error.js | 95 +++++ 14 files changed, 1158 insertions(+), 518 deletions(-) create mode 100644 modules/ui/keepRight_details.js create mode 100644 modules/util/keepRight/errorSchema.json create mode 100644 modules/util/keepRight/index.js create mode 100644 modules/util/keepRight/keepRight_error.js diff --git a/css/65_data.css b/css/65_data.css index d15ece1bd..b3c85e284 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -171,3 +171,4 @@ [dir='rtl'] .kr_error-header-label { border-radius: 5px 0 0 5px; } + diff --git a/css/80_app.css b/css/80_app.css index 296359eb2..c333cbd23 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -586,6 +586,7 @@ button.add-note svg.icon { .field-help-title button.close, .sidebar-component .header button.data-editor-close, .sidebar-component .header button.note-editor-close, +.sidebar-component .header button.keepRight-editor-close, .entity-editor-pane .header button.preset-close, .preset-list-pane .header button.preset-choose { position: absolute; @@ -595,6 +596,7 @@ button.add-note svg.icon { [dir='rtl'] .field-help-title button.close, [dir='rtl'] .sidebar-component .header button.data-editor-close, [dir='rtl'] .sidebar-component .header button.note-editor-close, +[dir='rtl'] .sidebar-component .header button.keepRight-editor-close, [dir='rtl'] .entity-editor-pane .header button.preset-close, [dir='rtl'] .preset-list-pane .header button.preset-choose { left: 0; diff --git a/data/core.yaml b/data/core.yaml index 6c7176e1f..838205268 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -648,6 +648,264 @@ en: keepRight: Error - tooltip: Q/A data from keepright.at title: Edit Error + inputPlaceholder: Enter a comment to share with other users. + newComment: New Comment + upload_explanation: Your comments will be publicly visible to all keepRight.at users. + upload_explanation_with_user: "Your comments as {user} will be publicly visible to all OpenStreetMap users." + close_comment: Close and Comment + open_comment: Reopen and Comment + close: Close Note + open: Reopen Note + entities: + node: node + way: way + relation: relation + highway: highway + cycleway: cycleway + waterway: waterway + types: + errors: + _30: + title: 'non-closed_areas' + description: 'This way is tagged with {var1}={var2} and should be closed-loop' + _40: + title: 'dead-ended one-ways' + description: 'The first node (id {var1}) of this one-way is not connected to any other way' + _41: + title: '' + description: 'The last node (id {var1}) of this one-way is not connected to any other way' + _42: + title: '' + description: 'This node cannot be reached because one-ways only lead away from here' + _43: + title: '' + description: 'You cannot escape from this node because one-ways only lead to here' + _50: + title: 'almost-junctions' + description: 'This node is very close but not connected to way #{var1}' + _70: + title: 'missing tags' + description: 'This {var1} has an empty tag: {var2}' + _71: + title: '' + description: 'This way has no tags' + _72: + title: '' + description: 'This node is not member of any way and doesn''t have any tags' + _90: + title: 'motorways without ref' + description: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' + _100: + title: 'places of worship without religion' + description: 'This {var1} is tagged as place of worship and therefore needs a religion tag' + _110: + title: 'point of interest without name' + description: 'This node is tagged as {var1} and therefore needs a name tag' + _120: + title: 'ways without nodes' + description: 'This way has just one single node' + _130: + title: 'floating islands' + description: 'This way is not connected to the rest of the map' + _150: + title: 'railway crossing without tag' + description: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + _160: + title: 'wrongly used railway tag' + description: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + _170: + title: 'FIXME tagged items' + description: '{var1}' + _180: + title: 'relations without type' + description: 'This relation has no type tag which is mandatory for relations' + _190: + title: 'intersections without junctions' + description: 'Finds way crossings on same layer without common node as a junction' + _191: + title: 'highway-highway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _192: + title: 'highway-waterway' + description: 'This {var1} intersects the {var2} #{var3}' + _193: + title: 'highway-riverbank' + description: 'This {var1} intersects the {var2} #{var3}' + _194: + title: 'waterway-waterway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _195: + title: 'cycleway-cycleway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _196: + title: 'highway-cycleway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _197: + title: 'cycleway-waterway' + description: 'This {var1} intersects the {var2} #{var3}' + _198: + title: 'cycleway-riverbank' + description: 'This {var1} intersects the {var2} #{var3}' + _200: + title: 'overlapping ways' + description: 'Finds overlapping ways on same layer' + _201: + title: 'highway-highway' + description: 'This {var1} overlaps the {var2} #{var3}' + _202: + title: 'highway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}' + _203: + title: 'highway-riverbank' + description: 'This {var1} overlaps the {var2} #{var3}' + _204: + title: 'waterway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}' + _205: + title: 'cycleway-cycleway' + description: 'This {var1} overlaps the {var2} #{var3}' + _206: + title: 'highway-cycleway' + description: 'This {var1} overlaps the {var2} #{var3}' + _207: + title: 'cycleway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}' + _208: + title: 'cycleway-riverbank' + description: 'This {var1} overlaps the {var2} #{var3}' + _210: + title: 'loopings' + description: 'These errors contain self intersecting ways' + _211: + title: '' + description: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' + _212: + title: '' + description: 'This way has only two different nodes and contains one of them more than once' + _220: + title: 'misspelled tags' + description: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' + _221: + title: '' + description: 'The key of this {var1} tag is key {var2}' + _230: + title: 'layer conflicts' + description: '' + _231: + title: 'mixed layers intersection' + description: 'This node is a junction of ways on different layers: {var1}' + _232: + title: 'strange layers' + description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' + _270: + title: 'motorways connected directly' + description: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' + _280: + title: 'boundaries' + description: '' + _281: + title: 'missing name' + description: 'This boundary has no name' + _282: + title: 'missing admin level' + description: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' + _283: + title: 'no closed loop' + description: 'The boundary of {var1} is not closed-loop' + _284: + title: 'splitting boundary' + description: 'The boundary of {var1} splits here' + _285: + title: 'admin_level too high' + description: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + _290: + title: 'restrictions' + description: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' + _291: + title: 'missing type' + description: 'This turn-restriction has no known restriction type' + _292: + title: 'missing from way' + description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _293: + title: 'missing to way' + description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _294: + title: 'from or to not a way' + description: 'From- and To-members of turn restrictions need to be ways. {var1}' + _295: + title: 'via is not on the way ends' + description: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' + _296: + title: 'wrong restriction angle' + description: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' + _297: + title: 'wrong direction of to member' + description: 'wrong direction of to way {var1}' + _298: + title: 'already restricted by oneway' + description: 'entry already prohibited by oneway tag on {var1}' + _310: + title: 'roundabouts' + description: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' + _311: + title: 'not closed loop' + description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + _312: + title: 'wrong direction' + description: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + _313: + title: 'faintly connected' + description: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' + _320: + title: '*_link connections' + description: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' + _350: + title: 'bridge-tags' + description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' + _370: + title: 'doubled places' + description: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' + _380: + title: 'non-physical use of sport-tag' + description: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' + _400: + title: 'geometry glitches' + description: '' + _401: + title: 'missing turn restriction' + description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' + _402: + title: 'impossible angles' + description: 'this way bends in a very sharp angle here' + _410: + title: 'website' + description: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' + _411: + title: 'http error' + description: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' + _412: + title: 'domain hijacking' + description: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' + _413: + title: 'non-match' + description: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' + warnings: + _20: + title: 'multiple nodes on the same spot' + description: 'There is more than one node in this spot. Offending node IDs: {var1}' + _60: + title: 'depreciated tags' + description: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' + _300: + title: 'missing maxspeed' + description: 'missing maxspeed tag' + _360: + title: 'language unknown' + description: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' + _390: + title: 'missing tracktype' + description: This track doesn't have a tracktype streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index 47a566277..aeb19ddc9 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -786,7 +786,349 @@ "keepRight": { "keepRight": "Error -", "tooltip": "Q/A data from keepright.at", - "title": "Edit Error" + "title": "Edit Error", + "inputPlaceholder": "Enter a comment to share with other users.", + "newComment": "New Comment", + "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", + "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all OpenStreetMap users.", + "close_comment": "Close and Comment", + "open_comment": "Reopen and Comment", + "close": "Close Note", + "open": "Reopen Note", + "entities": { + "node": "node", + "way": "way", + "relation": "relation", + "highway": "highway", + "cycleway": "cycleway", + "waterway": "waterway" + }, + "types": { + "errors": { + "_30": { + "title": "non-closed_areas", + "description": "This way is tagged with {var1}={var2} and should be closed-loop" + }, + "_40": { + "title": "dead-ended one-ways", + "description": "The first node (id {var1}) of this one-way is not connected to any other way", + "_41": { + "title": "", + "description": "The last node (id {var1}) of this one-way is not connected to any other way" + }, + "_42": { + "title": "", + "description": "This node cannot be reached because one-ways only lead away from here" + }, + "_43": { + "title": "", + "description": "You cannot escape from this node because one-ways only lead to here" + } + }, + "_50": { + "title": "almost-junctions", + "description": "This node is very close but not connected to way #{var1}" + }, + "_70": { + "title": "missing tags", + "description": "This {var1} has an empty tag: {var2}", + "_71": { + "title": "", + "description": "This way has no tags" + }, + "_72": { + "title": "", + "description": "This node is not member of any way and doesn't have any tags" + } + }, + "_90": { + "title": "motorways without ref", + "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + }, + "_100": { + "title": "places of worship without religion", + "description": "This {var1} is tagged as place of worship and therefore needs a religion tag" + }, + "_110": { + "title": "point of interest without name", + "description": "This node is tagged as {var1} and therefore needs a name tag" + }, + "_120": { + "title": "ways without nodes", + "description": "This way has just one single node" + }, + "_130": { + "title": "floating islands", + "description": "This way is not connected to the rest of the map" + }, + "_150": { + "title": "railway crossing without tag", + "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + }, + "_160": { + "title": "wrongly used railway tag", + "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + }, + "_170": { + "title": "FIXME tagged items", + "description": "{var1}" + }, + "_180": { + "title": "relations without type", + "description": "This relation has no type tag which is mandatory for relations" + }, + "_190": { + "title": "intersections without junctions", + "description": "Finds way crossings on same layer without common node as a junction", + "_191": { + "title": "highway-highway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_192": { + "title": "highway-waterway", + "description": "This {var1} intersects the {var2} #{var3}" + }, + "_193": { + "title": "highway-riverbank", + "description": "This {var1} intersects the {var2} #{var3}" + }, + "_194": { + "title": "waterway-waterway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_195": { + "title": "cycleway-cycleway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_196": { + "title": "highway-cycleway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_197": { + "title": "cycleway-waterway", + "description": "This {var1} intersects the {var2} #{var3}" + }, + "_198": { + "title": "cycleway-riverbank", + "description": "This {var1} intersects the {var2} #{var3}" + } + }, + "_200": { + "title": "overlapping ways", + "description": "Finds overlapping ways on same layer", + "_201": { + "title": "highway-highway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_202": { + "title": "highway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_203": { + "title": "highway-riverbank", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_204": { + "title": "waterway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_205": { + "title": "cycleway-cycleway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_206": { + "title": "highway-cycleway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_207": { + "title": "cycleway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}" + }, + "_208": { + "title": "cycleway-riverbank", + "description": "This {var1} overlaps the {var2} #{var3}" + } + }, + "_210": { + "title": "loopings", + "description": "These errors contain self intersecting ways", + "_211": { + "title": "", + "description": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" + }, + "_212": { + "title": "", + "description": "This way has only two different nodes and contains one of them more than once" + } + }, + "_220": { + "title": "misspelled tags", + "description": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}", + "_221": { + "title": "", + "description": "The key of this {var1} tag is key {var2}" + }, + "_230": { + "title": "layer conflicts", + "description": "" + }, + "_231": { + "title": "mixed layers intersection", + "description": "This node is a junction of ways on different layers: {var1}" + }, + "_232": { + "title": "strange layers", + "description": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" + } + }, + "_270": { + "title": "motorways connected directly", + "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + }, + "_280": { + "title": "boundaries", + "description": "", + "_281": { + "title": "missing name", + "description": "This boundary has no name" + }, + "_282": { + "title": "missing admin level", + "description": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + }, + "_283": { + "title": "no closed loop", + "description": "The boundary of {var1} is not closed-loop" + }, + "_284": { + "title": "splitting boundary", + "description": "The boundary of {var1} splits here" + }, + "_285": { + "title": "admin_level too high", + "description": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + } + }, + "_290": { + "title": "restrictions", + "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat", + "_291": { + "title": "missing type", + "description": "This turn-restriction has no known restriction type" + }, + "_292": { + "title": "missing from way", + "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_293": { + "title": "missing to way", + "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_294": { + "title": "from or to not a way", + "description": "From- and To-members of turn restrictions need to be ways. {var1}" + }, + "_295": { + "title": "via is not on the way ends", + "description": "via (node #{var1}) is not the first or the last member of from (way #{var2})" + }, + "_296": { + "title": "wrong restriction angle", + "description": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" + }, + "_297": { + "title": "wrong direction of to member", + "description": "wrong direction of to way {var1}" + }, + "_298": { + "title": "already restricted by oneway", + "description": "entry already prohibited by oneway tag on {var1}" + } + }, + "_310": { + "title": "roundabouts", + "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1", + "_311": { + "title": "not closed loop", + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + }, + "_312": { + "title": "wrong direction", + "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + }, + "_313": { + "title": "faintly connected", + "description": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" + } + }, + "_320": { + "title": "*_link connections", + "description": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" + }, + "_350": { + "title": "bridge-tags", + "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" + }, + "_370": { + "title": "doubled places", + "description": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" + }, + "_380": { + "title": "non-physical use of sport-tag", + "description": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" + }, + "_400": { + "title": "geometry glitches", + "description": "", + "_401": { + "title": "missing turn restriction", + "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" + }, + "_402": { + "title": "impossible angles", + "description": "this way bends in a very sharp angle here" + } + }, + "_410": { + "title": "website", + "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*", + "_411": { + "title": "http error", + "description": "The URL ({var1}) cannot be opened (HTTP status code {var2})" + }, + "_412": { + "title": "domain hijacking", + "description": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" + }, + "_413": { + "title": "non-match", + "description": "Content of the URL ({var1}) did not contain these keywords: ({var2})" + } + } + }, + "warnings": { + "_20": { + "title": "multiple nodes on the same spot", + "description": "There is more than one node in this spot. Offending node IDs: {var1}" + }, + "_60": { + "title": "depreciated tags", + "description": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" + }, + "_300": { + "title": "missing maxspeed", + "description": "missing maxspeed tag" + }, + "_360": { + "title": "language unknown", + "description": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + }, + "_390": { + "title": "missing tracktype", + "description": "This track doesn't have a tracktype" + } + } + } }, "streetside": { "tooltip": "Streetside photos from Microsoft", diff --git a/modules/osm/keepRight.js b/modules/osm/keepRight.js index 76710deba..1ea2d4b25 100644 --- a/modules/osm/keepRight.js +++ b/modules/osm/keepRight.js @@ -41,481 +41,9 @@ _extend(krError.prototype, { } return this; + }, + + update: function(attrs) { + return krError(this, attrs); // {v: 1 + (this.v || 0)} } -}); - -var keepRightSchema = { - 'schema': '', - 'id': 0, - 'error_type': 0, - 'error_name': 0, - 'object_type': [ - 'node', - 'way', - 'relation' - ], - 'object_id': 0, - 'state': [ - 'new', - 'reopened', - 'ignore_temporarily', - 'ignore' - ], - 'first_occurrence': new Date(), - 'last_checked': new Date(), - 'object_timestamp': new Date(), - 'user_name': '', - 'lat': 0, - 'lon': 0, - 'comment': '', - 'comment_timestamp': new Date(), - 'msgid': '', - 'txt1': '', - 'txt2': '', - 'txt3': '', - 'txt4': '', - 'txt5': '' - }; - -var errorSchema = { - errors: { - 0: { - errorType: 0, - errorName: '', - message: '', - subTypes: {} - }, - 30: { - errorType: 30, - errorName: 'non_closed_areas', - message: 'This way is tagged with \'$1=$2\' and should be closed-loop', - subTypes: {} - }, - 40: { - errorType: 40, - errorName: 'dead ended oneways', - message: 'The first node (id $1) of this one-way is not connected to any other way', - subTypes: { - 41: { - errorType: 41, - errorName: '', - message: 'The last node (id $1) of this one-way is not connected to any other way' - }, - 42: { - errorType: 42, - errorName: '', - message: 'This node cannot be reached, because one-ways only lead away from here' - }, - 43: { - errorType: 43, - errorName: '', - message: 'You cannot escape from this node, because one-ways only lead to here' - }, - } - }, - 50: { - errorType: 50, - errorName: 'almost junctions', - message: 'This node is very close but not connected to way #$1', - subTypes: {} - }, - 60: { - errorType: 60, - errorName: 'depreciated tags', - message: 'This $1 uses deprecated tag $2 = $3. Please use $4 instead!', - subTypes: {} - }, - 70: { - errorType: 70, - errorName: 'missing tags', - message: 'This $1 has an empty tag: $2', - 71: { - errorType: 71, - errorName: '', - message: 'This way has no tags' - }, - 72: { - errorType: 72, - errorName: '', - message: 'This node is not member of any way and does not have any tags' - } - }, - 90: { - errorType: 90, - errorName: 'motorways without ref', - message: 'This way is tagged as motorway and therefore needs a ref, nat_ref or int_ref tag' - }, - 100: { - errorType: 100, - errorName: 'places of worship without religion', - message: 'This $1 is tagged as place of worship and therefore needs a religion tag' - }, - 110: { - errorType: 110, - errorName: 'point of interest without name', - message: 'This node is tagged as $1 and therefore needs a name tag' - }, - 120: { - errorType: 120, - errorName: 'ways without nodes', - message: 'This way has just one single node' - }, - 130: { - errorType: 130, - errorName: 'floating islands', - message: 'This way is not connected to the rest of the map' - }, - 150: { - errorType: 150, - errorName: 'railway crossing without tag', - message: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' - }, - 160: { - errorType: 160, - errorName: 'wrongly used railway tag', - message: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' - }, - 170: { - errorType: 0, - errorName: 'FIXME tagged items', - message: '$1' - }, - 180: { - errorType: 180, - errorName: 'relations without type', - message: 'This relation has no type tag, which is mandatory for relations' - }, - 190: { - errorType: 190, - errorName: 'intersections without junctions', - message: 'Finds way crossings on same layer without common node as a junction', - subtypes: { - 191: { - errorType: 191, - errorName: 'highway-highway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 192: { - errorType: 192, - errorName: 'highway-waterway', - message: 'This $1 intersects the $2 #$3' - }, - 193: { - errorType: 193, - errorName: 'highway-riverbank', - message: 'This $1 intersects the $2 #$3' - }, - 194: { - errorType: 194, - errorName: 'waterway-waterway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 195: { - errorType: 195, - errorName: 'cycleway-cycleway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 196: { - errorType: 196, - errorName: 'highway-cycleway', - message: 'This $1 intersects the $2 #$3 but there is no junction node' - }, - 197: { - errorType: 197, - errorName: 'cycleway-waterway', - message: 'This $1 intersects the $2 #$3' - }, - 198: { - errorType: 198, - errorName: 'cycleway-riverbank', - message: 'This $1 intersects the $2 #$3' - } - } - }, - 200: { - errorType: 200, - errorName: 'intersections without junctions', - message: 'Finds overlapping ways on same layer.', - subtypes: { - 201: { - errorType: 201, - errorName: 'highway-highway', - message: 'This $1 overlaps the $2 #$3' - }, - 202: { - errorType: 202, - errorName: 'highway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 203: { - errorType: 203, - errorName: 'highway-riverbank', - message: 'This $1 overlaps the $2 #$3' - }, - 204: { - errorType: 204, - errorName: 'waterway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 205: { - errorType: 205, - errorName: 'cycleway-cycleway', - message: 'This $1 overlaps the $2 #$3' - }, - 206: { - errorType: 206, - errorName: 'highway-cycleway', - message: 'This $1 overlaps the $2 #$3' - }, - 207: { - errorType: 207, - errorName: 'cycleway-waterway', - message: 'This $1 overlaps the $2 #$3' - }, - 208: { - errorType: 208, - errorName: 'cycleway-riverbank', - message: 'This $1 overlaps the $2 #$3' - } - } - }, - 210: { - errorType: 210, - errorName: 'loopings', - message: 'These errors contain self intersecting ways', - subTypes: { - 211: { - errorType: 211, - errorName: '', - message: 'This way contains more than one node at least twice. Nodes are $1. This may or may not be an error' - }, - 212: { - errorType: 212, - errorName: '', - message: 'This way has only two different nodes and contains one of them more than once' - }, - } - }, - 220: { - errorType: 220, - errorName: 'misspelled tags', - message: ' This $1 is tagged \'$2=$3\' where $4 looks like $5', - subTypes: { - 221: { - errorType: 221, - errorName: 'misspelled tags', - message: 'The key of this $1\'s tag is \'key\': $2' - } - } - }, - 230: { - errorType: 230, - errorName: 'layer conflicts', - message: '', - subTypes: { - 231: { - errorType: 231, - errorName: 'mixed layers intersection', - message: 'This node is a junction of ways on different layers: $1' - }, - 232: { - errorType: 232, - errorName: 'strange layers', - message: 'This $1 is tagged with layer $2. This need not be an error, but it looks strange' - } - } - }, - 270: { - errorType: 270, - errorName: 'motorways connected directly', - message: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or if it is a service=parking_aisle.' - }, - 280: { - errorType: 280, - errorName: 'boundaries', - message: '', - subTypes: { - 281: { - errorType: 281, - errorName: 'missing name', - message: 'This boundary has no name' - }, - 282: { - errorType: 282, - errorName: 'missing admin level', - message: 'The boundary of $1 has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' - }, - 283: { - errorType: 283, - errorName: 'no closed loop', - message: 'The boundary of $1 is not closed-loop' - }, - 284: { - errorType: 284, - errorName: 'splitting boundary', - message: 'The boundary of $1 splits here' - }, - 285: { - errorType: 285, - errorName: 'admin_level too high', - message: 'This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' - }, - } - }, - 290: { - errorType: 290, - errorName: 'faulty restrictions', - message: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv, type=restriction:caravan, type=restriction:motorcar, type=restriction:bus, type=restriction:agricultural, type=restriction:motorcycle, type=restriction:bicycle and type=restriction:hazmat.', - subTypes: { - 291: { - errorType: 291, - errorName: 'missing type', - message: 'This turn-restriction has no known restriction type' - }, - 292: { - errorType: 292, - errorName: 'missing from way', - message: 'A turn-restriction needs exactly one $1 member. This one has $2' - }, - 293: { - errorType: 293, - errorName: 'missing to way', - message: 'A turn-restriction needs exactly one $1 member. This one has $2' - }, - 294: { - errorType: 294, - errorName: 'from or to not a way', - message: 'From- and To-members of turn restrictions need to be ways. $1' - }, - 295: { - errorType: 295, - errorName: 'via is not on the way ends', - message: 'via (node #$1) is not the first or the last member of from (way #$2)' - }, - 296: { - errorType: 296, - errorName: 'wrong restriction angle', - message: 'restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?' - }, - 297: { - errorType: 297, - errorName: 'wrong direction of to member', - message: 'wrong direction of to way $1' - }, - 298: { - errorType: 298, - errorName: 'already restricted by oneway', - message: 'entry already prohibited by oneway tag on $1' - }, - } - }, - 310: { - errorType: 310, - errorName: 'roundabouts', - message: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.', - subTypes: { - 311: { - errorType: 311, - errorName: 'not closed loop', - message: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' - }, - 312: { - errorType: 312, - errorName: 'wrong direction', - message: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' - }, - 313: { - errorType: 313, - errorName: 'faintly connected', - message: 'This roundabout has only $1 other roads connected. Roundabouts typically have three.' - }, - } - }, - 320: { - errorType: 320, - errorName: '*link connections', - message: 'This way is tagged as highway=$1_link but doesn\'t have a connection to any other $1 or $1_link' - }, - 350: { - errorType: 350, - errorName: 'bridge tags', - message: 'This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: $1' - }, - 370: { - errorType: 370, - errorName: 'doubled places', - message: 'This node has tags in common with the surrounding way #$1 and seems to be redundand | This node has tags in common with the surrounding way #$1 (including the name \'$2\') and seems to be redundand' - }, - 380: { - errorType: 380, - errorName: 'non-physical use of sportage', - message: 'This way is tagged $1 but has no physical tag like e.g. leisure, building, amenity or highway' - }, - 400: { - errorType: 400, - errorName: 'geometry glitches', - message: '', - subTypes: { - 401: { - errorType: 401, - errorName: 'missing turn restrictions', - message: 'ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning' - }, - 402: { - errorType: 402, - errorName: 'impossible angles', - message: 'this way bends in a very sharp angle here' - }, - } - }, - 410: { - errorType: 410, - errorName: 'websites', - message: 'Web pages are analyzed. Web page is defined by any of the following tags website=*, url=*, website:mobile=*, contact:website=*, contact:url=*, image=*, source:website=* or source:url=*.', - subTypes: { - 411: { - errorType: 411, - errorName: 'http error', - message: 'The URL ($1) cannot be opened (HTTP status code $2)' - }, - 412: { - errorType: 412, - errorName: 'domain hijacking', - message: 'Possible domain squatting: $1. Suspicious text is: "$2"' - }, - 413: { - errorType: 413, - errorName: 'non-match', - message: 'Content of the URL ($1) did not contain these keywords: ($2)' - }, - } - } - }, - warnings: { - 20: { - errorType: 20, - errorName: 'multiple nodes on the same spot', - message: ' There is more than one node in this spot. Offending node IDs: $1' - }, - 60: { - errorType: 60, - errorName: '', - message: '' - }, - 300: { - errorType: 300, - errorName: 'missing maxspeed', - message: 'missing maxspeed tag' - }, - 360: { - errorType: 360, - errorName: 'language unknown', - message: 'It would be nice if this $1 had an additional tag \'name:XX=$2\' where XX shows the language of its name \'$2\'.' - }, - 390: { - errorType: 390, - errorName: 'missing tracktype', - message: 'This track doesn\'t have a tracktype' - }, - }, -}; \ No newline at end of file +}); \ No newline at end of file diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index f49b48a8a..7bc6ddc2f 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -47,6 +47,27 @@ function abortUnwantedRequests(cache, tiles) { } +function encodeErrorRtree(error) { + return { + minX: error.loc[0], + minY: error.loc[1], + maxX: error.loc[0], + maxY: error.loc[1], + data: error + }; +} + + +// replace or remove error from rtree +function updateRtree(item, replace) { + _keepRightCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); + + if (replace) { + _keepRightCache.rtree.insert(item); + } +} + + export default { init: function() { if (!_keepRightCache) { @@ -180,4 +201,21 @@ export default { getError: function(id) { return _keepRightCache.keepRight[id]; }, + + // replace a single note in the cache + replaceError: function(error) { + if (!(error instanceof krError) || !error.id) return; + + _keepRightCache.note[error.id] = error; + updateRtree(encodeErrorRtree(error), true); // true = replace + return error; + }, + + // remove a single note from the cache + removeError: function(error) { + if (!(error instanceof krError) || !error.id) return; + + delete _keepRightCache.note[error.id]; + updateRtree(encodeErrorRtree(error), false); // false = remove + }, }; \ No newline at end of file diff --git a/modules/ui/index.js b/modules/ui/index.js index f59aa1657..d80ba9117 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -31,6 +31,7 @@ export { uiHelp } from './help'; export { uiInfo } from './info'; export { uiInspector } from './inspector'; export { uiKeepRightComment } from './keepRight_comment'; +export { uiKeepRightDetails } from './keepRight_details'; export { uiKeepRightEditor } from './keepRight_editor'; export { uiKeepRightHeader } from './keepRight_header'; export { uiLasso } from './lasso'; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js new file mode 100644 index 000000000..461da3f19 --- /dev/null +++ b/modules/ui/keepRight_details.js @@ -0,0 +1,41 @@ +import { t } from '../util/locale'; +import { utilGetErrorDetails } from '../util'; + + +export function uiKeepRightDetails() { + var _error; + + + function keepRightDetails(selection) { + var details = selection.selectAll('.kr_error-details') + .data( + (_error ? [_error] : []), + function(d) { return d.status + d.id; } + ); + + details.exit() + .remove(); + + var detailsEnter = details.enter() + .append('div') + .attr('class', 'kr_error-details'); + + detailsEnter + .append('div') + .attr('class', 'kr_error-details-label') + .text(function(d) { + var error = utilGetErrorDetails(d); + return t('keepRight.keepRight'); // TODO: add details here + }); + } + + + keepRightDetails.error = function(_) { + if (!arguments.length) return _error; + _error = _; + return keepRightDetails; + }; + + + return keepRightDetails; +} diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 6058e98a6..d7054f0a6 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -9,11 +9,9 @@ import { services } from '../services'; import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; -// import { uiField } from './field'; -// import { uiFormFields } from './form_fields'; - import { uiKeepRightComment, + uiKeepRightDetails, uiKeepRightHeader, uiViewOnKeepRight, } from './index'; @@ -27,6 +25,7 @@ import { export function uiKeepRightEditor(context) { var dispatch = d3_dispatch('change'); var keepRightComment = uiKeepRightComment(); + var keepRightDetails = uiKeepRightDetails(); var keepRightHeader = uiKeepRightHeader(); var _error; @@ -42,7 +41,7 @@ export function uiKeepRightEditor(context) { headerEnter .append('button') - .attr('class', 'fr note-editor-close') + .attr('class', 'fr keepRight-editor-close') .on('click', function() { context.enter(modeBrowse(context)); }) @@ -66,10 +65,11 @@ export function uiKeepRightEditor(context) { editor.enter() .append('div') - .attr('class', 'modal-section note-editor') + .attr('class', 'modal-section keepRight-editor') .merge(editor) .call(keepRightHeader.error(_error)) // .call(keepRightComment.error(_error)) + .call(keepRightDetails.error(_error)) .call(errorSaveSection); @@ -85,7 +85,7 @@ export function uiKeepRightEditor(context) { function errorSaveSection(selection) { - var isSelected = (_error && _error.id === context.selectedNoteID()); + var isSelected = (_error && _error.id === context.selectedErrorID()); var errorSave = selection.selectAll('.error-save') .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); @@ -96,19 +96,19 @@ export function uiKeepRightEditor(context) { // enter var errorSaveEnter = errorSave.enter() .append('div') - .attr('class', 'note-save save-section cf'); + .attr('class', 'keepRight-save save-section cf'); errorSaveEnter .append('h4') .attr('class', '.error-save-header') .text(function() { - return t('note.newComment'); + return t('keepRight.newComment'); }); errorSaveEnter .append('textarea') - .attr('id', 'new-comment-input') - .attr('placeholder', t('note.inputPlaceholder')) + .attr('class', 'new-comment-input') + .attr('placeholder', t('keepRight.inputPlaceholder')) .attr('maxlength', 1000) .property('value', function(d) { return d.newComment; }) .call(utilNoAuto) @@ -126,12 +126,12 @@ export function uiKeepRightEditor(context) { var input = d3_select(this); var val = input.property('value').trim() || undefined; - // store the unsaved comment with the note itself + // store the unsaved comment with the error itself _error = _error.update({ newComment: val }); - var osm = services.osm; - if (osm) { - osm.replaceNote(_error); // update note cache + var keepRight = services.keepRight; + if (keepRight) { + keepRight.replaceError(_error); // update keepright cache } errorSave @@ -200,8 +200,8 @@ export function uiKeepRightEditor(context) { prose = prose.enter() .append('p') - .attr('class', 'note-save-prose') - .text(t('note.upload_explanation')) + .attr('class', 'error-save-prose') + .text(t('keepRight.upload_explanation')) .merge(prose); osm.userDetails(function(err, user) { @@ -225,7 +225,7 @@ export function uiKeepRightEditor(context) { .attr('target', '_blank'); prose - .html(t('note.upload_explanation_with_user', { user: userLink.html() })); + .html(t('keepRight.upload_explanation_with_user', { user: userLink.html() })); }); } @@ -234,7 +234,7 @@ export function uiKeepRightEditor(context) { var osm = services.osm; var hasAuth = osm && osm.authenticated(); - var isSelected = (_error && _error.id === context.selectedNoteID()); + var isSelected = (_error && _error.id === context.selectedErrorID()); var buttonSection = selection.selectAll('.buttons') .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); @@ -264,9 +264,9 @@ export function uiKeepRightEditor(context) { buttonSection.select('.cancel-button') // select and propagate data .on('click.cancel', function(d) { this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.removeNote(d); + var keepRight = services.keepRight; + if (keepRight) { + keepRight.removeError(d); } context.enter(modeBrowse(context)); dispatch.call('change'); @@ -278,29 +278,30 @@ export function uiKeepRightEditor(context) { }) .on('click.save', function(d) { this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.postNoteCreate(d, function(err, note) { - dispatch.call('change', note); - }); + var keepRight = services.keepRight; + if (keepRight) { + // TODO: handle posting updates + // keepRight.postKeepRightCreate(d, function(err, error) { + // dispatch.call('change', error); + // }); } }); buttonSection.select('.status-button') // select and propagate data .attr('disabled', (hasAuth ? null : true)) .text(function(d) { - var action = (d.status === 'open' ? 'close' : 'open'); + var action = (d.status === 'open' ? 'close' : 'open'); // TODO: possibly remove reopen since I don't think it's an option var andComment = (d.newComment ? '_comment' : ''); - return t('note.' + action + andComment); + return t('keepRight.' + action + andComment); }) .on('click.status', function(d) { this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - var setStatus = (d.status === 'open' ? 'closed' : 'open'); - osm.postNoteUpdate(d, setStatus, function(err, note) { - dispatch.call('change', note); - }); + var keepRight = services.keepRight; + if (keepRight) { + // TODO: handle posting updates + // keepRight.postKeepRightUpdate(d, function(err, error) { + // dispatch.call('change', error); + // }); } }); @@ -310,11 +311,12 @@ export function uiKeepRightEditor(context) { }) .on('click.comment', function(d) { this.blur(); // avoid keeping focus on the button - #4641 - var osm = services.osm; - if (osm) { - osm.postNoteUpdate(d, d.status, function(err, note) { - dispatch.call('change', note); - }); + var keepRight = services.keepRight; + if (keepRight) { + // TODO: handle posting updates + // keepRight.postKeepRightUpdate(d, function(err, error) { + // dispatch.call('change', error); + // }); } }); } diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index 4ebc6e89d..9df0a8f69 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -155,7 +155,7 @@ export function uiNoteEditor(context) { noteSaveEnter .append('textarea') - .attr('id', 'new-comment-input') + .attr('class', 'new-comment-input') .attr('placeholder', t('note.inputPlaceholder')) .attr('maxlength', 1000) .property('value', function(d) { return d.newComment; }) diff --git a/modules/util/index.js b/modules/util/index.js index 0517ce874..acf73b74c 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -13,6 +13,7 @@ export { utilExternalValidationRules } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; +export { utilGetErrorDetails } from './keepRight'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json new file mode 100644 index 000000000..00bb2d993 --- /dev/null +++ b/modules/util/keepRight/errorSchema.json @@ -0,0 +1,329 @@ + +{ + "types": { + "errors": { + "_30": { + "title": "non-closed_areas", + "description": "This way is tagged with ''{$1}={$2}''and should be closed-loop" + }, + "_40": { + "title": "dead-ended one-ways", + "description": "The first node (id {$1}) of this one-way is not connected to any other way" + }, + "_41": { + "title": "", + "description": "The last node (id {$1}) of this one-way is not connected to any other way" + }, + "_42": { + "title": "", + "description": "This node cannot be reached because one-ways only lead away from here" + }, + "_43": { + "title": "", + "description": "You cannot escape from this node because one-ways only lead to here" + }, + "_50": { + "title": "almost-junctions", + "description": "This node is very close but not connected to way #{$1}" + }, + "_70": { + "title": "missing tags", + "description": "This {$1} has an empty tag: {$2}" + }, + "_71": { + "title": "", + "description": "This way has no tags" + }, + "_72": { + "title": "", + "description": "This node is not member of any way and does not have any tags" + }, + "_90": { + "title": "motorways without ref", + "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + }, + "_100": { + "title": "places of worship without religion", + "description": "This {$1} is tagged as place of worship and therefore needs a religion tag" + }, + "_110": { + "title": "point of interest without name", + "description": "This node is tagged as {$1} and therefore needs a name tag" + }, + "_120": { + "title": "ways without nodes", + "description": "This way has just one single node" + }, + "_130": { + "title": "floating islands", + "description": "This way is not connected to the rest of the map" + }, + "_150": { + "title": "railway crossing without tag", + "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + }, + "_160": { + "title": "wrongly used railway tag", + "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + }, + "_170": { + "title": "FIXME tagged items", + "description": "{$1}" + }, + "_180": { + "title": "relations without type", + "description": "This relation has no type tag which is mandatory for relations" + }, + "_190": { + "title": "intersections without junctions", + "description": "Finds way crossings on same layer without common node as a junction" + }, + "_191": { + "title": "highway-highway", + "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + }, + "_192": { + "title": "highway-waterway", + "description": "This {$1} intersects the {$2} #{$3}" + }, + "_193": { + "title": "highway-riverbank", + "description": "This {$1} intersects the {$2} #{$3}" + }, + "_194": { + "title": "waterway-waterway", + "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + }, + "_195": { + "title": "cycleway-cycleway", + "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + }, + "_196": { + "title": "highway-cycleway", + "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + }, + "_197": { + "title": "cycleway-waterway", + "description": "This {$1} intersects the {$2} #{$3}" + }, + "_198": { + "title": "cycleway-riverbank", + "description": "This {$1} intersects the {$2} #{$3}" + }, + "_200": { + "title": "overlapping ways", + "description": "Finds overlapping ways on same layer" + }, + "_201": { + "title": "highway-highway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_202": { + "title": "highway-waterway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_203": { + "title": "highway-riverbank", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_204": { + "title": "waterway-waterway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_205": { + "title": "cycleway-cycleway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_206": { + "title": "highway-cycleway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_207": { + "title": "cycleway-waterway", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_208": { + "title": "cycleway-riverbank", + "description": "This {$1} overlaps the {$2} #{$3}" + }, + "_210": { + "title": "loopings", + "description": "These errors contain self intersecting ways" + }, + "_211": { + "title": "", + "description": "This way contains more than one node at least twice. Nodes are {$1}. This may or may not be an error" + }, + "_212": { + "title": "", + "description": "This way has only two different nodes and contains one of them more than once" + }, + "_220": { + "title": "misspelled tags", + "description": "This {$1} is tagged ''{$2}={$3}''where {$4} looks like {$5}" + }, + "_221": { + "title": "", + "description": "The key of this {$1}''s tag is ''key'': {$2}" + }, + "_230": { + "title": "layer conflicts", + "description": "" + }, + "_231": { + "title": "mixed layers intersection", + "description": "This node is a junction of ways on different layers: {$1}" + }, + "_232": { + "title": "strange layers", + "description": "This {$1} is tagged with layer {$2}. This need not be an error but it looks strange" + }, + "_270": { + "title": "motorways connected directly", + "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + }, + "_280": { + "title": "boundaries", + "description": "" + }, + "_281": { + "title": "missing name", + "description": "This boundary has no name" + }, + "_282": { + "title": "missing admin level", + "description": "The boundary of {$1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + }, + "_283": { + "title": "no closed loop", + "description": "The boundary of {$1} is not closed-loop" + }, + "_284": { + "title": "splitting boundary", + "description": "The boundary of {$1} splits here" + }, + "_285": { + "title": "admin_level too high", + "description": "This boundary-way has admin_level {$1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + }, + "_290": { + "title": "restrictions", + "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" + }, + "_291": { + "title": "missing type", + "description": "This turn-restriction has no known restriction type" + }, + "_292": { + "title": "missing from way", + "description": "A turn-restriction needs exactly one {$1} member. This one has {$2}" + }, + "_293": { + "title": "missing to way", + "description": "A turn-restriction needs exactly one {$1} member. This one has {$2}" + }, + "_294": { + "title": "from or to not a way", + "description": "From- and To-members of turn restrictions need to be ways. {$1}" + }, + "_295": { + "title": "via is not on the way ends", + "description": "via (node #{$1}) is not the first or the last member of from (way #{$2})" + }, + "_296": { + "title": "wrong restriction angle", + "description": "restriction type is {$1} but angle is {$2} degrees. Maybe the restriction type is not appropriate?" + }, + "_297": { + "title": "wrong direction of to member", + "description": "wrong direction of to way {$1}" + }, + "_298": { + "title": "already restricted by oneway", + "description": "entry already prohibited by oneway tag on {$1}" + }, + "_310": { + "title": "roundabouts", + "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" + }, + "_311": { + "title": "not closed loop", + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + }, + "_312": { + "title": "wrong direction", + "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + }, + "_313": { + "title": "faintly connected", + "description": "This roundabout has only {$1} other roads connected. Roundabouts typically have three" + }, + "_320": { + "title": "*_link connections", + "description": "This way is tagged as highway={$1}_link but doesn''t have a connection to any other {$1} or {$1}_link" + }, + "_350": { + "title": "bridge-tags", + "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {$1}" + }, + "_370": { + "title": "doubled places", + "description": "This node has tags in common with the surrounding way #{$1} (tah fix this-->)((including the name ''The Garage'')) and seems to be redundand | This node has tags in common with the surrounding way #{$1} (including the name ''{$2}'') and seems to be redundand" + }, + "_380": { + "title": "non-physical use of sport-tag", + "description": "This way is tagged {$1} but has no physical tag like e.g. leisure, building, amenity or highway" + }, + "_400": { + "title": "geometry glitches", + "description": "" + }, + "_401": { + "title": "missing turn restriction", + "description": "ways {$1} and {$2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {$1} to {$2}" + }, + "_402": { + "title": "impossible angles", + "description": "this way bends in a very sharp angle here" + }, + "_410": { + "title": "website", + "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" + }, + "_411": { + "title": "http error", + "description": "The URL ({$1}) cannot be opened (HTTP status code {$2})" + }, + "_412": { + "title": "domain hijacking", + "description": "Possible domain squatting: {$1}. Suspicious text is: ''{$2}''" + }, + "_413": { + "title": "non-match", + "description": "Content of the URL ({$1}) did not contain these keywords: ({$2})" + } + }, + "warnings": { + "_20": { + "title": "multiple nodes on the same spot", + "description": "There is more than one node in this spot. Offending node IDs: {$1}" + }, + "_60": { + "title": "depreciated tags", + "description": "This {$1} uses deprecated tag {$2} = {$3}. Please use {$4} instead!" + }, + "_300": { + "title": "missing maxspeed", + "description": "missing maxspeed tag" + }, + "_360": { + "title": "language unknown", + "description": "It would be nice if this {$1} had an additional tag ''name:XX={$2}''where XX shows the language of its name ''{$2}''" + }, + "_390": { + "title": "missing tracktype", + "description": "This track doesn''t have a tracktype" + } + } + } +} \ No newline at end of file diff --git a/modules/util/keepRight/index.js b/modules/util/keepRight/index.js new file mode 100644 index 000000000..b14eacd63 --- /dev/null +++ b/modules/util/keepRight/index.js @@ -0,0 +1,2 @@ +export { utilGetErrorDetails } from './keepRight_error'; +export { types } from './errorSchema.json'; \ No newline at end of file diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js new file mode 100644 index 000000000..2c07cb443 --- /dev/null +++ b/modules/util/keepRight/keepRight_error.js @@ -0,0 +1,95 @@ +import { krError } from '../../osm'; +import { types } from './errorSchema.json'; + + +var keepRightSchema = { + 'schema': '', + 'id': 0, + 'error_type': 0, + 'error_name': 0, + 'object_type': [ + 'node', + 'way', + 'relation' + ], + 'object_id': 0, + 'state': [ + 'new', + 'reopened', + 'ignore_temporarily', + 'ignore' + ], + 'first_occurrence': new Date(), + 'last_checked': new Date(), + 'object_timestamp': new Date(), + 'user_name': '', + 'lat': 0, + 'lon': 0, + 'comment': '', + 'comment_timestamp': new Date(), + 'msgid': '', + 'txt1': '', + 'txt2': '', + 'txt3': '', + 'txt4': '', + 'txt5': '' + }; + +var keepRightSchemaFromWeb = { + 'error_type': '192', + 'object_type': 'way', + 'object_id': '339948768', + 'comment': null, + 'error_id': '92854860', + 'schema': '58', + 'description': 'This waterway intersects the highway #450282565', + 'title': 'intersections without junctions, highway-waterway' +}; + +export function utilGetErrorDetails(entity) { + if (!(entity instanceof krError)) return; + + // find the matching template from the error schema + var errorType = '_' + entity.error_type; + var matchingTemplate = types.errors[errorType] || types.warnings[errorType]; + if (!matchingTemplate) return; + + // tokenize descriptions + var errorDescriptions = entity.description.split(' '); + var schemaDescriptions = matchingTemplate.description.split(' '); + + + function iterator() { + + var parsedDescription = []; + var re = new RegExp(/{\$[0-9]}/); + + schemaDescriptions.forEach(function(word, index) { // TODO: figure out how to get the word and the index in a foreach + if (!re.test(word)) return; + + // get the word at this index, and at the next index value + var nextWord = schemaDescriptions[index + 1] ? schemaDescriptions[index + 1] : null; + + // also get the word at the same index from the errorDescription + + var parsedPhrase = ''; + + + // while error terms do not equal the next schema term + for (var i = index; i <= errorDescriptions.length - 1; i++) { + if (errorDescriptions[i] !== nextWord) { + parsedPhrase += errorDescriptions[i]; + } + parsedDescription.push(parsedPhrase); + break; + } + }); + } + + + function getCommonWords() { // TODO: implement, see if a variable is a common word like 'node', so that we can translate it before sending it off + } + + + iterator(); +} \ No newline at end of file From 3e7146c63f3d6150ecbaa3a5d1c9c47deeff56a7 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 3 Aug 2018 15:35:49 -0500 Subject: [PATCH 007/114] WIP: menu with sub-layer buttons; TODO: bug fixes, ui updates, wiring buttons --- css/65_data.css | 193 +++++++- data/core.yaml | 436 +++++++++--------- dist/locales/en.json | 534 +++++++++++----------- modules/services/keepRight.js | 10 +- modules/svg/keepRight.js | 28 +- modules/ui/keepRight_details.js | 78 +++- modules/ui/keepRight_editor.js | 32 +- modules/ui/keepRight_header.js | 26 +- modules/ui/map_data.js | 82 ++++ modules/util/index.js | 2 +- modules/util/keepRight/errorSchema.json | 2 +- modules/util/keepRight/index.js | 4 +- modules/util/keepRight/keepRight_error.js | 67 +-- svg/iD-sprite/icons/icon-bolt.svg | 6 + svg/iD-sprite/icons/icon-keepRight.svg | 10 - 15 files changed, 949 insertions(+), 561 deletions(-) create mode 100644 svg/iD-sprite/icons/icon-bolt.svg delete mode 100644 svg/iD-sprite/icons/icon-keepRight.svg diff --git a/css/65_data.css b/css/65_data.css index b3c85e284..af60c2b66 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -25,10 +25,14 @@ .layer-keepRight .kr_error .kr_error-shadow { color: #000; } -.note-header-icon .note-fill, -.layer-notes .note .note-fill, + .kr_error-header-icon .kr_error-fill, .layer-keepRight .kr_error .kr_error-fill { + stroke: #333; +} + +.note-header-icon .note-fill, +.layer-notes .note .note-fill { color: #ff3300; stroke: #333; stroke-width: 40px; @@ -172,3 +176,188 @@ border-radius: 5px 0 0 5px; } +/* KeepRight */ + +.kr_error_type_30 { + color: #ddb87d; +} + +.kr_error_type_40, +.kr_error_type_41, +.kr_error_type_42, +.kr_error_type_43 { + color: #894668; +} + +.kr_error_type_50 { + color: #c827fe; +} + +.kr_error_type_70, +.kr_error_type_71, +.kr_error_type_72 { + color: #74aeaf; +} + +.kr_error_type_90 { + color: #3124af; +} + +.kr_error_type_100 { + color: #9e8e91; +} + +.kr_error_type_110 { + color: #44650b; +} + +.kr_error_type_120 { + color: #99274d; +} + +.kr_error_type_130 { + color: #eb7310; +} + +.kr_error_type_150 { + color: #7218c1; +} + +.kr_error_type_160 { + color: #c903ae; +} + +.kr_error_type_170 { + color: #07d40b; +} + +.kr_error_type_180 { + color: #09ef12; +} + +.kr_error_type_190, +.kr_error_type_191, +.kr_error_type_192, +.kr_error_type_193, +.kr_error_type_194, +.kr_error_type_195, +.kr_error_type_196, +.kr_error_type_197, +.kr_error_type_198 { + color: #e6fcb0; +} + +.kr_error_type_200, +.kr_error_type_201, +.kr_error_type_202, +.kr_error_type_203, +.kr_error_type_204, +.kr_error_type_205, +.kr_error_type_206, +.kr_error_type_207, +.kr_error_type_208 { + color: #71f264; +} + +.kr_error_type_210, +.kr_error_type_211, +.kr_error_type_212 { + color: #4a7601; +} + +.kr_error_type_220, +.kr_error_type_221 { + color: #ef7cf2; +} + +.kr_error_type_230, +.kr_error_type_231, +.kr_error_type_232 { + color: #5f775c; +} + +.kr_error_type_270 { + color: #2aaf92; +} + +.kr_error_type_280, +.kr_error_type_281, +.kr_error_type_282, +.kr_error_type_283, +.kr_error_type_284, +.kr_error_type_285 { + color: #5f47a0; +} + +.kr_error_type_290, +.kr_error_type_291, +.kr_error_type_292, +.kr_error_type_293, +.kr_error_type_294, +.kr_error_type_295, +.kr_error_type_296, +.kr_error_type_297, +.kr_error_type_298 { + color: #9bb2cd; +} + +.kr_error_type_310, +.kr_error_type_311, +.kr_error_type_312, +.kr_error_type_313 { + color: #0550e8; +} + +.kr_error_type_320 { + color: #28d9bb; +} + +.kr_error_type_350 { + color: #4d719c; +} + +.kr_error_type_370 { + color: #ff8fdf; +} + +.kr_error_type_380 { + color: #b3b465; +} + +.kr_error_type_400, +.kr_error_type_401, +.kr_error_type_402 { + color: #b20e36; +} + +.kr_error_type_410, +.kr_error_type_411, +.kr_error_type_412, +.kr_error_type_413 { + color: #b07f7e; +} + +.kr_error-details-description { + text-align: left; + margin-bottom: 20px; +} + +.kr_error-details-description { + text-align: left; +} + +.QA-buttons { + display: flex; + flex-flow: row nowrap; + justify-content: space-around; + align-items: center; + padding: 0 5px; +} + +.QA-toggle-off, +.QA-toggle-on { + width: 35%; + margin: 10px 0px; + text-align: center; + vertical-align: middle; +} diff --git a/data/core.yaml b/data/core.yaml index 838205268..2d2eeb0b9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -486,6 +486,7 @@ en: zoom: Zoom to data fill_area: Fill Areas map_features: Map Features + QA: QA autohidden: "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." osmhidden: "These features have been automatically hidden because the OpenStreetMap layer is hidden." feature: @@ -648,264 +649,265 @@ en: keepRight: Error - tooltip: Q/A data from keepright.at title: Edit Error + detail_title: Error + detail_description: Description inputPlaceholder: Enter a comment to share with other users. newComment: New Comment upload_explanation: Your comments will be publicly visible to all keepRight.at users. - upload_explanation_with_user: "Your comments as {user} will be publicly visible to all OpenStreetMap users." - close_comment: Close and Comment - open_comment: Reopen and Comment - close: Close Note - open: Reopen Note + upload_explanation_with_user: "Your comments as {user} will be publicly visible to all keepRight.at users." + resolve_comment: Comment and Resolve + ignore_comment: Comment and Ignore + resolve: Resolve + ignore: Ignore + toggle-on: All on + toggle-off: All off entities: node: node way: way relation: relation - highway: highway - cycleway: cycleway - waterway: waterway - types: + errorTypes: errors: _30: - title: 'non-closed_areas' - description: 'This way is tagged with {var1}={var2} and should be closed-loop' + description: 'non-closed_areas' + tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop' _40: - title: 'dead-ended one-ways' - description: 'The first node (id {var1}) of this one-way is not connected to any other way' - _41: - title: '' - description: 'The last node (id {var1}) of this one-way is not connected to any other way' - _42: - title: '' - description: 'This node cannot be reached because one-ways only lead away from here' - _43: - title: '' - description: 'You cannot escape from this node because one-ways only lead to here' + description: 'dead-ended one-ways' + tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way' + _41: + description: '' + tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way' + _42: + description: '' + tooltip: 'This node cannot be reached because one-ways only lead away from here' + _43: + description: '' + tooltip: 'You cannot escape from this node because one-ways only lead to here' _50: - title: 'almost-junctions' - description: 'This node is very close but not connected to way #{var1}' + description: 'almost-junctions' + tooltip: 'This node is very close but not connected to way #{var1}' _70: - title: 'missing tags' - description: 'This {var1} has an empty tag: {var2}' - _71: - title: '' - description: 'This way has no tags' - _72: - title: '' - description: 'This node is not member of any way and doesn''t have any tags' + description: 'missing tags' + tooltip: 'This {var1} has an empty tag: {var2}' + _71: + description: '' + tooltip: 'This way has no tags' + _72: + description: '' + tooltip: 'This node is not member of any way and doesn''t have any tags' _90: - title: 'motorways without ref' - description: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' + description: 'motorways without ref' + tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' _100: - title: 'places of worship without religion' - description: 'This {var1} is tagged as place of worship and therefore needs a religion tag' + description: 'places of worship without religion' + tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag' _110: - title: 'point of interest without name' - description: 'This node is tagged as {var1} and therefore needs a name tag' + description: 'point of interest without name' + tooltip: 'This node is tagged as {var1} and therefore needs a name tag' _120: - title: 'ways without nodes' - description: 'This way has just one single node' + description: 'ways without nodes' + tooltip: 'This way has just one single node' _130: - title: 'floating islands' - description: 'This way is not connected to the rest of the map' + description: 'floating islands' + tooltip: 'This way is not connected to the rest of the map' _150: - title: 'railway crossing without tag' - description: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + description: 'railway crossing without tag' + tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' _160: - title: 'wrongly used railway tag' - description: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + description: 'wrongly used railway tag' + tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' _170: - title: 'FIXME tagged items' - description: '{var1}' + description: 'FIXME tagged items' + tooltip: '{var1}' _180: - title: 'relations without type' - description: 'This relation has no type tag which is mandatory for relations' + description: 'relations without type' + tooltip: 'This relation has no type tag which is mandatory for relations' _190: - title: 'intersections without junctions' - description: 'Finds way crossings on same layer without common node as a junction' - _191: - title: 'highway-highway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _192: - title: 'highway-waterway' - description: 'This {var1} intersects the {var2} #{var3}' - _193: - title: 'highway-riverbank' - description: 'This {var1} intersects the {var2} #{var3}' - _194: - title: 'waterway-waterway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _195: - title: 'cycleway-cycleway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _196: - title: 'highway-cycleway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _197: - title: 'cycleway-waterway' - description: 'This {var1} intersects the {var2} #{var3}' - _198: - title: 'cycleway-riverbank' - description: 'This {var1} intersects the {var2} #{var3}' + description: 'intersections without junctions' + tooltip: 'Finds way crossings on same layer without common node as a junction' + _191: + description: 'highway-highway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _192: + description: 'highway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _193: + description: 'highway-riverbank' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _194: + description: 'waterway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _195: + description: 'cycleway-cycleway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _196: + description: 'highway-cycleway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _197: + description: 'cycleway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _198: + description: 'cycleway-riverbank' + tooltip: 'This {var1} intersects the {var2} #{var3}' _200: - title: 'overlapping ways' - description: 'Finds overlapping ways on same layer' - _201: - title: 'highway-highway' - description: 'This {var1} overlaps the {var2} #{var3}' - _202: - title: 'highway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}' - _203: - title: 'highway-riverbank' - description: 'This {var1} overlaps the {var2} #{var3}' - _204: - title: 'waterway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}' - _205: - title: 'cycleway-cycleway' - description: 'This {var1} overlaps the {var2} #{var3}' - _206: - title: 'highway-cycleway' - description: 'This {var1} overlaps the {var2} #{var3}' - _207: - title: 'cycleway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}' - _208: - title: 'cycleway-riverbank' - description: 'This {var1} overlaps the {var2} #{var3}' + description: 'overlapping ways' + tooltip: 'Finds overlapping ways on same layer' + _201: + description: 'highway-highway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _202: + description: 'highway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _203: + description: 'highway-riverbank' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _204: + description: 'waterway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _205: + description: 'cycleway-cycleway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _206: + description: 'highway-cycleway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _207: + description: 'cycleway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _208: + description: 'cycleway-riverbank' + tooltip: 'This {var1} overlaps the {var2} #{var3}' _210: - title: 'loopings' - description: 'These errors contain self intersecting ways' - _211: - title: '' - description: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' - _212: - title: '' - description: 'This way has only two different nodes and contains one of them more than once' + description: 'loopings' + tooltip: 'These errors contain self intersecting ways' + _211: + description: '' + tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' + _212: + description: '' + tooltip: 'This way has only two different nodes and contains one of them more than once' _220: - title: 'misspelled tags' - description: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' - _221: - title: '' - description: 'The key of this {var1} tag is key {var2}' - _230: - title: 'layer conflicts' - description: '' - _231: - title: 'mixed layers intersection' - description: 'This node is a junction of ways on different layers: {var1}' - _232: - title: 'strange layers' - description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' + description: 'misspelled tags' + tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' + _221: + description: '' + tooltip: 'The key of this {var1} tag is key {var2}' + _230: + description: 'layer conflicts' + tooltip: '' + _231: + description: 'mixed layers intersection' + tooltip: 'This node is a junction of ways on different layers: {var1}' + _232: + description: 'strange layers' + tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' _270: - title: 'motorways connected directly' - description: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' + description: 'motorways connected directly' + tooltip: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' _280: - title: 'boundaries' - description: '' - _281: - title: 'missing name' - description: 'This boundary has no name' - _282: - title: 'missing admin level' - description: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' - _283: - title: 'no closed loop' - description: 'The boundary of {var1} is not closed-loop' - _284: - title: 'splitting boundary' - description: 'The boundary of {var1} splits here' - _285: - title: 'admin_level too high' - description: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + description: 'boundaries' + tooltip: '' + _281: + description: 'missing name' + tooltip: 'This boundary has no name' + _282: + description: 'missing admin level' + tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' + _283: + description: 'no closed loop' + tooltip: 'The boundary of {var1} is not closed-loop' + _284: + description: 'splitting boundary' + tooltip: 'The boundary of {var1} splits here' + _285: + description: 'admin_level too high' + tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' _290: - title: 'restrictions' - description: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' - _291: - title: 'missing type' - description: 'This turn-restriction has no known restriction type' - _292: - title: 'missing from way' - description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' - _293: - title: 'missing to way' - description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' - _294: - title: 'from or to not a way' - description: 'From- and To-members of turn restrictions need to be ways. {var1}' - _295: - title: 'via is not on the way ends' - description: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' - _296: - title: 'wrong restriction angle' - description: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' - _297: - title: 'wrong direction of to member' - description: 'wrong direction of to way {var1}' - _298: - title: 'already restricted by oneway' - description: 'entry already prohibited by oneway tag on {var1}' + description: 'restrictions' + tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' + _291: + description: 'missing type' + tooltip: 'This turn-restriction has no known restriction type' + _292: + description: 'missing from way' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _293: + description: 'missing to way' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _294: + description: 'from or to not a way' + tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}' + _295: + description: 'via is not on the way ends' + tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' + _296: + description: 'wrong restriction angle' + tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' + _297: + description: 'wrong direction of to member' + tooltip: 'wrong direction of to way {var1}' + _298: + description: 'already restricted by oneway' + tooltip: 'entry already prohibited by oneway tag on {var1}' _310: - title: 'roundabouts' - description: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' - _311: - title: 'not closed loop' - description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' - _312: - title: 'wrong direction' - description: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' - _313: - title: 'faintly connected' - description: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' + description: 'roundabouts' + tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' + _311: + description: 'not closed loop' + tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + _312: + description: 'wrong direction' + tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + _313: + description: 'faintly connected' + tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' _320: - title: '*_link connections' - description: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' + description: '*_link connections' + tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' _350: - title: 'bridge-tags' - description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' + description: 'bridge-tags' + tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' _370: - title: 'doubled places' - description: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' + description: 'doubled places' + tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' _380: - title: 'non-physical use of sport-tag' - description: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' + description: 'non-physical use of sport-tag' + tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' _400: - title: 'geometry glitches' - description: '' - _401: - title: 'missing turn restriction' - description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' - _402: - title: 'impossible angles' - description: 'this way bends in a very sharp angle here' + description: 'geometry glitches' + tooltip: '' + _401: + description: 'missing turn restriction' + tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' + _402: + description: 'impossible angles' + tooltip: 'this way bends in a very sharp angle here' _410: - title: 'website' - description: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' - _411: - title: 'http error' - description: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' - _412: - title: 'domain hijacking' - description: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' - _413: - title: 'non-match' - description: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' + description: 'website' + tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' + _411: + description: 'http error' + tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' + _412: + description: 'domain hijacking' + tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' + _413: + description: 'non-match' + tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' warnings: _20: - title: 'multiple nodes on the same spot' - description: 'There is more than one node in this spot. Offending node IDs: {var1}' + description: 'multiple nodes on the same spot' + tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}' _60: - title: 'depreciated tags' - description: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' + description: 'depreciated tags' + tooltip: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' _300: - title: 'missing maxspeed' - description: 'missing maxspeed tag' + description: 'missing maxspeed' + tooltip: 'missing maxspeed tag' _360: - title: 'language unknown' - description: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' + description: 'language unknown' + tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' _390: - title: 'missing tracktype' - description: This track doesn't have a tracktype + description: 'missing tracktype' + tooltip: This track doesn't have a tracktype streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index aeb19ddc9..24dfbfc72 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -591,6 +591,7 @@ }, "fill_area": "Fill Areas", "map_features": "Map Features", + "QA": "QA", "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them.", "osmhidden": "These features have been automatically hidden because the OpenStreetMap layer is hidden." }, @@ -787,345 +788,346 @@ "keepRight": "Error -", "tooltip": "Q/A data from keepright.at", "title": "Edit Error", + "detail_title": "Error", + "detail_description": "Description", "inputPlaceholder": "Enter a comment to share with other users.", "newComment": "New Comment", "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", - "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all OpenStreetMap users.", - "close_comment": "Close and Comment", - "open_comment": "Reopen and Comment", - "close": "Close Note", - "open": "Reopen Note", + "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all keepRight.at users.", + "resolve_comment": "Comment and Resolve", + "ignore_comment": "Comment and Ignore", + "resolve": "Resolve", + "ignore": "Ignore", + "toggle-on": "All on", + "toggle-off": "All off", "entities": { "node": "node", "way": "way", - "relation": "relation", - "highway": "highway", - "cycleway": "cycleway", - "waterway": "waterway" + "relation": "relation" }, - "types": { + "errorTypes": { "errors": { "_30": { - "title": "non-closed_areas", - "description": "This way is tagged with {var1}={var2} and should be closed-loop" + "description": "non-closed_areas", + "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop" }, "_40": { - "title": "dead-ended one-ways", - "description": "The first node (id {var1}) of this one-way is not connected to any other way", - "_41": { - "title": "", - "description": "The last node (id {var1}) of this one-way is not connected to any other way" - }, - "_42": { - "title": "", - "description": "This node cannot be reached because one-ways only lead away from here" - }, - "_43": { - "title": "", - "description": "You cannot escape from this node because one-ways only lead to here" - } + "description": "dead-ended one-ways", + "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way" + }, + "_41": { + "description": "", + "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way" + }, + "_42": { + "description": "", + "tooltip": "This node cannot be reached because one-ways only lead away from here" + }, + "_43": { + "description": "", + "tooltip": "You cannot escape from this node because one-ways only lead to here" }, "_50": { - "title": "almost-junctions", - "description": "This node is very close but not connected to way #{var1}" + "description": "almost-junctions", + "tooltip": "This node is very close but not connected to way #{var1}" }, "_70": { - "title": "missing tags", - "description": "This {var1} has an empty tag: {var2}", - "_71": { - "title": "", - "description": "This way has no tags" - }, - "_72": { - "title": "", - "description": "This node is not member of any way and doesn't have any tags" - } + "description": "missing tags", + "tooltip": "This {var1} has an empty tag: {var2}" + }, + "_71": { + "description": "", + "tooltip": "This way has no tags" + }, + "_72": { + "description": "", + "tooltip": "This node is not member of any way and doesn't have any tags" }, "_90": { - "title": "motorways without ref", - "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + "description": "motorways without ref", + "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" }, "_100": { - "title": "places of worship without religion", - "description": "This {var1} is tagged as place of worship and therefore needs a religion tag" + "description": "places of worship without religion", + "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag" }, "_110": { - "title": "point of interest without name", - "description": "This node is tagged as {var1} and therefore needs a name tag" + "description": "point of interest without name", + "tooltip": "This node is tagged as {var1} and therefore needs a name tag" }, "_120": { - "title": "ways without nodes", - "description": "This way has just one single node" + "description": "ways without nodes", + "tooltip": "This way has just one single node" }, "_130": { - "title": "floating islands", - "description": "This way is not connected to the rest of the map" + "description": "floating islands", + "tooltip": "This way is not connected to the rest of the map" }, "_150": { - "title": "railway crossing without tag", - "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + "description": "railway crossing without tag", + "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" }, "_160": { - "title": "wrongly used railway tag", - "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + "description": "wrongly used railway tag", + "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" }, "_170": { - "title": "FIXME tagged items", - "description": "{var1}" + "description": "FIXME tagged items", + "tooltip": "{var1}" }, "_180": { - "title": "relations without type", - "description": "This relation has no type tag which is mandatory for relations" + "description": "relations without type", + "tooltip": "This relation has no type tag which is mandatory for relations" }, "_190": { - "title": "intersections without junctions", - "description": "Finds way crossings on same layer without common node as a junction", - "_191": { - "title": "highway-highway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_192": { - "title": "highway-waterway", - "description": "This {var1} intersects the {var2} #{var3}" - }, - "_193": { - "title": "highway-riverbank", - "description": "This {var1} intersects the {var2} #{var3}" - }, - "_194": { - "title": "waterway-waterway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_195": { - "title": "cycleway-cycleway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_196": { - "title": "highway-cycleway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_197": { - "title": "cycleway-waterway", - "description": "This {var1} intersects the {var2} #{var3}" - }, - "_198": { - "title": "cycleway-riverbank", - "description": "This {var1} intersects the {var2} #{var3}" - } + "description": "intersections without junctions", + "tooltip": "Finds way crossings on same layer without common node as a junction" + }, + "_191": { + "description": "highway-highway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_192": { + "description": "highway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_193": { + "description": "highway-riverbank", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_194": { + "description": "waterway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_195": { + "description": "cycleway-cycleway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_196": { + "description": "highway-cycleway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_197": { + "description": "cycleway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_198": { + "description": "cycleway-riverbank", + "tooltip": "This {var1} intersects the {var2} #{var3}" }, "_200": { - "title": "overlapping ways", - "description": "Finds overlapping ways on same layer", - "_201": { - "title": "highway-highway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_202": { - "title": "highway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_203": { - "title": "highway-riverbank", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_204": { - "title": "waterway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_205": { - "title": "cycleway-cycleway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_206": { - "title": "highway-cycleway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_207": { - "title": "cycleway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}" - }, - "_208": { - "title": "cycleway-riverbank", - "description": "This {var1} overlaps the {var2} #{var3}" - } + "description": "overlapping ways", + "tooltip": "Finds overlapping ways on same layer" + }, + "_201": { + "description": "highway-highway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_202": { + "description": "highway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_203": { + "description": "highway-riverbank", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_204": { + "description": "waterway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_205": { + "description": "cycleway-cycleway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_206": { + "description": "highway-cycleway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_207": { + "description": "cycleway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_208": { + "description": "cycleway-riverbank", + "tooltip": "This {var1} overlaps the {var2} #{var3}" }, "_210": { - "title": "loopings", - "description": "These errors contain self intersecting ways", - "_211": { - "title": "", - "description": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" - }, - "_212": { - "title": "", - "description": "This way has only two different nodes and contains one of them more than once" - } + "description": "loopings", + "tooltip": "These errors contain self intersecting ways" + }, + "_211": { + "description": "", + "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" + }, + "_212": { + "description": "", + "tooltip": "This way has only two different nodes and contains one of them more than once" }, "_220": { - "title": "misspelled tags", - "description": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}", - "_221": { - "title": "", - "description": "The key of this {var1} tag is key {var2}" - }, - "_230": { - "title": "layer conflicts", - "description": "" - }, - "_231": { - "title": "mixed layers intersection", - "description": "This node is a junction of ways on different layers: {var1}" - }, - "_232": { - "title": "strange layers", - "description": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" - } + "description": "misspelled tags", + "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}" + }, + "_221": { + "description": "", + "tooltip": "The key of this {var1} tag is key {var2}" + }, + "_230": { + "description": "layer conflicts", + "tooltip": "" + }, + "_231": { + "description": "mixed layers intersection", + "tooltip": "This node is a junction of ways on different layers: {var1}" + }, + "_232": { + "description": "strange layers", + "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" }, "_270": { - "title": "motorways connected directly", - "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + "description": "motorways connected directly", + "tooltip": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." }, "_280": { - "title": "boundaries", - "description": "", - "_281": { - "title": "missing name", - "description": "This boundary has no name" - }, - "_282": { - "title": "missing admin level", - "description": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" - }, - "_283": { - "title": "no closed loop", - "description": "The boundary of {var1} is not closed-loop" - }, - "_284": { - "title": "splitting boundary", - "description": "The boundary of {var1} splits here" - }, - "_285": { - "title": "admin_level too high", - "description": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" - } + "description": "boundaries", + "tooltip": "" + }, + "_281": { + "description": "missing name", + "tooltip": "This boundary has no name" + }, + "_282": { + "description": "missing admin level", + "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + }, + "_283": { + "description": "no closed loop", + "tooltip": "The boundary of {var1} is not closed-loop" + }, + "_284": { + "description": "splitting boundary", + "tooltip": "The boundary of {var1} splits here" + }, + "_285": { + "description": "admin_level too high", + "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" }, "_290": { - "title": "restrictions", - "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat", - "_291": { - "title": "missing type", - "description": "This turn-restriction has no known restriction type" - }, - "_292": { - "title": "missing from way", - "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}" - }, - "_293": { - "title": "missing to way", - "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}" - }, - "_294": { - "title": "from or to not a way", - "description": "From- and To-members of turn restrictions need to be ways. {var1}" - }, - "_295": { - "title": "via is not on the way ends", - "description": "via (node #{var1}) is not the first or the last member of from (way #{var2})" - }, - "_296": { - "title": "wrong restriction angle", - "description": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" - }, - "_297": { - "title": "wrong direction of to member", - "description": "wrong direction of to way {var1}" - }, - "_298": { - "title": "already restricted by oneway", - "description": "entry already prohibited by oneway tag on {var1}" - } + "description": "restrictions", + "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" + }, + "_291": { + "description": "missing type", + "tooltip": "This turn-restriction has no known restriction type" + }, + "_292": { + "description": "missing from way", + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_293": { + "description": "missing to way", + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_294": { + "description": "from or to not a way", + "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}" + }, + "_295": { + "description": "via is not on the way ends", + "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})" + }, + "_296": { + "description": "wrong restriction angle", + "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" + }, + "_297": { + "description": "wrong direction of to member", + "tooltip": "wrong direction of to way {var1}" + }, + "_298": { + "description": "already restricted by oneway", + "tooltip": "entry already prohibited by oneway tag on {var1}" }, "_310": { - "title": "roundabouts", - "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1", - "_311": { - "title": "not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" - }, - "_312": { - "title": "wrong direction", - "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" - }, - "_313": { - "title": "faintly connected", - "description": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" - } + "description": "roundabouts", + "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" + }, + "_311": { + "description": "not closed loop", + "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + }, + "_312": { + "description": "wrong direction", + "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + }, + "_313": { + "description": "faintly connected", + "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" }, "_320": { - "title": "*_link connections", - "description": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" + "description": "*_link connections", + "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" }, "_350": { - "title": "bridge-tags", - "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" + "description": "bridge-tags", + "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" }, "_370": { - "title": "doubled places", - "description": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" + "description": "doubled places", + "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" }, "_380": { - "title": "non-physical use of sport-tag", - "description": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" + "description": "non-physical use of sport-tag", + "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" }, "_400": { - "title": "geometry glitches", - "description": "", - "_401": { - "title": "missing turn restriction", - "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" - }, - "_402": { - "title": "impossible angles", - "description": "this way bends in a very sharp angle here" - } + "description": "geometry glitches", + "tooltip": "" + }, + "_401": { + "description": "missing turn restriction", + "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" + }, + "_402": { + "description": "impossible angles", + "tooltip": "this way bends in a very sharp angle here" }, "_410": { - "title": "website", - "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*", - "_411": { - "title": "http error", - "description": "The URL ({var1}) cannot be opened (HTTP status code {var2})" - }, - "_412": { - "title": "domain hijacking", - "description": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" - }, - "_413": { - "title": "non-match", - "description": "Content of the URL ({var1}) did not contain these keywords: ({var2})" - } + "description": "website", + "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" + }, + "_411": { + "description": "http error", + "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})" + }, + "_412": { + "description": "domain hijacking", + "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" + }, + "_413": { + "description": "non-match", + "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})" } }, "warnings": { "_20": { - "title": "multiple nodes on the same spot", - "description": "There is more than one node in this spot. Offending node IDs: {var1}" + "description": "multiple nodes on the same spot", + "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}" }, "_60": { - "title": "depreciated tags", - "description": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" + "description": "depreciated tags", + "tooltip": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" }, "_300": { - "title": "missing maxspeed", - "description": "missing maxspeed tag" + "description": "missing maxspeed", + "tooltip": "missing maxspeed tag" }, "_360": { - "title": "language unknown", - "description": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + "description": "language unknown", + "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" }, "_390": { - "title": "missing tracktype", - "description": "This track doesn't have a tracktype" + "description": "missing tracktype", + "tooltip": "This track doesn't have a tracktype" } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 7bc6ddc2f..5a1e3dc5d 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -186,7 +186,7 @@ export default { }, - // get all cached notes covering the viewport + // get all cached errors covering the viewport keepRight: function(projection) { var viewport = projection.clipExtent(); var min = [viewport[0][0], viewport[1][1]]; @@ -202,20 +202,20 @@ export default { return _keepRightCache.keepRight[id]; }, - // replace a single note in the cache + // replace a single error in the cache replaceError: function(error) { if (!(error instanceof krError) || !error.id) return; - _keepRightCache.note[error.id] = error; + _keepRightCache.keepRight[error.id] = error; updateRtree(encodeErrorRtree(error), true); // true = replace return error; }, - // remove a single note from the cache + // remove a single error from the cache removeError: function(error) { if (!(error instanceof krError) || !error.id) return; - delete _keepRightCache.note[error.id]; + delete _keepRightCache.keepRight[error.id]; updateRtree(encodeErrorRtree(error), false); // false = remove }, }; \ No newline at end of file diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index fb0c76603..561bf6970 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -1,4 +1,3 @@ -import _some from 'lodash-es/some'; import _throttle from 'lodash-es/throttle'; import { select as d3_select } from 'd3-selection'; import { svgPointTransform } from './index'; @@ -16,6 +15,7 @@ export function svgKeepRight(projection, context, dispatch) { if (svgKeepRight.initialized) return; // run once svgKeepRight.enabled = false; svgKeepRight.initialized = true; + svgKeepRight.visibleErrors = [30]; } @@ -49,7 +49,7 @@ export function svgKeepRight(projection, context, dispatch) { function editOff() { - layer.selectAll('.icon-sign').remove(); + layer.selectAll('.kr_error').remove(); layer.style('display', 'none'); } @@ -83,9 +83,10 @@ export function svgKeepRight(projection, context, dispatch) { var service = getService(); var selectedID = context.selectedNoteID(); // TODO: update with selectedErrorID var data = (service ? service.keepRight(projection) : []); + var visibleData = data; // getVisible(data); // TODO: only show sub-layers that are toggled on var transform = svgPointTransform(projection); var kr_errors = layer.selectAll('.kr_error') - .data(data, function(d) { return d.id; }); + .data(visibleData, function(d) { return d.id; }); // exit kr_errors.exit() @@ -94,7 +95,8 @@ export function svgKeepRight(projection, context, dispatch) { // enter var kr_errorsEnter = kr_errors.enter() .append('g') - .attr('class', function(d) { return 'kr_error kr_error-' + d.id; }) + .attr('class', function(d) { + return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; }) .classed('new', function(d) { return d.id < 0; }); kr_errorsEnter @@ -114,9 +116,9 @@ export function svgKeepRight(projection, context, dispatch) { .attr('class', 'kr_error-fill') .attr('width', '20px') .attr('height', '20px') - .attr('x', '-8px') - .attr('y', '-22px') - .attr('xlink:href', '#iD-icon-note'); // TODO: update icon + .attr('x', '-4px') + .attr('y', '-24px') + .attr('xlink:href', '#iD-icon-bolt'); // update kr_errors @@ -179,8 +181,16 @@ export function svgKeepRight(projection, context, dispatch) { }; - drawKeepRight.supported = function() { - return !!getService(); + drawKeepRight.visibleErrors = function(_) { + if (!arguments.length) return svgKeepRight.visibleErrors; + svgKeepRight.visibleErrors.push(_); + if (svgKeepRight.visibleErrors) { + showLayer(); + } else { + hideLayer(); + } + dispatch.call('change'); + return this; }; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 461da3f19..646609b61 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,12 +1,46 @@ import { t } from '../util/locale'; -import { utilGetErrorDetails } from '../util'; +import { parseErrorDescriptions, errorTypes } from '../util'; export function uiKeepRightDetails() { var _error; + var _template; + var _templateErrorType; + var _category; + var _categoryElements; + var _parent_error_type = ''; + var _titleBase; + + + function initDetails() { + if (errorTypes.errors['_' + _error.error_type]) { + _templateErrorType = '_' + _error.error_type; + _template = errorTypes.errors[_templateErrorType]; + _category = 'errors'; + } else if (errorTypes.warnings[_templateErrorType]) { + _template = errorTypes.errors[_templateErrorType]; + _category = 'warnings'; + } else { return; } + + // if there is a parent, save it's error type + _categoryElements = errorTypes[_category]; + var base_error_type = (Math.round(_error.error_type / 10) * 10).toString(); + if ((_categoryElements['_' + base_error_type]) && (base_error_type !== _error.error_type) ) { + _parent_error_type = '_' + base_error_type; + } + + _titleBase = 'keepRight.errorTypes.' + _category + '.'; + + } function keepRightDetails(selection) { + if (!_error || !_error.error_type) return; + + initDetails(); + if (!_template) return; + + var details = selection.selectAll('.kr_error-details') .data( (_error ? [_error] : []), @@ -18,14 +52,46 @@ export function uiKeepRightDetails() { var detailsEnter = details.enter() .append('div') - .attr('class', 'kr_error-details'); + .attr('class', 'kr_error-details kr_error-details-container'); - detailsEnter + // title + var title = detailsEnter + .append('div') + .attr('class', 'kr_error-details-title'); + + title.append('h4') + .text(function() { return t('keepRight.detail_title'); }); + + title.append('div') + .text(function() { + var title = ''; + + // if this is a subtype, append it's parent title + if (_parent_error_type) { + title = t(_titleBase + _parent_error_type + '.description' + ':\n'); + } + + // append title + if (_error.error_type) { + title += t(_titleBase + _templateErrorType + '.description'); + } + + return title; + }); + + // description + var description = detailsEnter + .append('div') + .attr('class', 'kr_error-details-description'); + + description + .append('h4') + .text(function() { return t('keepRight.detail_description'); }); + + description .append('div') - .attr('class', 'kr_error-details-label') .text(function(d) { - var error = utilGetErrorDetails(d); - return t('keepRight.keepRight'); // TODO: add details here + return t(_titleBase + _templateErrorType + '.tooltip', parseErrorDescriptions(d)); }); } diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index d7054f0a6..9002dc4ba 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -26,7 +26,7 @@ export function uiKeepRightEditor(context) { var dispatch = d3_dispatch('change'); var keepRightComment = uiKeepRightComment(); var keepRightDetails = uiKeepRightDetails(); - var keepRightHeader = uiKeepRightHeader(); + var keepRightHeader = uiKeepRightHeader(context); var _error; @@ -249,7 +249,11 @@ export function uiKeepRightEditor(context) { buttonEnter .append('button') - .attr('class', 'button status-button action'); + .attr('class', 'button resolve-button action'); + + buttonEnter + .append('button') + .attr('class', 'button ignore-button action'); buttonEnter .append('button') @@ -287,12 +291,28 @@ export function uiKeepRightEditor(context) { } }); - buttonSection.select('.status-button') // select and propagate data + buttonSection.select('.resolve-button') // select and propagate data .attr('disabled', (hasAuth ? null : true)) .text(function(d) { - var action = (d.status === 'open' ? 'close' : 'open'); // TODO: possibly remove reopen since I don't think it's an option var andComment = (d.newComment ? '_comment' : ''); - return t('keepRight.' + action + andComment); + return t('keepRight.resolve' + andComment); + }) + .on('click.status', function(d) { + this.blur(); // avoid keeping focus on the button - #4641 + var keepRight = services.keepRight; + if (keepRight) { + // TODO: handle posting updates + // keepRight.postKeepRightUpdate(d, function(err, error) { + // dispatch.call('change', error); + // }); + } + }); + + buttonSection.select('.ignore-button') // select and propagate data + .attr('disabled', (hasAuth ? null : true)) + .text(function(d) { + var andComment = (d.newComment ? '_comment' : ''); + return t('keepRight.ignore' + andComment); }) .on('click.status', function(d) { this.blur(); // avoid keeping focus on the button - #4641 @@ -307,7 +327,7 @@ export function uiKeepRightEditor(context) { buttonSection.select('.comment-button') // select and propagate data .attr('disabled', function(d) { - return (hasAuth && d.status === 'open' && d.newComment) ? null : true; + return (hasAuth && d.newComment) ? null : true; }) .on('click.comment', function(d) { this.blur(); // avoid keeping focus on the button - #4641 diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 6fbbfdd4a..e98282725 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -2,15 +2,21 @@ import { t } from '../util/locale'; import { svgIcon } from '../svg'; -export function uiKeepRightHeader() { +export function uiKeepRightHeader(context) { var _error; + function getEntityLink() { + + var url = context.connection().entityURL(context.entity(_error.object_id)); + } + + function keepRightHeader(selection) { var header = selection.selectAll('.kr_error-header') .data( (_error ? [_error] : []), - function(d) { return d.status + d.id; } + function(d) { return d.id; } ); header.exit() @@ -22,20 +28,26 @@ export function uiKeepRightHeader() { var iconEnter = headerEnter .append('div') - .attr('class', function(d) { return 'kr_error-header-icon ' + d.status; }) + .attr('class', function(d) { return 'kr_error-header-icon '; }) .classed('new', function(d) { return d.id < 0; }); iconEnter .append('div') - .attr('class', 'preset-icon-28') - .call(svgIcon('#iD-icon-note', 'note-fill')); // TODO: change classes + .attr('class', function(d) { + return 'preset-icon-28 kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; + }) + + .call(svgIcon('#iD-icon-bolt', 'kr_error-fill')); headerEnter .append('div') .attr('class', 'kr_error-header-label') .text(function(d) { - return t('keepRight.keepRight') + ' ' + d.object_type + ' ' + ' ' + d.error_id; - }); + return t('keepRight.entities.' + d.object_type); + }) + .append('div') + // .attr('href', getEntityLink()) // TODO: add / remove link if entity is/isn't in the graph + .text(function(d) { return d.object_id; }); } diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index b6fbe8a4b..1132957c2 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -1,9 +1,11 @@ +import { dispatch as d3_dispatch } from 'd3-dispatch'; import { event as d3_event, select as d3_select } from 'd3-selection'; import { svgIcon } from '../svg'; +import { errorTypes } from '../util'; import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; import { geoExtent } from '../geo'; @@ -16,8 +18,11 @@ import { uiTooltipHtml } from './tooltipHtml'; export function uiMapData(context) { + var dispatch = d3_dispatch('change'); + var key = t('map_data.key'); var features = context.features().keys(); + var errors = Object.keys(errorTypes.errors); // TODO: add warnings var layers = context.layers(); var fills = ['wireframe', 'partial', 'full']; @@ -29,6 +34,7 @@ export function uiMapData(context) { var _dataLayerContainer = d3_select(null); var _fillList = d3_select(null); var _featureList = d3_select(null); + var _QAList = d3_select(null); function showsFeature(d) { @@ -47,6 +53,17 @@ export function uiMapData(context) { } + function showsError(d) { + // return context.errors().enabled(d); + } + + + function clickError(d) { + // context.errors().toggle(d); + // update(); + } + + function showsFill(d) { return _fillSelected === d; } @@ -411,6 +428,43 @@ export function uiMapData(context) { } + function drawQAButtons(selection) { + var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); + var buttonSection = selection.selectAll('.QA-buttons') + .data([0]); + + // exit + buttonSection.exit() + .remove(); + + // enter + var buttonEnter = buttonSection.enter() + .append('div') + .attr('class', 'QA-buttons'); + + buttonEnter + .append('button') + .attr('class', 'button QA-toggle-on action') + .text(t('keepRight.toggle-on')) + .on('click', function() { + QAButtons.property('checked', true); + dispatch.call('change'); + }); + + buttonEnter + .append('button') + .attr('class', 'button QA-toggle-off action') + .text(t('keepRight.toggle-off')) + .on('click', function() { + QAButtons.property('checked', false); + dispatch.call('change'); + }); + + buttonSection = buttonSection + .merge(buttonEnter); + } + + function drawListItems(selection, data, type, name, change, active) { var items = selection.selectAll('li') .data(data); @@ -498,6 +552,17 @@ export function uiMapData(context) { } + function renderQA(selection) { + var container = selection.selectAll('layer-QA') + .data([0]); + + _QAList = container.enter() + .append('ul') + .attr('class', 'layer-list layer-QA') + .merge(container); + } + + function update() { _dataLayerContainer .call(drawOsmItems) @@ -510,6 +575,11 @@ export function uiMapData(context) { _featureList .call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature); + + _QAList + .call(drawListItems, errors, 'checkbox', 'keepRight.errorTypes.errors', clickError, showsError); + d3_select('.disclosure-wrap-QA') + .call(drawQAButtons); } @@ -636,11 +706,23 @@ export function uiMapData(context) { .content(renderFeatureList) ); + // Q/A tools + content + .append('div') + .attr('class', 'map-data-QA') + .call(uiDisclosure(context, 'QA', false) + .title(t('map_data.QA')) + .content(renderQA) + ); + // add listeners context.features() .on('change.map_data-update', update); + // context.errors() + // .on('change.map_data-update', update); // TODO: add errors list to context? + update(); setFill(_fillSelected); diff --git a/modules/util/index.js b/modules/util/index.js index acf73b74c..be877351f 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -13,7 +13,7 @@ export { utilExternalValidationRules } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; -export { utilGetErrorDetails } from './keepRight'; +export { errorTypes, parseErrorDescriptions } from './keepRight'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 00bb2d993..c6688d9d2 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -1,6 +1,6 @@ { - "types": { + "errorTypes": { "errors": { "_30": { "title": "non-closed_areas", diff --git a/modules/util/keepRight/index.js b/modules/util/keepRight/index.js index b14eacd63..4a95afaf5 100644 --- a/modules/util/keepRight/index.js +++ b/modules/util/keepRight/index.js @@ -1,2 +1,2 @@ -export { utilGetErrorDetails } from './keepRight_error'; -export { types } from './errorSchema.json'; \ No newline at end of file +export { parseErrorDescriptions } from './keepRight_error'; +export { errorTypes } from './errorSchema.json'; \ No newline at end of file diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 2c07cb443..683b0ebee 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -1,7 +1,9 @@ +import { t } from '../locale'; import { krError } from '../../osm'; -import { types } from './errorSchema.json'; +import { errorTypes } from './errorSchema.json'; +// TODO: remove these objects, here for reference var keepRightSchema = { 'schema': '', 'id': 0, @@ -46,50 +48,57 @@ var keepRightSchemaFromWeb = { 'title': 'intersections without junctions, highway-waterway' }; -export function utilGetErrorDetails(entity) { +// TODO: clean up description parsing some: remove or ignore spurious characters +export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; // find the matching template from the error schema var errorType = '_' + entity.error_type; - var matchingTemplate = types.errors[errorType] || types.warnings[errorType]; + var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; if (!matchingTemplate) return; // tokenize descriptions var errorDescriptions = entity.description.split(' '); - var schemaDescriptions = matchingTemplate.description.split(' '); + var templateDescriptions = matchingTemplate.description.split(' '); + var parsedDescriptions = []; + var re = new RegExp(/{\$[0-9]}/); - function iterator() { + var commonEntities = ['node', 'way', 'relation']; // TODO: expand this list, or implement a different translation function - var parsedDescription = []; - var re = new RegExp(/{\$[0-9]}/); + templateDescriptions.forEach(function(word, index) { + if (!re.test(word)) return; - schemaDescriptions.forEach(function(word, index) { // TODO: figure out how to get the word and the index in a foreach - if (!re.test(word)) return; + // get the word at this index, and at the next index value + var nextWord = templateDescriptions[index + 1] ? templateDescriptions[index + 1] : null; - // get the word at this index, and at the next index value - var nextWord = schemaDescriptions[index + 1] ? schemaDescriptions[index + 1] : null; + var parsedPhrase = ''; - // also get the word at the same index from the errorDescription + // parse error description words + for (var i = index; i <= errorDescriptions.length - 1; i++) { + if (errorDescriptions[i] !== nextWord) { + var currWord = errorDescriptions[i]; - var parsedPhrase = ''; - - - // while error terms do not equal the next schema term - for (var i = index; i <= errorDescriptions.length - 1; i++) { - if (errorDescriptions[i] !== nextWord) { - parsedPhrase += errorDescriptions[i]; + // if any variables contain common words, like node, way, relation, translate those + if (commonEntities.includes(currWord)) { + currWord = t('keepRight.entities.' + currWord); } - parsedDescription.push(parsedPhrase); - break; + + parsedPhrase += currWord; } - }); - } + // add phrase (or single word) to variable list + parsedDescriptions.push(parsedPhrase); + break; + } + }); - function getCommonWords() { // TODO: implement, see if a variable is a common word like 'node', so that we can translate it before sending it off - } - - - iterator(); -} \ No newline at end of file + return { + var1: parsedDescriptions[0] || '', + var2: parsedDescriptions[1] || '', + var3: parsedDescriptions[2] || '', + var4: parsedDescriptions[3] || '', + var5: parsedDescriptions[4] || '', + var6: parsedDescriptions[4] || '', + }; +} diff --git a/svg/iD-sprite/icons/icon-bolt.svg b/svg/iD-sprite/icons/icon-bolt.svg new file mode 100644 index 000000000..129fa8ac7 --- /dev/null +++ b/svg/iD-sprite/icons/icon-bolt.svg @@ -0,0 +1,6 @@ + + + + + diff --git a/svg/iD-sprite/icons/icon-keepRight.svg b/svg/iD-sprite/icons/icon-keepRight.svg deleted file mode 100644 index 0498225bb..000000000 --- a/svg/iD-sprite/icons/icon-keepRight.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - From 6dbea577217ace8cf13b8760010f89190ca85341 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 3 Aug 2018 16:07:43 -0500 Subject: [PATCH 008/114] added back supported --- modules/svg/keepRight.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 561bf6970..cf2b46339 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -181,6 +181,11 @@ export function svgKeepRight(projection, context, dispatch) { }; + drawKeepRight.supported = function() { + return !!getService(); + }; + + drawKeepRight.visibleErrors = function(_) { if (!arguments.length) return svgKeepRight.visibleErrors; svgKeepRight.visibleErrors.push(_); @@ -194,6 +199,7 @@ export function svgKeepRight(projection, context, dispatch) { }; + init(); return drawKeepRight; } From cbcdfc075c84932d75b2637f78727eaba4641984 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 3 Aug 2018 16:51:35 -0500 Subject: [PATCH 009/114] small UI change to header --- modules/ui/keepRight_header.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index e98282725..a40fdc96e 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -43,9 +43,9 @@ export function uiKeepRightHeader(context) { .append('div') .attr('class', 'kr_error-header-label') .text(function(d) { - return t('keepRight.entities.' + d.object_type); + return t('keepRight.entities.' + d.object_type + ' '); }) - .append('div') + .append('span') // .attr('href', getEntityLink()) // TODO: add / remove link if entity is/isn't in the graph .text(function(d) { return d.object_id; }); } From 805dd394c984fa1435eeef57999ad92101226d1b Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Mon, 6 Aug 2018 12:47:51 -0500 Subject: [PATCH 010/114] added link for object in header --- css/65_data.css | 3 +- data/core.yaml | 8 +++-- dist/locales/en.json | 10 ++++-- modules/ui/keepRight_details.js | 20 +++++++++-- modules/ui/keepRight_editor.js | 2 +- modules/ui/keepRight_header.js | 43 +++++++++++++++++++---- modules/ui/map_data.js | 2 +- modules/util/keepRight/keepRight_error.js | 2 +- 8 files changed, 71 insertions(+), 19 deletions(-) diff --git a/css/65_data.css b/css/65_data.css index af60c2b66..ba85cc533 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -337,13 +337,14 @@ color: #b07f7e; } -.kr_error-details-description { +.kr_error-details-title { text-align: left; margin-bottom: 20px; } .kr_error-details-description { text-align: left; + margin-bottom: 10px; } .QA-buttons { diff --git a/data/core.yaml b/data/core.yaml index 2d2eeb0b9..55b4a3286 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -665,6 +665,10 @@ en: node: node way: way relation: relation + highway: highway + cycleway: cycleway + waterway: waterway + riverbank: riverbank errorTypes: errors: _30: @@ -689,10 +693,10 @@ en: description: 'missing tags' tooltip: 'This {var1} has an empty tag: {var2}' _71: - description: '' + description: 'way without tags' tooltip: 'This way has no tags' _72: - description: '' + description: 'node without tags' tooltip: 'This node is not member of any way and doesn''t have any tags' _90: description: 'motorways without ref' diff --git a/dist/locales/en.json b/dist/locales/en.json index 24dfbfc72..60e6beaf6 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -803,7 +803,11 @@ "entities": { "node": "node", "way": "way", - "relation": "relation" + "relation": "relation", + "highway": "highway", + "cycleway": "cycleway", + "waterway": "waterway", + "riverbank": "riverbank" }, "errorTypes": { "errors": { @@ -836,11 +840,11 @@ "tooltip": "This {var1} has an empty tag: {var2}" }, "_71": { - "description": "", + "description": "way without tags", "tooltip": "This way has no tags" }, "_72": { - "description": "", + "description": "node without tags", "tooltip": "This node is not member of any way and doesn't have any tags" }, "_90": { diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 646609b61..ead79998e 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,8 +1,9 @@ import { t } from '../util/locale'; import { parseErrorDescriptions, errorTypes } from '../util'; +import { select as d3_select } from 'd3-selection'; -export function uiKeepRightDetails() { +export function uiKeepRightDetails(context) { var _error; var _template; var _templateErrorType; @@ -11,6 +12,8 @@ export function uiKeepRightDetails() { var _parent_error_type = ''; var _titleBase; + var _links; + function initDetails() { if (errorTypes.errors['_' + _error.error_type]) { @@ -18,7 +21,7 @@ export function uiKeepRightDetails() { _template = errorTypes.errors[_templateErrorType]; _category = 'errors'; } else if (errorTypes.warnings[_templateErrorType]) { - _template = errorTypes.errors[_templateErrorType]; + _template = errorTypes.warnings[_templateErrorType]; _category = 'warnings'; } else { return; } @@ -54,6 +57,7 @@ export function uiKeepRightDetails() { .append('div') .attr('class', 'kr_error-details kr_error-details-container'); + // title var title = detailsEnter .append('div') @@ -68,7 +72,7 @@ export function uiKeepRightDetails() { // if this is a subtype, append it's parent title if (_parent_error_type) { - title = t(_titleBase + _parent_error_type + '.description' + ':\n'); + title = t(_titleBase + _parent_error_type + '.description') + ':\n'; } // append title @@ -79,6 +83,7 @@ export function uiKeepRightDetails() { return title; }); + // description var description = detailsEnter .append('div') @@ -90,9 +95,18 @@ export function uiKeepRightDetails() { description .append('div') + .attr('class', 'kr_error-details-description-text') .text(function(d) { return t(_titleBase + _templateErrorType + '.tooltip', parseErrorDescriptions(d)); }); + + // TODO: add links to ids in description + // d3_select('.kr_error-details-description-text').enter() + // .append('span') + // .append('a') + // .text(function(d) { return d.object_id; }) + // .on('click', function() { console.log('hi'); }); + } diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 9002dc4ba..18319539a 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -25,7 +25,7 @@ import { export function uiKeepRightEditor(context) { var dispatch = d3_dispatch('change'); var keepRightComment = uiKeepRightComment(); - var keepRightDetails = uiKeepRightDetails(); + var keepRightDetails = uiKeepRightDetails(context); var keepRightHeader = uiKeepRightHeader(context); var _error; diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index a40fdc96e..770c9be40 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -1,14 +1,44 @@ import { t } from '../util/locale'; import { svgIcon } from '../svg'; +import { event as d3_event } from 'd3-selection'; +import { geoChooseEdge } from '../geo'; +import { modeSelect } from '../modes'; export function uiKeepRightHeader(context) { var _error; - function getEntityLink() { + function clickLink() { + var d = {}; - var url = context.connection().entityURL(context.entity(_error.object_id)); + var entityType = + _error.object_type === 'node' ? 'n' : + _error.object_type === 'way' ? 'w' : + _error.object_type === 'relation' ? 'r' : null; + + // if an entity has been loaded in the graph, select the entity + if (context.hasEntity(entityType + _error.object_id)) { + d = context.hasEntity(entityType + _error.object_id); + } + + d3_event.preventDefault(); + if (d.location) { + context.map().centerZoom([d.location[1], d.location[0]], 19); + } + else if (d.entity) { + if (d.entity.type === 'node') { + context.map().center(d.entity.loc); + } else if (d.entity.type === 'way') { + var center = context.projection(context.map().center()); + var edge = geoChooseEdge(context.childNodes(d.entity), center, context.projection); + context.map().center(edge.loc); + } + context.enter(modeSelect(context, [d.entity.id])); + } else { + // TODO: turn on osm layer + context.zoomToEntity(d.id); + } } @@ -42,12 +72,11 @@ export function uiKeepRightHeader(context) { headerEnter .append('div') .attr('class', 'kr_error-header-label') - .text(function(d) { - return t('keepRight.entities.' + d.object_type + ' '); - }) + .text(function(d) { return t('keepRight.entities.' + d.object_type) + ' '; }) .append('span') - // .attr('href', getEntityLink()) // TODO: add / remove link if entity is/isn't in the graph - .text(function(d) { return d.object_id; }); + .append('a') + .text(function(d) { return d.object_id; }) + .on('click', clickLink); } diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index 1132957c2..429fa4f16 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -722,7 +722,7 @@ export function uiMapData(context) { // context.errors() // .on('change.map_data-update', update); // TODO: add errors list to context? - +'' update(); setFill(_fillSelected); diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 683b0ebee..c9a73047f 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -64,7 +64,7 @@ export function parseErrorDescriptions(entity) { var parsedDescriptions = []; var re = new RegExp(/{\$[0-9]}/); - var commonEntities = ['node', 'way', 'relation']; // TODO: expand this list, or implement a different translation function + var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; // TODO: expand this list, or implement a different translation function templateDescriptions.forEach(function(word, index) { if (!re.test(word)) return; From 1335549ad3d9e2916a25619081b242ed5b493461 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Thu, 9 Aug 2018 14:19:01 -0600 Subject: [PATCH 011/114] cleaned map data UI; commented sub-layer filtering --- css/20_map.css | 8 + css/65_data.css | 4 +- data/core.yaml | 283 ++++++++++++++++- dist/locales/en.json | 359 +++++++++++++++++++++- modules/services/keepRight.js | 1 - modules/svg/keepRight.js | 39 ++- modules/ui/keepRight_details.js | 8 +- modules/ui/keepRight_editor.js | 14 +- modules/ui/keepRight_header.js | 2 +- modules/ui/map_data.js | 133 +++++--- modules/util/keepRight/keepRight_error.js | 2 +- 11 files changed, 783 insertions(+), 70 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index 9ded521e0..0200a8317 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -75,8 +75,11 @@ pointer-events: none !important; } +/* NOTE: when more QA layers are added, replace kr_error with generic QA layer selector */ +/* points, notes & QA */ /* points & notes */ +g.kr_error .stroke, g.note .stroke { stroke: #222; stroke-width: 1; @@ -84,6 +87,7 @@ g.note .stroke { opacity: 0.6; } +g.kr_error.active .stroke, g.note.active .stroke { stroke: #222; stroke-width: 1; @@ -97,6 +101,7 @@ g.point .stroke { fill: #fff; } +g.kr_error .shadow, g.point .shadow, g.note .shadow { fill: none; @@ -105,6 +110,8 @@ g.note .shadow { stroke-opacity: 0; } +g.kr_error.related:not(.selected) .shadow, +g.kr_error.hover:not(.selected) .shadow, g.note.related:not(.selected) .shadow, g.note.hover:not(.selected) .shadow, g.point.related:not(.selected) .shadow, @@ -112,6 +119,7 @@ g.point.hover:not(.selected) .shadow { stroke-opacity: 0.5; } +g.kr_error.selected .shadow, g.note.selected .shadow, g.point.selected .shadow { stroke-opacity: 0.7; diff --git a/css/65_data.css b/css/65_data.css index ba85cc533..ce67cf006 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -20,9 +20,7 @@ } .note-header-icon .note-shadow, -.layer-notes .note .note-shadow, -.kr_error-header-icon .kr_error-shadow, -.layer-keepRight .kr_error .kr_error-shadow { +.layer-notes .note .note-shadow { color: #000; } diff --git a/data/core.yaml b/data/core.yaml index 55b4a3286..2cd79b3aa 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -486,7 +486,9 @@ en: zoom: Zoom to data fill_area: Fill Areas map_features: Map Features - QA: QA + QA: + title: Quality Assurance + keepRight: KeepRight autohidden: "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." osmhidden: "These features have been automatically hidden because the OpenStreetMap layer is hidden." feature: @@ -912,6 +914,285 @@ en: _390: description: 'missing tracktype' tooltip: This track doesn't have a tracktype + gpx: + local_layer: "Add a GPX" + drag_drop: "Drag and drop a .gpx, .geojson or .kml file on the page, or click the button to the right to browse" + zoom: "Zoom to layer" + browse: "Browse for a file" + mvt: + local_layer: "Add a MVT" + drag_drop: "Drag and drop a .mvt or .pbf file on the page, or click the button to the right to browse" + zoom: "Zoom to layer" + browse: "Browse for a file" + QA: + keepRight: + tooltip: automatically detected errors from keepright.at + description: Keep Right + title: Edit Error + detail_title: Error + detail_description: Description + inputPlaceholder: Enter a comment to share with other users. + newComment: New Comment + upload_explanation: Your comments will be publicly visible to all keepRight.at users. + upload_explanation_with_user: "Your comments as {user} will be publicly visible to all keepRight.at users." + resolve_comment: Comment and Resolve + ignore_comment: Comment and Ignore + resolve: Resolve + ignore: Ignore + toggle-on: All on + toggle-off: All off + entities: + node: node + way: way + relation: relation + highway: highway + cycleway: cycleway + waterway: waterway + riverbank: riverbank + errorTypes: + errors: + _30: + description: 'non-closed_areas' + tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop' + _40: + description: 'dead-ended one-ways' + tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way' + _41: + description: '' + tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way' + _42: + description: '' + tooltip: 'This node cannot be reached because one-ways only lead away from here' + _43: + description: '' + tooltip: 'You cannot escape from this node because one-ways only lead to here' + _50: + description: 'almost-junctions' + tooltip: 'This node is very close but not connected to way #{var1}' + _70: + description: 'missing tags' + tooltip: 'This {var1} has an empty tag: {var2}' + _71: + description: 'way without tags' + tooltip: 'This way has no tags' + _72: + description: 'node without tags' + tooltip: 'This node is not member of any way and doesn''t have any tags' + _90: + description: 'motorways without ref' + tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' + _100: + description: 'places of worship without religion' + tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag' + _110: + description: 'point of interest without name' + tooltip: 'This node is tagged as {var1} and therefore needs a name tag' + _120: + description: 'ways without nodes' + tooltip: 'This way has just one single node' + _130: + description: 'floating islands' + tooltip: 'This way is not connected to the rest of the map' + _150: + description: 'railway crossing without tag' + tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + _160: + description: 'wrongly used railway tag' + tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + _170: + description: 'FIXME tagged items' + tooltip: '{var1}' + _180: + description: 'relations without type' + tooltip: 'This relation has no type tag which is mandatory for relations' + _190: + description: 'intersections without junctions' + tooltip: 'Finds way crossings on same layer without common node as a junction' + _191: + description: 'highway-highway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _192: + description: 'highway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _193: + description: 'highway-riverbank' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _194: + description: 'waterway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _195: + description: 'cycleway-cycleway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _196: + description: 'highway-cycleway' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + _197: + description: 'cycleway-waterway' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _198: + description: 'cycleway-riverbank' + tooltip: 'This {var1} intersects the {var2} #{var3}' + _200: + description: 'overlapping ways' + tooltip: 'Finds overlapping ways on same layer' + _201: + description: 'highway-highway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _202: + description: 'highway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _203: + description: 'highway-riverbank' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _204: + description: 'waterway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _205: + description: 'cycleway-cycleway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _206: + description: 'highway-cycleway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _207: + description: 'cycleway-waterway' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _208: + description: 'cycleway-riverbank' + tooltip: 'This {var1} overlaps the {var2} #{var3}' + _210: + description: 'loopings' + tooltip: 'These errors contain self intersecting ways' + _211: + description: '' + tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' + _212: + description: '' + tooltip: 'This way has only two different nodes and contains one of them more than once' + _220: + description: 'misspelled tags' + tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' + _221: + description: '' + tooltip: 'The key of this {var1} tag is key {var2}' + _230: + description: 'layer conflicts' + tooltip: '' + _231: + description: 'mixed layers intersection' + tooltip: 'This node is a junction of ways on different layers: {var1}' + _232: + description: 'strange layers' + tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' + _270: + description: 'motorways connected directly' + tooltip: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' + _280: + description: 'boundaries' + tooltip: '' + _281: + description: 'missing name' + tooltip: 'This boundary has no name' + _282: + description: 'missing admin level' + tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' + _283: + description: 'no closed loop' + tooltip: 'The boundary of {var1} is not closed-loop' + _284: + description: 'splitting boundary' + tooltip: 'The boundary of {var1} splits here' + _285: + description: 'admin_level too high' + tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + _290: + description: 'restrictions' + tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' + _291: + description: 'missing type' + tooltip: 'This turn-restriction has no known restriction type' + _292: + description: 'missing from way' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _293: + description: 'missing to way' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + _294: + description: 'from or to not a way' + tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}' + _295: + description: 'via is not on the way ends' + tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' + _296: + description: 'wrong restriction angle' + tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' + _297: + description: 'wrong direction of to member' + tooltip: 'wrong direction of to way {var1}' + _298: + description: 'already restricted by oneway' + tooltip: 'entry already prohibited by oneway tag on {var1}' + _310: + description: 'roundabouts' + tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' + _311: + description: 'not closed loop' + tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + _312: + description: 'wrong direction' + tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + _313: + description: 'faintly connected' + tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' + _320: + description: '*_link connections' + tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' + _350: + description: 'bridge-tags' + tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' + _370: + description: 'doubled places' + tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' + _380: + description: 'non-physical use of sport-tag' + tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' + _400: + description: 'geometry glitches' + tooltip: '' + _401: + description: 'missing turn restriction' + tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' + _402: + description: 'impossible angles' + tooltip: 'this way bends in a very sharp angle here' + _410: + description: 'website' + tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' + _411: + description: 'http error' + tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' + _412: + description: 'domain hijacking' + tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' + _413: + description: 'non-match' + tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' + warnings: + _20: + description: 'multiple nodes on the same spot' + tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}' + _60: + description: 'depreciated tags' + tooltip: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' + _300: + description: 'missing maxspeed' + tooltip: 'missing maxspeed tag' + _360: + description: 'language unknown' + tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' + _390: + description: 'missing tracktype' + tooltip: This track doesn't have a tracktype +>>>>>>> cleaned map data UI; commented sub-layer filtering streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index 60e6beaf6..6a74c08d4 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -591,7 +591,10 @@ }, "fill_area": "Fill Areas", "map_features": "Map Features", - "QA": "QA", + "QA": { + "title": "Quality Assurance", + "keepRight": "KeepRight" + }, "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them.", "osmhidden": "These features have been automatically hidden because the OpenStreetMap layer is hidden." }, @@ -1127,11 +1130,357 @@ }, "_360": { "description": "language unknown", - "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}", + "QA": { + "keepRight": { + "tooltip": "automatically detected errors from keepright.at", + "description": "Keep Right", + "title": "Edit Error", + "detail_title": "Error", + "detail_description": "Description", + "inputPlaceholder": "Enter a comment to share with other users.", + "newComment": "New Comment", + "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", + "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all keepRight.at users.", + "resolve_comment": "Comment and Resolve", + "ignore_comment": "Comment and Ignore", + "resolve": "Resolve", + "ignore": "Ignore", + "toggle-on": "All on", + "toggle-off": "All off", + "entities": { + "node": "node", + "way": "way", + "relation": "relation", + "highway": "highway", + "cycleway": "cycleway", + "waterway": "waterway", + "riverbank": "riverbank" + }, + "errorTypes": { + "errors": { + "_30": { + "description": "non-closed_areas", + "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop" + }, + "_40": { + "description": "dead-ended one-ways", + "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way" + }, + "_41": { + "description": "", + "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way" + }, + "_42": { + "description": "", + "tooltip": "This node cannot be reached because one-ways only lead away from here" + }, + "_43": { + "description": "", + "tooltip": "You cannot escape from this node because one-ways only lead to here" + }, + "_50": { + "description": "almost-junctions", + "tooltip": "This node is very close but not connected to way #{var1}" + }, + "_70": { + "description": "missing tags", + "tooltip": "This {var1} has an empty tag: {var2}" + }, + "_71": { + "description": "way without tags", + "tooltip": "This way has no tags" + }, + "_72": { + "description": "node without tags", + "tooltip": "This node is not member of any way and doesn't have any tags" + }, + "_90": { + "description": "motorways without ref", + "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + }, + "_100": { + "description": "places of worship without religion", + "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag" + }, + "_110": { + "description": "point of interest without name", + "tooltip": "This node is tagged as {var1} and therefore needs a name tag" + }, + "_120": { + "description": "ways without nodes", + "tooltip": "This way has just one single node" + }, + "_130": { + "description": "floating islands", + "tooltip": "This way is not connected to the rest of the map" + }, + "_150": { + "description": "railway crossing without tag", + "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + }, + "_160": { + "description": "wrongly used railway tag", + "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + }, + "_170": { + "description": "FIXME tagged items", + "tooltip": "{var1}" + }, + "_180": { + "description": "relations without type", + "tooltip": "This relation has no type tag which is mandatory for relations" + }, + "_190": { + "description": "intersections without junctions", + "tooltip": "Finds way crossings on same layer without common node as a junction" + }, + "_191": { + "description": "highway-highway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_192": { + "description": "highway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_193": { + "description": "highway-riverbank", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_194": { + "description": "waterway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_195": { + "description": "cycleway-cycleway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_196": { + "description": "highway-cycleway", + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + }, + "_197": { + "description": "cycleway-waterway", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_198": { + "description": "cycleway-riverbank", + "tooltip": "This {var1} intersects the {var2} #{var3}" + }, + "_200": { + "description": "overlapping ways", + "tooltip": "Finds overlapping ways on same layer" + }, + "_201": { + "description": "highway-highway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_202": { + "description": "highway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_203": { + "description": "highway-riverbank", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_204": { + "description": "waterway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_205": { + "description": "cycleway-cycleway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_206": { + "description": "highway-cycleway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_207": { + "description": "cycleway-waterway", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_208": { + "description": "cycleway-riverbank", + "tooltip": "This {var1} overlaps the {var2} #{var3}" + }, + "_210": { + "description": "loopings", + "tooltip": "These errors contain self intersecting ways" + }, + "_211": { + "description": "", + "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" + }, + "_212": { + "description": "", + "tooltip": "This way has only two different nodes and contains one of them more than once" + }, + "_220": { + "description": "misspelled tags", + "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}" + }, + "_221": { + "description": "", + "tooltip": "The key of this {var1} tag is key {var2}" + }, + "_230": { + "description": "layer conflicts", + "tooltip": "" + }, + "_231": { + "description": "mixed layers intersection", + "tooltip": "This node is a junction of ways on different layers: {var1}" + }, + "_232": { + "description": "strange layers", + "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" + }, + "_270": { + "description": "motorways connected directly", + "tooltip": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + }, + "_280": { + "description": "boundaries", + "tooltip": "" + }, + "_281": { + "description": "missing name", + "tooltip": "This boundary has no name" + }, + "_282": { + "description": "missing admin level", + "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + }, + "_283": { + "description": "no closed loop", + "tooltip": "The boundary of {var1} is not closed-loop" + }, + "_284": { + "description": "splitting boundary", + "tooltip": "The boundary of {var1} splits here" + }, + "_285": { + "description": "admin_level too high", + "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + }, + "_290": { + "description": "restrictions", + "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" + }, + "_291": { + "description": "missing type", + "tooltip": "This turn-restriction has no known restriction type" + }, + "_292": { + "description": "missing from way", + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_293": { + "description": "missing to way", + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + }, + "_294": { + "description": "from or to not a way", + "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}" + }, + "_295": { + "description": "via is not on the way ends", + "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})" + }, + "_296": { + "description": "wrong restriction angle", + "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" + }, + "_297": { + "description": "wrong direction of to member", + "tooltip": "wrong direction of to way {var1}" + }, + "_298": { + "description": "already restricted by oneway", + "tooltip": "entry already prohibited by oneway tag on {var1}" + }, + "_310": { + "description": "roundabouts", + "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" + }, + "_311": { + "description": "not closed loop", + "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + }, + "_312": { + "description": "wrong direction", + "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + }, + "_313": { + "description": "faintly connected", + "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" + }, + "_320": { + "description": "*_link connections", + "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" + }, + "_350": { + "description": "bridge-tags", + "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" + }, + "_370": { + "description": "doubled places", + "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" + }, + "_380": { + "description": "non-physical use of sport-tag", + "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" + }, + "_400": { + "description": "geometry glitches", + "tooltip": "" + }, + "_401": { + "description": "missing turn restriction", + "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" + }, + "_402": { + "description": "impossible angles", + "tooltip": "this way bends in a very sharp angle here" + }, + "_410": { + "description": "website", + "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" + }, + "_411": { + "description": "http error", + "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})" + }, + "_412": { + "description": "domain hijacking", + "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" + }, + "_413": { + "description": "non-match", + "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})" + } }, - "_390": { - "description": "missing tracktype", - "tooltip": "This track doesn't have a tracktype" + "warnings": { + "_20": { + "description": "multiple nodes on the same spot", + "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}" + }, + "_60": { + "description": "depreciated tags", + "tooltip": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" + }, + "_300": { + "description": "missing maxspeed", + "tooltip": "missing maxspeed tag" + }, + "_360": { + "description": "language unknown", + "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + }, + "_390": { + "description": "missing tracktype", + "tooltip": "This track doesn't have a tracktype" + } } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 5a1e3dc5d..008605fb6 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -185,7 +185,6 @@ export default { }); }, - // get all cached errors covering the viewport keepRight: function(projection) { var viewport = projection.clipExtent(); diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index cf2b46339..4692f616e 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -1,5 +1,7 @@ import _throttle from 'lodash-es/throttle'; import { select as d3_select } from 'd3-selection'; + +import { modeBrowse } from '../modes'; import { svgPointTransform } from './index'; import { services } from '../services'; @@ -10,6 +12,13 @@ export function svgKeepRight(projection, context, dispatch) { var layer = d3_select(null); var _keepRight; + function markerPath(selection, klass) { + selection + .attr('class', klass) + .attr('transform', 'translate(-4, -24)') + .attr('d', 'M11.6,6.2H7.1l1.4-5.1C8.6,0.6,8.1,0,7.5,0H2.2C1.7,0,1.3,0.3,1.3,0.8L0,10.2c-0.1,0.6,0.4,1.1,0.9,1.1h4.6l-1.8,7.6C3.6,19.4,4.1,20,4.7,20c0.3,0,0.6-0.2,0.8-0.5l6.9-11.9C12.7,7,12.3,6.2,11.6,6.2z'); + } + function init() { if (svgKeepRight.initialized) return; // run once @@ -34,12 +43,31 @@ export function svgKeepRight(projection, context, dispatch) { var service = getService(); if (!service) return; editOn(); + + layer + .classed('disabled', false) + .style('opacity', 0) + .transition() + .duration(250) + .style('opacity', 1) + .on('end interrupt', function () { + dispatch.call('change'); + }); } function hideLayer() { throttledRedraw.cancel(); editOff(); + + layer + .transition() + .duration(250) + .style('opacity', 0) + .on('end interrupt', function () { + layer.classed('disabled', true); + dispatch.call('change'); + }); } @@ -107,9 +135,9 @@ export function svgKeepRight(projection, context, dispatch) { .attr('ry', 3) .attr('class', 'stroke'); - // kr_errorsEnter - // .append('path') - // .call(markerPath, 'kr_error-shadow'); + kr_errorsEnter + .append('path') + .call(markerPath, 'shadow'); kr_errorsEnter .append('use') @@ -156,7 +184,7 @@ export function svgKeepRight(projection, context, dispatch) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); update(); - var options = { // TODO: change out these options and place as default + var options = { ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413] }; @@ -175,6 +203,9 @@ export function svgKeepRight(projection, context, dispatch) { showLayer(); } else { hideLayer(); + if (context.selectedErrorID()) { + context.enter(modeBrowse(context)); + } } dispatch.call('change'); return this; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index ead79998e..4c1dfd4cc 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -32,7 +32,7 @@ export function uiKeepRightDetails(context) { _parent_error_type = '_' + base_error_type; } - _titleBase = 'keepRight.errorTypes.' + _category + '.'; + _titleBase = 'QA.keepRight.errorTypes.' + _category + '.'; } @@ -64,7 +64,7 @@ export function uiKeepRightDetails(context) { .attr('class', 'kr_error-details-title'); title.append('h4') - .text(function() { return t('keepRight.detail_title'); }); + .text(function() { return t('QA.keepRight.detail_title'); }); title.append('div') .text(function() { @@ -72,7 +72,7 @@ export function uiKeepRightDetails(context) { // if this is a subtype, append it's parent title if (_parent_error_type) { - title = t(_titleBase + _parent_error_type + '.description') + ':\n'; + title = t(_titleBase + _parent_error_type + '.description') + ': \n'; } // append title @@ -91,7 +91,7 @@ export function uiKeepRightDetails(context) { description .append('h4') - .text(function() { return t('keepRight.detail_description'); }); + .text(function() { return t('QA.keepRight.detail_description'); }); description .append('div') diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 18319539a..3e6fcee88 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -49,7 +49,7 @@ export function uiKeepRightEditor(context) { headerEnter .append('h3') - .text(t('keepRight.title')); + .text(t('QA.keepRight.title')); var body = selection.selectAll('.body') @@ -102,13 +102,13 @@ export function uiKeepRightEditor(context) { .append('h4') .attr('class', '.error-save-header') .text(function() { - return t('keepRight.newComment'); + return t('QA.keepRight.newComment'); }); errorSaveEnter .append('textarea') .attr('class', 'new-comment-input') - .attr('placeholder', t('keepRight.inputPlaceholder')) + .attr('placeholder', t('QA.keepRight.inputPlaceholder')) .attr('maxlength', 1000) .property('value', function(d) { return d.newComment; }) .call(utilNoAuto) @@ -201,7 +201,7 @@ export function uiKeepRightEditor(context) { prose = prose.enter() .append('p') .attr('class', 'error-save-prose') - .text(t('keepRight.upload_explanation')) + .text(t('QA.keepRight.upload_explanation')) .merge(prose); osm.userDetails(function(err, user) { @@ -225,7 +225,7 @@ export function uiKeepRightEditor(context) { .attr('target', '_blank'); prose - .html(t('keepRight.upload_explanation_with_user', { user: userLink.html() })); + .html(t('QA.keepRight.upload_explanation_with_user', { user: userLink.html() })); }); } @@ -295,7 +295,7 @@ export function uiKeepRightEditor(context) { .attr('disabled', (hasAuth ? null : true)) .text(function(d) { var andComment = (d.newComment ? '_comment' : ''); - return t('keepRight.resolve' + andComment); + return t('QA.keepRight.resolve' + andComment); }) .on('click.status', function(d) { this.blur(); // avoid keeping focus on the button - #4641 @@ -312,7 +312,7 @@ export function uiKeepRightEditor(context) { .attr('disabled', (hasAuth ? null : true)) .text(function(d) { var andComment = (d.newComment ? '_comment' : ''); - return t('keepRight.ignore' + andComment); + return t('QA.keepRight.ignore' + andComment); }) .on('click.status', function(d) { this.blur(); // avoid keeping focus on the button - #4641 diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 770c9be40..836388a30 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -72,7 +72,7 @@ export function uiKeepRightHeader(context) { headerEnter .append('div') .attr('class', 'kr_error-header-label') - .text(function(d) { return t('keepRight.entities.' + d.object_type) + ' '; }) + .text(function(d) { return t('QA.keepRight.entities.' + d.object_type) + ' '; }) .append('span') .append('a') .text(function(d) { return d.object_id; }) diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index 429fa4f16..316369abd 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -22,7 +22,8 @@ export function uiMapData(context) { var key = t('map_data.key'); var features = context.features().keys(); - var errors = Object.keys(errorTypes.errors); // TODO: add warnings + var QAs = ['keepRight']; + // var errors = Object.keys(errorTypes.errors); // TODO: add warnings var layers = context.layers(); var fills = ['wireframe', 'partial', 'full']; @@ -35,6 +36,7 @@ export function uiMapData(context) { var _fillList = d3_select(null); var _featureList = d3_select(null); var _QAList = d3_select(null); + // var _KeepRightList = d3_select(null); function showsFeature(d) { @@ -43,6 +45,7 @@ export function uiMapData(context) { function autoHiddenFeature(d) { + if (d.type === 'kr_error') return context.errors().autoHidden(d); return context.features().autoHidden(d); } @@ -53,15 +56,31 @@ export function uiMapData(context) { } - function showsError(d) { - // return context.errors().enabled(d); + function showsQA(d) { + + var QAKeys = [d]; + var QALayers = layers.all().filter(function(obj) { return QAKeys.indexOf(obj.id) !== -1; }); + var data = QALayers.filter(function(obj) { return obj.layer.supported(); }); + + function layerSupported(d) { + return d.layer && d.layer.supported(); + } + function layerEnabled(d) { + return layerSupported(d) && d.layer.enabled(); + } + + return layerEnabled(data[0]); + } + // function clickError(d) { - function clickError(d) { - // context.errors().toggle(d); - // update(); - } + // } + + + // function showsError(d) { + + // } function showsFill(d) { @@ -112,7 +131,7 @@ export function uiMapData(context) { function drawPhotoItems(selection) { - var photoKeys = ['streetside', 'mapillary-images', 'mapillary-signs', 'openstreetcam-images', 'keepRight']; + var photoKeys = ['streetside', 'mapillary-images', 'mapillary-signs', 'openstreetcam-images']; var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; }); var data = photoLayers.filter(function(obj) { return obj.layer.supported(); }); @@ -432,37 +451,43 @@ export function uiMapData(context) { var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); var buttonSection = selection.selectAll('.QA-buttons') .data([0]); + // function drawQAButtons(selection) { - // exit - buttonSection.exit() - .remove(); + // var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); - // enter - var buttonEnter = buttonSection.enter() - .append('div') - .attr('class', 'QA-buttons'); + // var buttonSection = selection.selectAll('.QA-buttons') + // .data([0]); - buttonEnter - .append('button') - .attr('class', 'button QA-toggle-on action') - .text(t('keepRight.toggle-on')) - .on('click', function() { - QAButtons.property('checked', true); - dispatch.call('change'); - }); + // // exit + // buttonSection.exit() + // .remove(); - buttonEnter - .append('button') - .attr('class', 'button QA-toggle-off action') - .text(t('keepRight.toggle-off')) - .on('click', function() { - QAButtons.property('checked', false); - dispatch.call('change'); - }); + // // enter + // var buttonEnter = buttonSection.enter() + // .append('div') + // .attr('class', 'QA-buttons'); - buttonSection = buttonSection - .merge(buttonEnter); - } + // buttonEnter + // .append('button') + // .attr('class', 'button QA-toggle-on action') + // .text(t('QA.keepRight.toggle-on')) + // .on('click', function() { + // QAButtons.property('checked', true); + // dispatch.call('change'); + // }); + + // buttonEnter + // .append('button') + // .attr('class', 'button QA-toggle-off action') + // .text(t('QA.keepRight.toggle-off')) + // .on('click', function() { + // QAButtons.property('checked', false); + // dispatch.call('change'); + // }); + + // buttonSection = buttonSection + // .merge(buttonEnter); + // } function drawListItems(selection, data, type, name, change, active) { @@ -483,7 +508,8 @@ export function uiMapData(context) { var tip = t(name + '.' + d + '.tooltip'), key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null); - if (name === 'feature' && autoHiddenFeature(d)) { + + if ((name === 'feature' || name === 'keepRight') && autoHiddenFeature(d)) { var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden'); tip += '
' + msg + '
'; } @@ -514,7 +540,7 @@ export function uiMapData(context) { .selectAll('input') .property('checked', active) .property('indeterminate', function(d) { - return (name === 'feature' && autoHiddenFeature(d)); + return ((name === 'feature' || name === 'keepRight') && autoHiddenFeature(d)); }); } @@ -552,7 +578,7 @@ export function uiMapData(context) { } - function renderQA(selection) { + function renderQAList(selection) { var container = selection.selectAll('layer-QA') .data([0]); @@ -562,6 +588,16 @@ export function uiMapData(context) { .merge(container); } + // function renderKeepRightList(selection) { + // var container = selection.selectAll('layer-keepRight') + // .data([0]); + + // _KeepRightList = container.enter() + // .append('ul') + // .attr('class', 'layer-list layer-keepRight') + // .merge(container); + // } + function update() { _dataLayerContainer @@ -577,9 +613,12 @@ export function uiMapData(context) { .call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature); _QAList - .call(drawListItems, errors, 'checkbox', 'keepRight.errorTypes.errors', clickError, showsError); - d3_select('.disclosure-wrap-QA') - .call(drawQAButtons); + .call(drawListItems, QAs, 'checkbox', 'QA', function(d) { toggleLayer(d); }, showsQA); + + // _KeepRightList + // .call(drawListItems, errors, 'checkbox', 'QA.keepRight.errorTypes.errors', clickError, showsError); + // d3_select('.disclosure-wrap-QA') + // .call(drawQAButtons); } @@ -711,10 +750,19 @@ export function uiMapData(context) { .append('div') .attr('class', 'map-data-QA') .call(uiDisclosure(context, 'QA', false) - .title(t('map_data.QA')) - .content(renderQA) + .title(t('map_data.QA.title')) + .content(renderQAList) ); + // // adding keepRight sublayers + // QA_list + // .append('div') + // .attr('class', 'keepRight-errors') + // .call(uiDisclosure(context, 'keepRight', false) + // .title(t('map_data.QA.keepRight')) + // .content(renderKeepRightList) + // ); + // add listeners context.features() @@ -722,7 +770,6 @@ export function uiMapData(context) { // context.errors() // .on('change.map_data-update', update); // TODO: add errors list to context? -'' update(); setFill(_fillSelected); diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index c9a73047f..83b899801 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -81,7 +81,7 @@ export function parseErrorDescriptions(entity) { // if any variables contain common words, like node, way, relation, translate those if (commonEntities.includes(currWord)) { - currWord = t('keepRight.entities.' + currWord); + currWord = t('QA.keepRight.entities.' + currWord); } parsedPhrase += currWord; From b5a316df48c6833b72e43162cee359679544032b Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Thu, 9 Aug 2018 15:59:05 -0600 Subject: [PATCH 012/114] fixed: svg icon placement --- modules/svg/keepRight.js | 4 ++-- modules/ui/keepRight_details.js | 2 -- modules/ui/keepRight_header.js | 19 ++++++++++--------- svg/iD-sprite/icons/icon-bolt.svg | 7 +++++-- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 4692f616e..fc84d7677 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -144,8 +144,8 @@ export function svgKeepRight(projection, context, dispatch) { .attr('class', 'kr_error-fill') .attr('width', '20px') .attr('height', '20px') - .attr('x', '-4px') - .attr('y', '-24px') + .attr('x', '-8px') + .attr('y', '-22px') .attr('xlink:href', '#iD-icon-bolt'); // update diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 4c1dfd4cc..34620a5d0 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -12,8 +12,6 @@ export function uiKeepRightDetails(context) { var _parent_error_type = ''; var _titleBase; - var _links; - function initDetails() { if (errorTypes.errors['_' + _error.error_type]) { diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 836388a30..f9a9e021d 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -9,17 +9,17 @@ export function uiKeepRightHeader(context) { var _error; - function clickLink() { + function clickLink(datum) { var d = {}; var entityType = - _error.object_type === 'node' ? 'n' : - _error.object_type === 'way' ? 'w' : - _error.object_type === 'relation' ? 'r' : null; + datum.object_type === 'node' ? 'n' : + datum.object_type === 'way' ? 'w' : + datum.object_type === 'relation' ? 'r' : null; // if an entity has been loaded in the graph, select the entity - if (context.hasEntity(entityType + _error.object_id)) { - d = context.hasEntity(entityType + _error.object_id); + if (context.hasEntity(entityType + datum.object_id)) { + d = context.hasEntity(entityType + datum.object_id); } d3_event.preventDefault(); @@ -36,8 +36,9 @@ export function uiKeepRightHeader(context) { } context.enter(modeSelect(context, [d.entity.id])); } else { - // TODO: turn on osm layer - context.zoomToEntity(d.id); + context.layers().layer('osm').enabled(true); + context.zoomToEntity(entityType + datum.object_id); + // TODO: select entity that has been zoomed to } } @@ -76,7 +77,7 @@ export function uiKeepRightHeader(context) { .append('span') .append('a') .text(function(d) { return d.object_id; }) - .on('click', clickLink); + .on('click', function(d) { clickLink(d); } ); } diff --git a/svg/iD-sprite/icons/icon-bolt.svg b/svg/iD-sprite/icons/icon-bolt.svg index 129fa8ac7..6eb402946 100644 --- a/svg/iD-sprite/icons/icon-bolt.svg +++ b/svg/iD-sprite/icons/icon-bolt.svg @@ -1,6 +1,9 @@ - + + + + From d095de08fe23d9060baa969a78e15b36e1c69b58 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 10 Aug 2018 11:01:31 -0600 Subject: [PATCH 013/114] updated comment UI, added stub for POST; TODO: finish links & POST --- css/65_data.css | 7 ++ data/core.yaml | 5 +- dist/locales/en.json | 5 +- modules/services/keepRight.js | 29 ++++++- modules/ui/keepRight_comment.js | 95 ++--------------------- modules/ui/keepRight_details.js | 1 + modules/ui/keepRight_editor.js | 53 +++++-------- modules/ui/keepRight_header.js | 2 +- modules/util/keepRight/keepRight_error.js | 10 +++ 9 files changed, 83 insertions(+), 124 deletions(-) diff --git a/css/65_data.css b/css/65_data.css index ce67cf006..023f61fc1 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -175,6 +175,13 @@ } /* KeepRight */ +.kr_error-comment-container, +.kr_error-details-container { + background: #ececec; + padding: 10px 10px; + border-radius: 8px; + margin-top: 20px; +} .kr_error_type_30 { color: #ddb87d; diff --git a/data/core.yaml b/data/core.yaml index 2cd79b3aa..065487d54 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -931,8 +931,11 @@ en: title: Edit Error detail_title: Error detail_description: Description - inputPlaceholder: Enter a comment to share with other users. + comment_header: Comment + newInputPlaceholder: Enter a comment to share with other users. + updateInputPlaceholder: Update the comment above to share with other users. newComment: New Comment + updateComment: Update Comment upload_explanation: Your comments will be publicly visible to all keepRight.at users. upload_explanation_with_user: "Your comments as {user} will be publicly visible to all keepRight.at users." resolve_comment: Comment and Resolve diff --git a/dist/locales/en.json b/dist/locales/en.json index 6a74c08d4..d8586a2ec 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1138,8 +1138,11 @@ "title": "Edit Error", "detail_title": "Error", "detail_description": "Description", - "inputPlaceholder": "Enter a comment to share with other users.", + "comment_header": "Comment", + "newInputPlaceholder": "Enter a comment to share with other users.", + "updateInputPlaceholder": "Update the comment above to share with other users.", "newComment": "New Comment", + "updateComment": "Update Comment", "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all keepRight.at users.", "resolve_comment": "Comment and Resolve", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 008605fb6..114bb119f 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -26,7 +26,7 @@ var _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()} var _off; var _keepRightZoom = 16; -var apiBase = 'https://www.keepright.at/export.php?'; +var apiBase = 'https://www.keepright.at/'; function abortRequest(i) { @@ -91,6 +91,7 @@ export default { var that = this; var path = apiBase + + 'export.php?' + 'format=' + options.format + '&ch=' + options.ch.join() + '&'; @@ -185,6 +186,32 @@ export default { }); }, + postKeepRightUpdate: function(d, callback) { + // TODO: check if a user is authenticated + // if (!this.authenticated()) { + // return callback({ message: 'Not Authenticated', status: -3 }, d); + // } + // if (_keepRightCache.inflightPost[d.id]) { + // return callback({ message: 'Error update already inflight', status: -2 }, d); + // } + + var path = apiBase + 'comment.php?'; + if (d.state) { path += '&st=' + d.state; } + if (d.newComment) { path += '&' + utilQsString({'co': d.newComment }); } + + path += '&schema=' + d.schema + '&id=' + d.error_id; + + d3_request(path) + .mimeType('application/json') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .post(function(err, data) { + console.log('error:', err); + console.log('data: ', data); + }); + }, + // get all cached errors covering the viewport keepRight: function(projection) { var viewport = projection.clipExtent(); diff --git a/modules/ui/keepRight_comment.js b/modules/ui/keepRight_comment.js index 7977d8fdc..5893a039a 100644 --- a/modules/ui/keepRight_comment.js +++ b/modules/ui/keepRight_comment.js @@ -15,98 +15,19 @@ export function uiKeepRightComment() { var comment = selection.selectAll('.comments-container') .data([0]); - comment = comment.enter() + var comment_details = comment.enter() .append('div') - .attr('class', 'comments-container') - .merge(comment); + .attr('class', 'kr_error-comment-container'); - var commentEnter = comment.selectAll('.comment') - .data(_error.comment) - .enter() + comment_details + .append('h4') + .text(t('QA.keepRight.comment_header')); + + comment_details .append('div') - .attr('class', 'comment'); - - commentEnter - .append('div') - .attr('class', function(d) { return 'comment-avatar user-' + d.uid; }) - .call(svgIcon('#iD-icon-avatar', 'comment-avatar-icon')); - - var mainEnter = commentEnter - .append('div') - .attr('class', 'comment-main'); - - var metadataEnter = mainEnter - .append('div') - .attr('class', 'comment-metadata'); - - metadataEnter - .append('div') - .attr('class', 'comment-author') - .each(function(d) { - var selection = d3_select(this); - var osm = services.osm; - if (osm && d.user) { - selection = selection - .append('a') - .attr('class', 'comment-author-link') - .attr('href', osm.userURL(d.user)) - .attr('tabindex', -1) - .attr('target', '_blank'); - } - selection - .text(function(d) { return d.user || t('note.anonymous'); }); - }); - - metadataEnter - .append('div') - .attr('class', 'comment-date') - .text(function(d) { return d.action + ' ' + localeDateString(d.date); }); - - mainEnter - .append('div') - .attr('class', 'comment-text') - .html(function(d) { return d.html; }); - - comment - .call(replaceAvatars); + .text(_error.comment); } - - function replaceAvatars(selection) { - var osm = services.osm; - if (!osm) return; - - var uids = {}; // gather uids in the comment thread - _error.comment.forEach(function(d) { - if (d.uid) uids[d.uid] = true; - }); - - Object.keys(uids).forEach(function(uid) { - osm.loadUser(uid, function(err, user) { - if (!user || !user.image_url) return; - - selection.selectAll('.comment-avatar.user-' + uid) - .html('') - .append('img') - .attr('class', 'icon comment-avatar-icon') - .attr('src', user.image_url) - .attr('alt', user.display_name); - }); - }); - } - - - function localeDateString(s) { - if (!s) return null; - var detected = utilDetect(); - var options = { day: 'numeric', month: 'short', year: 'numeric' }; - s = s.replace(/-/g, '/'); // fix browser-specific Date() issues - var d = new Date(s); - if (isNaN(d.getTime())) return null; - return d.toLocaleDateString(detected.locale, options); - } - - keepRightComment.error = function(_) { if (!arguments.length) return _error; _error = _; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 34620a5d0..4ec0e3d0d 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -98,6 +98,7 @@ export function uiKeepRightDetails(context) { return t(_titleBase + _templateErrorType + '.tooltip', parseErrorDescriptions(d)); }); + // var description_text = d3_select('.kr_error-details-description-text').text(); // TODO: add links to ids in description // d3_select('.kr_error-details-description-text').enter() // .append('span') diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 3e6fcee88..84ed15856 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -68,8 +68,8 @@ export function uiKeepRightEditor(context) { .attr('class', 'modal-section keepRight-editor') .merge(editor) .call(keepRightHeader.error(_error)) - // .call(keepRightComment.error(_error)) .call(keepRightDetails.error(_error)) + .call(keepRightComment.error(_error)) .call(errorSaveSection); @@ -101,14 +101,16 @@ export function uiKeepRightEditor(context) { errorSaveEnter .append('h4') .attr('class', '.error-save-header') - .text(function() { - return t('QA.keepRight.newComment'); + .text(function(d) { + return d.comment ? t('QA.keepRight.updateComment') : t('QA.keepRight.newComment'); }); errorSaveEnter .append('textarea') .attr('class', 'new-comment-input') - .attr('placeholder', t('QA.keepRight.inputPlaceholder')) + .attr('placeholder', function(d) { + return d.comment ? t('QA.keepRight.updateInputPlaceholder') : t('QA.keepRight.newInputPlaceholder'); + }) .attr('maxlength', 1000) .property('value', function(d) { return d.newComment; }) .call(utilNoAuto) @@ -276,35 +278,21 @@ export function uiKeepRightEditor(context) { dispatch.call('change'); }); - buttonSection.select('.save-button') // select and propagate data - .attr('disabled', function(d) { - return (hasAuth && d.status === 'open' && d.newComment) ? null : true; - }) - .on('click.save', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var keepRight = services.keepRight; - if (keepRight) { - // TODO: handle posting updates - // keepRight.postKeepRightCreate(d, function(err, error) { - // dispatch.call('change', error); - // }); - } - }); - buttonSection.select('.resolve-button') // select and propagate data .attr('disabled', (hasAuth ? null : true)) .text(function(d) { + // NOTE: no state is available because keepRight export only exports open errors var andComment = (d.newComment ? '_comment' : ''); return t('QA.keepRight.resolve' + andComment); }) - .on('click.status', function(d) { + .on('click.state', function(d) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - // TODO: handle posting updates - // keepRight.postKeepRightUpdate(d, function(err, error) { - // dispatch.call('change', error); - // }); + d.state = 'ignore_t'; + keepRight.postKeepRightUpdate(d, function(err, error) { + dispatch.call('change', error); + }); } }); @@ -314,14 +302,14 @@ export function uiKeepRightEditor(context) { var andComment = (d.newComment ? '_comment' : ''); return t('QA.keepRight.ignore' + andComment); }) - .on('click.status', function(d) { + .on('click.state', function(d) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - // TODO: handle posting updates - // keepRight.postKeepRightUpdate(d, function(err, error) { - // dispatch.call('change', error); - // }); + d.state = 'ignore'; + keepRight.postKeepRightUpdate(d, function(err, error) { + dispatch.call('change', error); + }); } }); @@ -333,10 +321,9 @@ export function uiKeepRightEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - // TODO: handle posting updates - // keepRight.postKeepRightUpdate(d, function(err, error) { - // dispatch.call('change', error); - // }); + keepRight.postKeepRightUpdate(d, function(err, error) { + dispatch.call('change', error); + }); } }); } diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index f9a9e021d..f24da49f0 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -59,7 +59,7 @@ export function uiKeepRightHeader(context) { var iconEnter = headerEnter .append('div') - .attr('class', function(d) { return 'kr_error-header-icon '; }) + .attr('class', 'kr_error-header-icon') .classed('new', function(d) { return d.id < 0; }); iconEnter diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 83b899801..c415bc6b2 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -52,6 +52,8 @@ var keepRightSchemaFromWeb = { export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; + var _links = []; + // find the matching template from the error schema var errorType = '_' + entity.error_type; var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; @@ -79,6 +81,13 @@ export function parseErrorDescriptions(entity) { if (errorDescriptions[i] !== nextWord) { var currWord = errorDescriptions[i]; + // strip leading # if present + if (currWord.charAt(0) === '#') { + currWord = currWord.slice(1, currWord.length); + // and add index to list of links + _links.push(currWord); + } + // if any variables contain common words, like node, way, relation, translate those if (commonEntities.includes(currWord)) { currWord = t('QA.keepRight.entities.' + currWord); @@ -94,6 +103,7 @@ export function parseErrorDescriptions(entity) { return { + links: _links, var1: parsedDescriptions[0] || '', var2: parsedDescriptions[1] || '', var3: parsedDescriptions[2] || '', From e0d5391f1b4c1b376cb7211c0e3c5434a317b97f Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 10 Aug 2018 14:45:22 -0600 Subject: [PATCH 014/114] added: entity links within descriptions --- modules/ui/keepRight_details.js | 15 ++++---- modules/ui/keepRight_header.js | 41 ++------------------- modules/util/index.js | 3 +- modules/util/keepRight/index.js | 2 +- modules/util/keepRight/keepRight_error.js | 44 +++++++++++++++++++---- modules/util/util.js | 9 +++++ 6 files changed, 59 insertions(+), 55 deletions(-) diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 4ec0e3d0d..9e9cd65c1 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,6 +1,8 @@ import { t } from '../util/locale'; import { parseErrorDescriptions, errorTypes } from '../util'; -import { select as d3_select } from 'd3-selection'; +import { select as d3_selectAll } from 'd3-selection'; + +import { clickLink } from '../util/keepRight'; export function uiKeepRightDetails(context) { @@ -94,17 +96,12 @@ export function uiKeepRightDetails(context) { description .append('div') .attr('class', 'kr_error-details-description-text') - .text(function(d) { + .html(function(d) { return t(_titleBase + _templateErrorType + '.tooltip', parseErrorDescriptions(d)); }); - // var description_text = d3_select('.kr_error-details-description-text').text(); - // TODO: add links to ids in description - // d3_select('.kr_error-details-description-text').enter() - // .append('span') - // .append('a') - // .text(function(d) { return d.object_id; }) - // .on('click', function() { console.log('hi'); }); + description.selectAll('.kr_error_description-id') + .on('click', function() { clickLink(context, this.text); }); } diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index f24da49f0..978008572 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -1,48 +1,13 @@ import { t } from '../util/locale'; +import { utilEntityRoot } from '../util'; +import { clickLink } from '../util/keepRight'; import { svgIcon } from '../svg'; -import { event as d3_event } from 'd3-selection'; -import { geoChooseEdge } from '../geo'; -import { modeSelect } from '../modes'; export function uiKeepRightHeader(context) { var _error; - function clickLink(datum) { - var d = {}; - - var entityType = - datum.object_type === 'node' ? 'n' : - datum.object_type === 'way' ? 'w' : - datum.object_type === 'relation' ? 'r' : null; - - // if an entity has been loaded in the graph, select the entity - if (context.hasEntity(entityType + datum.object_id)) { - d = context.hasEntity(entityType + datum.object_id); - } - - d3_event.preventDefault(); - if (d.location) { - context.map().centerZoom([d.location[1], d.location[0]], 19); - } - else if (d.entity) { - if (d.entity.type === 'node') { - context.map().center(d.entity.loc); - } else if (d.entity.type === 'way') { - var center = context.projection(context.map().center()); - var edge = geoChooseEdge(context.childNodes(d.entity), center, context.projection); - context.map().center(edge.loc); - } - context.enter(modeSelect(context, [d.entity.id])); - } else { - context.layers().layer('osm').enabled(true); - context.zoomToEntity(entityType + datum.object_id); - // TODO: select entity that has been zoomed to - } - } - - function keepRightHeader(selection) { var header = selection.selectAll('.kr_error-header') .data( @@ -77,7 +42,7 @@ export function uiKeepRightHeader(context) { .append('span') .append('a') .text(function(d) { return d.object_id; }) - .on('click', function(d) { clickLink(d); } ); + .on('click', function(d) { clickLink(context, (utilEntityRoot(d.object_type) + d.object_id)); }); } diff --git a/modules/util/index.js b/modules/util/index.js index be877351f..b386875c3 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -4,6 +4,7 @@ export { utilCleanTags } from './clean_tags'; export { utilDisplayName } from './util'; export { utilDisplayNameForPath } from './util'; export { utilDisplayType } from './util'; +export { utilEntityRoot } from './util'; export { utilEditDistance } from './util'; export { utilEntitySelector } from './util'; export { utilEntityOrMemberSelector } from './util'; @@ -13,7 +14,7 @@ export { utilExternalValidationRules } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; -export { errorTypes, parseErrorDescriptions } from './keepRight'; +export { errorTypes, parseErrorDescriptions, clickLink } from './keepRight'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; diff --git a/modules/util/keepRight/index.js b/modules/util/keepRight/index.js index 4a95afaf5..8bd2ca9a6 100644 --- a/modules/util/keepRight/index.js +++ b/modules/util/keepRight/index.js @@ -1,2 +1,2 @@ -export { parseErrorDescriptions } from './keepRight_error'; +export { parseErrorDescriptions, clickLink } from './keepRight_error'; export { errorTypes } from './errorSchema.json'; \ No newline at end of file diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index c415bc6b2..069e56f65 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -1,3 +1,5 @@ +import { event as d3_event } from 'd3-selection'; + import { t } from '../locale'; import { krError } from '../../osm'; @@ -52,8 +54,6 @@ var keepRightSchemaFromWeb = { export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; - var _links = []; - // find the matching template from the error schema var errorType = '_' + entity.error_type; var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; @@ -68,6 +68,23 @@ export function parseErrorDescriptions(entity) { var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; // TODO: expand this list, or implement a different translation function + function fillPlaceholder(d) { + return '' + d + ''; + } + + function getEntityBase(lastWord) { + var result; + commonEntities.forEach(function(entity) { + if (entity.includes(lastWord)) { result = entity; } + return; + }); + + if (result) { + result = result.includes('node') ? 'n' : result.includes('way') ? 'w' : result.includes('relation') ? 'r' : null; + } + return result; + } + templateDescriptions.forEach(function(word, index) { if (!re.test(word)) return; @@ -84,8 +101,17 @@ export function parseErrorDescriptions(entity) { // strip leading # if present if (currWord.charAt(0) === '#') { currWord = currWord.slice(1, currWord.length); - // and add index to list of links - _links.push(currWord); + + // get the entity type of the id + var lastWord = errorDescriptions[i-1]; + var base; + if (lastWord) { base = getEntityBase(lastWord); } + if (!base) { + base = getEntityBase(parsedDescriptions.slice(-1)[0].split(' ').slice(-1)[0]); + } + + // wrap id with linking span + currWord = fillPlaceholder(base + currWord); } // if any variables contain common words, like node, way, relation, translate those @@ -103,12 +129,18 @@ export function parseErrorDescriptions(entity) { return { - links: _links, var1: parsedDescriptions[0] || '', var2: parsedDescriptions[1] || '', var3: parsedDescriptions[2] || '', var4: parsedDescriptions[3] || '', var5: parsedDescriptions[4] || '', - var6: parsedDescriptions[4] || '', + var6: parsedDescriptions[5] || '', }; } + + +export function clickLink(context, id) { + d3_event.preventDefault(); + context.layers().layer('osm').enabled(true); + context.zoomToEntity(id); + } diff --git a/modules/util/util.js b/modules/util/util.js index d9c613263..605378ece 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -121,6 +121,15 @@ export function utilDisplayType(id) { } +export function utilEntityRoot(entityType) { + return { + node: 'n', + way: 'w', + relation: 'r' + }[entityType]; +} + + export function utilStringQs(str) { return str.split('&').reduce(function(obj, pair){ var parts = pair.split('='); From ed34d522ffcb63970f1b2ceea2c31acd2825f538 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Fri, 10 Aug 2018 15:02:19 -0600 Subject: [PATCH 015/114] updated: check entity type from second word as well --- modules/util/keepRight/keepRight_error.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 069e56f65..8bbc647a4 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -99,13 +99,14 @@ export function parseErrorDescriptions(entity) { var currWord = errorDescriptions[i]; // strip leading # if present - if (currWord.charAt(0) === '#') { - currWord = currWord.slice(1, currWord.length); + if (currWord.charAt(0) === '#' || errorDescriptions[i-1] === '(id') { + currWord = currWord.replace(/\D/g,''); // get the entity type of the id var lastWord = errorDescriptions[i-1]; + var secondLastWord = errorDescriptions[i-2]; var base; - if (lastWord) { base = getEntityBase(lastWord); } + if (lastWord) { base = getEntityBase(lastWord) || getEntityBase(secondLastWord); } if (!base) { base = getEntityBase(parsedDescriptions.slice(-1)[0].split(' ').slice(-1)[0]); } From d125c62c78dc18a0c249c2fcb455c534d1741e30 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Mon, 27 Aug 2018 16:24:47 -0600 Subject: [PATCH 016/114] updated: variable and function names, html regex --- modules/services/keepRight.js | 2 +- modules/svg/keepRight.js | 2 +- modules/ui/keepRight_details.js | 3 +- modules/util/keepRight/keepRight_error.js | 34 +++++++++++++---------- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 114bb119f..2889c8ec5 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -83,7 +83,7 @@ export default { _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; }, - loadKeepRight: function(context, projection, options, callback) { + loadKeepRightErrors: function(context, projection, options, callback) { options = _extend({ 'format': 'geojson' }, options); if (_off) return; diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index fc84d7677..33373a116 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -188,7 +188,7 @@ export function svgKeepRight(projection, context, dispatch) { ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413] }; - service.loadKeepRight(context, projection, options, exampleCallback); + service.loadKeepRightErrors(context, projection, options, exampleCallback); } else { editOff(); } diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 9e9cd65c1..f6d7c9967 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -11,11 +11,12 @@ export function uiKeepRightDetails(context) { var _templateErrorType; var _category; var _categoryElements; - var _parent_error_type = ''; + var _parent_error_type; var _titleBase; function initDetails() { + _parent_error_type = ''; if (errorTypes.errors['_' + _error.error_type]) { _templateErrorType = '_' + _error.error_type; _template = errorTypes.errors[_templateErrorType]; diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 8bbc647a4..8a9908384 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -50,7 +50,6 @@ var keepRightSchemaFromWeb = { 'title': 'intersections without junctions, highway-waterway' }; -// TODO: clean up description parsing some: remove or ignore spurious characters export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; @@ -60,11 +59,12 @@ export function parseErrorDescriptions(entity) { if (!matchingTemplate) return; // tokenize descriptions - var errorDescriptions = entity.description.split(' '); - var templateDescriptions = matchingTemplate.description.split(' '); + var errorDescription = entity.description.split(' '); + var templateDescription = matchingTemplate.description.split(' '); var parsedDescriptions = []; - var re = new RegExp(/{\$[0-9]}/); + var variable_re = new RegExp(/{\$[0-9]}/); + var html_re = new RegExp(/<\/[a-z][\s\S]*>/); var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; // TODO: expand this list, or implement a different translation function @@ -85,26 +85,26 @@ export function parseErrorDescriptions(entity) { return result; } - templateDescriptions.forEach(function(word, index) { - if (!re.test(word)) return; + templateDescription.forEach(function(word, index) { + if (!variable_re.test(word)) return; // get the word at this index, and at the next index value - var nextWord = templateDescriptions[index + 1] ? templateDescriptions[index + 1] : null; + var nextWord = templateDescription[index + 1] ? templateDescription[index + 1] : null; var parsedPhrase = ''; // parse error description words - for (var i = index; i <= errorDescriptions.length - 1; i++) { - if (errorDescriptions[i] !== nextWord) { - var currWord = errorDescriptions[i]; + for (var i = index; i <= errorDescription.length - 1; i++) { + if (errorDescription[i] !== nextWord) { + var currWord = errorDescription[i]; - // strip leading # if present - if (currWord.charAt(0) === '#' || errorDescriptions[i-1] === '(id') { + // select just numeric part of id + if (currWord.charAt(0) === '#' || errorDescription[i-1] === '(id') { // NOTE: hacky way of selecting the token before currWord = currWord.replace(/\D/g,''); // get the entity type of the id - var lastWord = errorDescriptions[i-1]; - var secondLastWord = errorDescriptions[i-2]; + var lastWord = errorDescription[i-1]; + var secondLastWord = errorDescription[i-2]; var base; if (lastWord) { base = getEntityBase(lastWord) || getEntityBase(secondLastWord); } if (!base) { @@ -120,9 +120,13 @@ export function parseErrorDescriptions(entity) { currWord = t('QA.keepRight.entities.' + currWord); } + // add phrase (or single word) to variable list parsedPhrase += currWord; } - // add phrase (or single word) to variable list + // if any variables have html, escape them + if (html_re.test(parsedPhrase)) { + parsedPhrase = '\\' + parsedPhrase + '\\'; + } parsedDescriptions.push(parsedPhrase); break; } From 187d1add49d1a74d14f03ce55a19830a9d9b39bf Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Mon, 27 Aug 2018 18:26:26 -0600 Subject: [PATCH 017/114] added: outline for KeepRight icon --- css/80_app.css | 178 +++++++++++++++++++++++++++++- svg/iD-sprite/icons/icon-bolt.svg | 4 +- 2 files changed, 179 insertions(+), 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index c333cbd23..c87e69d25 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2537,7 +2537,9 @@ input.key-trap { border-left: none; } -.note-save { +.note-save, +.keepRight-save, +.kr_error-details { padding: 10px; } @@ -2557,6 +2559,180 @@ input.key-trap { } +/* Keep Right Errors +------------------------------------------------------- */ +.kr_error-header-icon .kr_error .kr_error-fill, +.layer-keepRight .kr_error .kr_error-fill { + stroke: #333; + stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */ +} + +.kr_error_type_40, +.kr_error_type_41, +.kr_error_type_42, +.kr_error_type_43 { + color: #894668; +} + +.kr_error_type_50 { + color: #c827fe; +} + +.kr_error_type_70, +.kr_error_type_71, +.kr_error_type_72 { + color: #74aeaf; +} + +.kr_error_type_90 { + color: #3124af; +} + +.kr_error_type_100 { + color: #9e8e91; +} + +.kr_error_type_110 { + color: #44650b; +} + +.kr_error_type_120 { + color: #99274d; +} + +.kr_error_type_130 { + color: #eb7310; +} + +.kr_error_type_150 { + color: #7218c1; +} + +.kr_error_type_160 { + color: #c903ae; +} + +.kr_error_type_170 { + color: #07d40b; +} + +.kr_error_type_180 { + color: #09ef12; +} + +.kr_error_type_190, +.kr_error_type_191, +.kr_error_type_192, +.kr_error_type_193, +.kr_error_type_194, +.kr_error_type_195, +.kr_error_type_196, +.kr_error_type_197, +.kr_error_type_198 { + color: #e6fcb0; +} + +.kr_error_type_200, +.kr_error_type_201, +.kr_error_type_202, +.kr_error_type_203, +.kr_error_type_204, +.kr_error_type_205, +.kr_error_type_206, +.kr_error_type_207, +.kr_error_type_208 { + color: #71f264; +} + +.kr_error_type_210, +.kr_error_type_211, +.kr_error_type_212 { + color: #4a7601; +} + +.kr_error_type_220, +.kr_error_type_221 { + color: #ef7cf2; +} + +.kr_error_type_230, +.kr_error_type_231, +.kr_error_type_232 { + color: #5f775c; +} + +.kr_error_type_270 { + color: #2aaf92; +} + +.kr_error_type_280, +.kr_error_type_281, +.kr_error_type_282, +.kr_error_type_283, +.kr_error_type_284, +.kr_error_type_285 { + color: #5f47a0; +} + +.kr_error_type_290, +.kr_error_type_291, +.kr_error_type_292, +.kr_error_type_293, +.kr_error_type_294, +.kr_error_type_295, +.kr_error_type_296, +.kr_error_type_297, +.kr_error_type_298 { + color: #9bb2cd; +} + +.kr_error_type_310, +.kr_error_type_311, +.kr_error_type_312, +.kr_error_type_313 { + color: #0550e8; +} + +.kr_error_type_320 { + color: #28d9bb; +} + +.kr_error_type_350 { + color: #4d719c; +} + +.kr_error_type_370 { + color: #ff8fdf; +} + +.kr_error_type_380 { + color: #b3b465; +} + +.kr_error_type_400, +.kr_error_type_401, +.kr_error_type_402 { + color: #b20e36; +} + +.kr_error_type_410, +.kr_error_type_411, +.kr_error_type_412, +.kr_error_type_413 { + color: #b07f7e; +} + +.kr_error-details-title { + text-align: left; + margin-bottom: 20px; +} + +.kr_error-details-description { + text-align: left; + margin-bottom: 10px; +} + + /* Custom Data Editor ------------------------------------------------------- */ .data-header { diff --git a/svg/iD-sprite/icons/icon-bolt.svg b/svg/iD-sprite/icons/icon-bolt.svg index 6eb402946..8078987b2 100644 --- a/svg/iD-sprite/icons/icon-bolt.svg +++ b/svg/iD-sprite/icons/icon-bolt.svg @@ -1,8 +1,8 @@ - + From 84895834f7ff49abcbf1a99b581194d0b149b3af Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Thu, 30 Aug 2018 16:42:35 -0600 Subject: [PATCH 018/114] updated: punctuation and missing descriptions --- css/80_app.css | 4 +- data/core.yaml | 149 +++++++++++----------- dist/locales/en.json | 148 ++++++++++----------- modules/util/keepRight/errorSchema.json | 6 +- modules/util/keepRight/keepRight_error.js | 6 +- 5 files changed, 156 insertions(+), 157 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index c87e69d25..1d601bf0d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2589,7 +2589,7 @@ input.key-trap { } .kr_error_type_100 { - color: #9e8e91; + color: #a80000; } .kr_error_type_110 { @@ -2683,7 +2683,7 @@ input.key-trap { .kr_error_type_296, .kr_error_type_297, .kr_error_type_298 { - color: #9bb2cd; + color: #d1dce7; } .kr_error_type_310, diff --git a/data/core.yaml b/data/core.yaml index 065487d54..1f680ff42 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -956,136 +956,136 @@ en: errors: _30: description: 'non-closed_areas' - tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop' + tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop.' _40: description: 'dead-ended one-ways' - tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way' + tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way.' _41: description: '' - tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way' + tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way.' _42: description: '' - tooltip: 'This node cannot be reached because one-ways only lead away from here' + tooltip: 'This node cannot be reached because one-ways only lead away from here.' _43: description: '' - tooltip: 'You cannot escape from this node because one-ways only lead to here' + tooltip: 'You cannot escape from this node because one-ways only lead to here.' _50: description: 'almost-junctions' - tooltip: 'This node is very close but not connected to way #{var1}' + tooltip: 'This node is very close but not connected to way #{var1}.' _70: description: 'missing tags' - tooltip: 'This {var1} has an empty tag: {var2}' + tooltip: 'This {var1} has an empty tag: {var2}.' _71: description: 'way without tags' - tooltip: 'This way has no tags' + tooltip: 'This way has no tags.' _72: description: 'node without tags' - tooltip: 'This node is not member of any way and doesn''t have any tags' + tooltip: 'This node is not member of any way and doesn''t have any tags.' _90: description: 'motorways without ref' - tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' + tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag.' _100: description: 'places of worship without religion' - tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag' + tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag.' _110: description: 'point of interest without name' - tooltip: 'This node is tagged as {var1} and therefore needs a name tag' + tooltip: 'This node is tagged as {var1} and therefore needs a name tag.' _120: description: 'ways without nodes' - tooltip: 'This way has just one single node' + tooltip: 'This way has just one single node.' _130: description: 'floating islands' - tooltip: 'This way is not connected to the rest of the map' + tooltip: 'This way is not connected to the rest of the map.' _150: description: 'railway crossing without tag' - tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' + tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing.' _160: description: 'wrongly used railway tag' - tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' + tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing.' _170: description: 'FIXME tagged items' tooltip: '{var1}' _180: description: 'relations without type' - tooltip: 'This relation has no type tag which is mandatory for relations' + tooltip: 'This relation has no type tag which is mandatory for relations.' _190: description: 'intersections without junctions' - tooltip: 'Finds way crossings on same layer without common node as a junction' + tooltip: 'Finds way crossings on same layer without common node as a junction.' _191: description: 'highway-highway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _192: description: 'highway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}' + tooltip: 'This {var1} intersects the {var2} #{var3}.' _193: description: 'highway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}' + tooltip: 'This {var1} intersects the {var2} #{var3}.' _194: description: 'waterway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _195: description: 'cycleway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _196: description: 'highway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' + tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _197: description: 'cycleway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}' + tooltip: 'This {var1} intersects the {var2} #{var3}.' _198: description: 'cycleway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}' + tooltip: 'This {var1} intersects the {var2} #{var3}.' _200: description: 'overlapping ways' - tooltip: 'Finds overlapping ways on same layer' + tooltip: 'Finds overlapping ways on same layer.' _201: description: 'highway-highway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _202: description: 'highway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _203: description: 'highway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _204: description: 'waterway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _205: description: 'cycleway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _206: description: 'highway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _207: description: 'cycleway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _208: description: 'cycleway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}' + tooltip: 'This {var1} overlaps the {var2} #{var3}.' _210: description: 'loopings' - tooltip: 'These errors contain self intersecting ways' + tooltip: 'These errors contain self intersecting ways.' _211: description: '' - tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' + tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error.' _212: description: '' - tooltip: 'This way has only two different nodes and contains one of them more than once' + tooltip: 'This way has only two different nodes and contains one of them more than once.' _220: description: 'misspelled tags' - tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' + tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}.' _221: description: '' - tooltip: 'The key of this {var1} tag is key {var2}' + tooltip: 'The key of this {var1} tag is key {var2}.' _230: description: 'layer conflicts' tooltip: '' _231: description: 'mixed layers intersection' - tooltip: 'This node is a junction of ways on different layers: {var1}' + tooltip: 'This node is a junction of ways on different layers: {var1}.' _232: description: 'strange layers' - tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' + tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' _270: description: 'motorways connected directly' tooltip: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' @@ -1094,108 +1094,107 @@ en: tooltip: '' _281: description: 'missing name' - tooltip: 'This boundary has no name' + tooltip: 'This boundary has no name.' _282: description: 'missing admin level' - tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' + tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' _283: description: 'no closed loop' - tooltip: 'The boundary of {var1} is not closed-loop' + tooltip: 'The boundary of {var1} is not closed-loop.' _284: description: 'splitting boundary' - tooltip: 'The boundary of {var1} splits here' + tooltip: 'The boundary of {var1} splits here.' _285: description: 'admin_level too high' - tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' + tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations.' _290: description: 'restrictions' - tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' + tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat.' _291: description: 'missing type' - tooltip: 'This turn-restriction has no known restriction type' + tooltip: 'This turn-restriction has no known restriction type.' _292: description: 'missing from way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' _293: description: 'missing to way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' + tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' _294: description: 'from or to not a way' - tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}' + tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}.' _295: description: 'via is not on the way ends' - tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' + tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2}).' _296: description: 'wrong restriction angle' - tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' + tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?.' _297: description: 'wrong direction of to member' - tooltip: 'wrong direction of to way {var1}' + tooltip: 'wrong direction of to way {var1}.' _298: description: 'already restricted by oneway' - tooltip: 'entry already prohibited by oneway tag on {var1}' + tooltip: 'entry already prohibited by oneway tag on {var1}.' _310: description: 'roundabouts' - tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' + tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.' _311: description: 'not closed loop' - tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' + tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' _312: description: 'wrong direction' - tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' + tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around.' _313: description: 'faintly connected' - tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' + tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three.' _320: description: '*_link connections' - tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' + tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link.' _350: description: 'bridge-tags' - tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' + tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' _370: description: 'doubled places' - tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' + tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand.' _380: description: 'non-physical use of sport-tag' - tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' + tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway.' _400: description: 'geometry glitches' tooltip: '' _401: description: 'missing turn restriction' - tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' + tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}.' _402: description: 'impossible angles' - tooltip: 'this way bends in a very sharp angle here' + tooltip: 'this way bends in a very sharp angle here.' _410: description: 'website' - tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' + tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*.' _411: description: 'http error' - tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' + tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2}).' _412: description: 'domain hijacking' - tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' + tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}".' _413: description: 'non-match' - tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' + tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2}).' warnings: _20: description: 'multiple nodes on the same spot' - tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}' + tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}.' _60: description: 'depreciated tags' tooltip: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' _300: description: 'missing maxspeed' - tooltip: 'missing maxspeed tag' + tooltip: 'missing maxspeed tag.' _360: description: 'language unknown' - tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' + tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}.' _390: description: 'missing tracktype' - tooltip: This track doesn't have a tracktype ->>>>>>> cleaned map data UI; commented sub-layer filtering + tooltip: This track doesn't have a tracktype. streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index d8586a2ec..1a861d74b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1164,67 +1164,67 @@ "errors": { "_30": { "description": "non-closed_areas", - "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop" + "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop." }, "_40": { "description": "dead-ended one-ways", - "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way" + "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way." }, "_41": { "description": "", - "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way" + "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way." }, "_42": { "description": "", - "tooltip": "This node cannot be reached because one-ways only lead away from here" + "tooltip": "This node cannot be reached because one-ways only lead away from here." }, "_43": { "description": "", - "tooltip": "You cannot escape from this node because one-ways only lead to here" + "tooltip": "You cannot escape from this node because one-ways only lead to here." }, "_50": { "description": "almost-junctions", - "tooltip": "This node is very close but not connected to way #{var1}" + "tooltip": "This node is very close but not connected to way #{var1}." }, "_70": { "description": "missing tags", - "tooltip": "This {var1} has an empty tag: {var2}" + "tooltip": "This {var1} has an empty tag: {var2}." }, "_71": { "description": "way without tags", - "tooltip": "This way has no tags" + "tooltip": "This way has no tags." }, "_72": { "description": "node without tags", - "tooltip": "This node is not member of any way and doesn't have any tags" + "tooltip": "This node is not member of any way and doesn't have any tags." }, "_90": { "description": "motorways without ref", - "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag." }, "_100": { "description": "places of worship without religion", - "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag" + "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag." }, "_110": { "description": "point of interest without name", - "tooltip": "This node is tagged as {var1} and therefore needs a name tag" + "tooltip": "This node is tagged as {var1} and therefore needs a name tag." }, "_120": { "description": "ways without nodes", - "tooltip": "This way has just one single node" + "tooltip": "This way has just one single node." }, "_130": { "description": "floating islands", - "tooltip": "This way is not connected to the rest of the map" + "tooltip": "This way is not connected to the rest of the map." }, "_150": { "description": "railway crossing without tag", - "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing." }, "_160": { "description": "wrongly used railway tag", - "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing." }, "_170": { "description": "FIXME tagged items", @@ -1232,99 +1232,99 @@ }, "_180": { "description": "relations without type", - "tooltip": "This relation has no type tag which is mandatory for relations" + "tooltip": "This relation has no type tag which is mandatory for relations." }, "_190": { "description": "intersections without junctions", - "tooltip": "Finds way crossings on same layer without common node as a junction" + "tooltip": "Finds way crossings on same layer without common node as a junction." }, "_191": { "description": "highway-highway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_192": { "description": "highway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}" + "tooltip": "This {var1} intersects the {var2} #{var3}." }, "_193": { "description": "highway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}" + "tooltip": "This {var1} intersects the {var2} #{var3}." }, "_194": { "description": "waterway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_195": { "description": "cycleway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_196": { "description": "highway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" + "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_197": { "description": "cycleway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}" + "tooltip": "This {var1} intersects the {var2} #{var3}." }, "_198": { "description": "cycleway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}" + "tooltip": "This {var1} intersects the {var2} #{var3}." }, "_200": { "description": "overlapping ways", - "tooltip": "Finds overlapping ways on same layer" + "tooltip": "Finds overlapping ways on same layer." }, "_201": { "description": "highway-highway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_202": { "description": "highway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_203": { "description": "highway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_204": { "description": "waterway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_205": { "description": "cycleway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_206": { "description": "highway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_207": { "description": "cycleway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_208": { "description": "cycleway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}" + "tooltip": "This {var1} overlaps the {var2} #{var3}." }, "_210": { "description": "loopings", - "tooltip": "These errors contain self intersecting ways" + "tooltip": "These errors contain self intersecting ways." }, "_211": { "description": "", - "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" + "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error." }, "_212": { "description": "", - "tooltip": "This way has only two different nodes and contains one of them more than once" + "tooltip": "This way has only two different nodes and contains one of them more than once." }, "_220": { "description": "misspelled tags", - "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}" + "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}." }, "_221": { "description": "", - "tooltip": "The key of this {var1} tag is key {var2}" + "tooltip": "The key of this {var1} tag is key {var2}." }, "_230": { "description": "layer conflicts", @@ -1332,11 +1332,11 @@ }, "_231": { "description": "mixed layers intersection", - "tooltip": "This node is a junction of ways on different layers: {var1}" + "tooltip": "This node is a junction of ways on different layers: {var1}." }, "_232": { "description": "strange layers", - "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" + "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange." }, "_270": { "description": "motorways connected directly", @@ -1348,91 +1348,91 @@ }, "_281": { "description": "missing name", - "tooltip": "This boundary has no name" + "tooltip": "This boundary has no name." }, "_282": { "description": "missing admin level", - "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries." }, "_283": { "description": "no closed loop", - "tooltip": "The boundary of {var1} is not closed-loop" + "tooltip": "The boundary of {var1} is not closed-loop." }, "_284": { "description": "splitting boundary", - "tooltip": "The boundary of {var1} splits here" + "tooltip": "The boundary of {var1} splits here." }, "_285": { "description": "admin_level too high", - "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations." }, "_290": { "description": "restrictions", - "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" + "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat." }, "_291": { "description": "missing type", - "tooltip": "This turn-restriction has no known restriction type" + "tooltip": "This turn-restriction has no known restriction type." }, "_292": { "description": "missing from way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}." }, "_293": { "description": "missing to way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" + "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}." }, "_294": { "description": "from or to not a way", - "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}" + "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}." }, "_295": { "description": "via is not on the way ends", - "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})" + "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})." }, "_296": { "description": "wrong restriction angle", - "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" + "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?." }, "_297": { "description": "wrong direction of to member", - "tooltip": "wrong direction of to way {var1}" + "tooltip": "wrong direction of to way {var1}." }, "_298": { "description": "already restricted by oneway", - "tooltip": "entry already prohibited by oneway tag on {var1}" + "tooltip": "entry already prohibited by oneway tag on {var1}." }, "_310": { "description": "roundabouts", - "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" + "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1." }, "_311": { "description": "not closed loop", - "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." }, "_312": { "description": "wrong direction", - "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around." }, "_313": { "description": "faintly connected", - "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" + "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three." }, "_320": { "description": "*_link connections", - "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" + "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link." }, "_350": { "description": "bridge-tags", - "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" + "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." }, "_370": { "description": "doubled places", - "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" + "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand." }, "_380": { "description": "non-physical use of sport-tag", - "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" + "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway." }, "_400": { "description": "geometry glitches", @@ -1440,33 +1440,33 @@ }, "_401": { "description": "missing turn restriction", - "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" + "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}." }, "_402": { "description": "impossible angles", - "tooltip": "this way bends in a very sharp angle here" + "tooltip": "this way bends in a very sharp angle here." }, "_410": { "description": "website", - "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" + "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*." }, "_411": { "description": "http error", - "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})" + "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})." }, "_412": { "description": "domain hijacking", - "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" + "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"." }, "_413": { "description": "non-match", - "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})" + "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})." } }, "warnings": { "_20": { "description": "multiple nodes on the same spot", - "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}" + "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}." }, "_60": { "description": "depreciated tags", @@ -1474,15 +1474,15 @@ }, "_300": { "description": "missing maxspeed", - "tooltip": "missing maxspeed tag" + "tooltip": "missing maxspeed tag." }, "_360": { "description": "language unknown", - "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}." }, "_390": { "description": "missing tracktype", - "tooltip": "This track doesn't have a tracktype" + "tooltip": "This track doesn't have a tracktype." } } } diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index c6688d9d2..3bfd75b72 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -168,7 +168,7 @@ }, "_230": { "title": "layer conflicts", - "description": "" + "description": "Connected ways should be on the same layer. Crossings on intermediate nodes of ways on different layers are obviously wrong. Junctions on end-nodes of ways on different layers are also deprecated, but common practice. So you may ignore this part of the check and switch them off separately. Please note that bridges are set to layer +1, and tunnels to -1, anything else to layer 0 implicitly if no layer tag is present." }, "_231": { "title": "mixed layers intersection", @@ -184,7 +184,7 @@ }, "_280": { "title": "boundaries", - "description": "" + "description": "Administrative Boundaries can be expressed either by tagging ways or by adding them to a relation. They should be closed-loop sequences of ways, they must not self-intersect or split and they must have a name and an admin_level." }, "_281": { "title": "missing name", @@ -276,7 +276,7 @@ }, "_400": { "title": "geometry glitches", - "description": "" + "description": "Impossible sharp angles on highways and junctions. These may be caused by missing turn restrictions on junctions or glitches along the linestring of ways" }, "_401": { "title": "missing turn restriction", diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 8a9908384..8986b3bd5 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -65,6 +65,7 @@ export function parseErrorDescriptions(entity) { var parsedDescriptions = []; var variable_re = new RegExp(/{\$[0-9]}/); var html_re = new RegExp(/<\/[a-z][\s\S]*>/); + var span_re = new RegExp(/<\/span>/); var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; // TODO: expand this list, or implement a different translation function @@ -123,8 +124,8 @@ export function parseErrorDescriptions(entity) { // add phrase (or single word) to variable list parsedPhrase += currWord; } - // if any variables have html, escape them - if (html_re.test(parsedPhrase)) { + // if any variables have html (excluding spans which are added ^), escape them + if (html_re.test(parsedPhrase) && !span_re.test(parsedPhrase)) { parsedPhrase = '\\' + parsedPhrase + '\\'; } parsedDescriptions.push(parsedPhrase); @@ -132,7 +133,6 @@ export function parseErrorDescriptions(entity) { } }); - return { var1: parsedDescriptions[0] || '', var2: parsedDescriptions[1] || '', From fbe3c94b011d63090ab9749978c786bce93bd866 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Thu, 30 Aug 2018 19:25:35 -0600 Subject: [PATCH 019/114] updated: punctuation, some specialized QA error parsing --- css/80_app.css | 3 +- data/core.yaml | 320 +++++++++++----------- dist/locales/en.json | 320 +++++++++++----------- modules/ui/keepRight_details.js | 6 +- modules/util/keepRight/errorSchema.json | 2 +- modules/util/keepRight/keepRight_error.js | 79 ++++-- modules/util/locale.js | 4 +- 7 files changed, 391 insertions(+), 343 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 1d601bf0d..81f46f468 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2539,7 +2539,8 @@ input.key-trap { .note-save, .keepRight-save, -.kr_error-details { +.kr_error-details, +.kr_error-comment-container { padding: 10px; } diff --git a/data/core.yaml b/data/core.yaml index 1f680ff42..b92b61509 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -955,246 +955,246 @@ en: errorTypes: errors: _30: - description: 'non-closed_areas' - tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop.' + title: 'non-closed_areas' + description: 'This way is tagged with {var1}={var2} and should be closed-loop.' _40: - description: 'dead-ended one-ways' - tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way.' + title: 'dead-ended one-ways' + description: 'The first node (id {var1}) of this one-way is not connected to any other way.' _41: - description: '' - tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way.' + title: '' + description: 'The last node (id {var1}) of this one-way is not connected to any other way.' _42: - description: '' - tooltip: 'This node cannot be reached because one-ways only lead away from here.' + title: '' + description: 'This node cannot be reached because one-ways only lead away from here.' _43: - description: '' - tooltip: 'You cannot escape from this node because one-ways only lead to here.' + title: '' + description: 'You cannot escape from this node because one-ways only lead to here.' _50: - description: 'almost-junctions' - tooltip: 'This node is very close but not connected to way #{var1}.' + title: 'almost-junctions' + description: 'This node is very close but not connected to way #{var1}.' _70: - description: 'missing tags' - tooltip: 'This {var1} has an empty tag: {var2}.' + title: 'missing tags' + description: 'This {var1} has an empty tag: {var2}.' _71: - description: 'way without tags' - tooltip: 'This way has no tags.' + title: 'way without tags' + description: 'This way has no tags.' _72: - description: 'node without tags' - tooltip: 'This node is not member of any way and doesn''t have any tags.' + title: 'node without tags' + description: 'This node is not member of any way and doesn''t have any tags.' _90: - description: 'motorways without ref' - tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag.' + title: 'motorways without ref' + description: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag.' _100: - description: 'places of worship without religion' - tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag.' + title: 'places of worship without religion' + description: 'This {var1} is tagged as place of worship and therefore needs a religion tag.' _110: - description: 'point of interest without name' - tooltip: 'This node is tagged as {var1} and therefore needs a name tag.' + title: 'point of interest without name' + description: 'This node is tagged as {var1} and therefore needs a name tag.' _120: - description: 'ways without nodes' - tooltip: 'This way has just one single node.' + title: 'ways without nodes' + description: 'This way has just one single node.' _130: - description: 'floating islands' - tooltip: 'This way is not connected to the rest of the map.' + title: 'floating islands' + description: 'This way is not connected to the rest of the map.' _150: - description: 'railway crossing without tag' - tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing.' + title: 'railway crossing without tag' + description: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing.' _160: - description: 'wrongly used railway tag' - tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing.' + title: 'wrongly used railway tag' + description: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing.' _170: - description: 'FIXME tagged items' - tooltip: '{var1}' + title: 'FIXME tagged items' + description: '{var1}' _180: - description: 'relations without type' - tooltip: 'This relation has no type tag which is mandatory for relations.' + title: 'relations without type' + description: 'This relation has no type tag which is mandatory for relations.' _190: - description: 'intersections without junctions' - tooltip: 'Finds way crossings on same layer without common node as a junction.' + title: 'intersections without junctions' + description: 'Finds way crossings on same layer without common node as a junction.' _191: - description: 'highway-highway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' + title: 'highway-highway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _192: - description: 'highway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}.' + title: 'highway-waterway' + description: 'This {var1} intersects the {var2} #{var3}.' _193: - description: 'highway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}.' + title: 'highway-riverbank' + description: 'This {var1} intersects the {var2} #{var3}.' _194: - description: 'waterway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' + title: 'waterway-waterway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _195: - description: 'cycleway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' + title: 'cycleway-cycleway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _196: - description: 'highway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' + title: 'highway-cycleway' + description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' _197: - description: 'cycleway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}.' + title: 'cycleway-waterway' + description: 'This {var1} intersects the {var2} #{var3}.' _198: - description: 'cycleway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}.' + title: 'cycleway-riverbank' + description: 'This {var1} intersects the {var2} #{var3}.' _200: - description: 'overlapping ways' - tooltip: 'Finds overlapping ways on same layer.' + title: 'overlapping ways' + description: 'Finds overlapping ways on same layer.' _201: - description: 'highway-highway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'highway-highway' + description: 'This {var1} overlaps the {var2} #{var3}.' _202: - description: 'highway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'highway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}.' _203: - description: 'highway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'highway-riverbank' + description: 'This {var1} overlaps the {var2} #{var3}.' _204: - description: 'waterway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'waterway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}.' _205: - description: 'cycleway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'cycleway-cycleway' + description: 'This {var1} overlaps the {var2} #{var3}.' _206: - description: 'highway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'highway-cycleway' + description: 'This {var1} overlaps the {var2} #{var3}.' _207: - description: 'cycleway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'cycleway-waterway' + description: 'This {var1} overlaps the {var2} #{var3}.' _208: - description: 'cycleway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}.' + title: 'cycleway-riverbank' + description: 'This {var1} overlaps the {var2} #{var3}.' _210: - description: 'loopings' - tooltip: 'These errors contain self intersecting ways.' + title: 'loopings' + description: 'These errors contain self intersecting ways.' _211: - description: '' - tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error.' + title: '' + description: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error.' _212: - description: '' - tooltip: 'This way has only two different nodes and contains one of them more than once.' + title: '' + description: 'This way has only two different nodes and contains one of them more than once.' _220: - description: 'misspelled tags' - tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}.' + title: 'misspelled tags' + description: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}.' _221: - description: '' - tooltip: 'The key of this {var1} tag is key {var2}.' + title: '' + description: 'The key of this {var1} tag is key {var2}.' _230: - description: 'layer conflicts' - tooltip: '' + title: 'layer conflicts' + description: '' _231: - description: 'mixed layers intersection' - tooltip: 'This node is a junction of ways on different layers: {var1}.' + title: 'mixed layers intersection' + description: 'This node is a junction of ways on different layers: {var1}.' _232: - description: 'strange layers' - tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' + title: 'strange layers' + description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' _270: - description: 'motorways connected directly' - tooltip: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' + title: 'motorways connected directly' + description: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' _280: - description: 'boundaries' - tooltip: '' + title: 'boundaries' + description: '' _281: - description: 'missing name' - tooltip: 'This boundary has no name.' + title: 'missing name' + description: 'This boundary has no name.' _282: - description: 'missing admin level' - tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' + title: 'missing admin level' + description: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' _283: - description: 'no closed loop' - tooltip: 'The boundary of {var1} is not closed-loop.' + title: 'no closed loop' + description: 'The boundary of {var1} is not closed-loop.' _284: - description: 'splitting boundary' - tooltip: 'The boundary of {var1} splits here.' + title: 'splitting boundary' + description: 'The boundary of {var1} splits here.' _285: - description: 'admin_level too high' - tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations.' + title: 'admin_level too high' + description: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations.' _290: - description: 'restrictions' - tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat.' + title: 'restrictions' + description: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat.' _291: - description: 'missing type' - tooltip: 'This turn-restriction has no known restriction type.' + title: 'missing type' + description: 'This turn-restriction has no known restriction type.' _292: - description: 'missing from way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' + title: 'missing from way' + description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' _293: - description: 'missing to way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' + title: 'missing to way' + description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' _294: - description: 'from or to not a way' - tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}.' + title: 'from or to not a way' + description: 'From- and To-members of turn restrictions need to be ways. {var1}.' _295: - description: 'via is not on the way ends' - tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2}).' + title: 'via is not on the way ends' + description: 'via (node #{var1}) is not the first or the last member of from (way #{var2}).' _296: - description: 'wrong restriction angle' - tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?.' + title: 'wrong restriction angle' + description: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' _297: - description: 'wrong direction of to member' - tooltip: 'wrong direction of to way {var1}.' + title: 'wrong direction of to member' + description: 'wrong direction of to way {var1}.' _298: - description: 'already restricted by oneway' - tooltip: 'entry already prohibited by oneway tag on {var1}.' + title: 'already restricted by oneway' + description: 'entry already prohibited by oneway tag on {var1}.' _310: - description: 'roundabouts' - tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.' + title: 'roundabouts' + description: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.' _311: - description: 'not closed loop' - tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' + title: 'not closed loop' + description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' _312: - description: 'wrong direction' - tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around.' + title: 'wrong direction' + description: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around.' _313: - description: 'faintly connected' - tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three.' + title: 'faintly connected' + description: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three.' _320: - description: '*_link connections' - tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link.' + title: '*_link connections' + description: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link.' _350: - description: 'bridge-tags' - tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' + title: 'bridge-tags' + description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' _370: - description: 'doubled places' - tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand.' + title: 'doubled places' + description: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand.' _380: - description: 'non-physical use of sport-tag' - tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway.' + title: 'non-physical use of sport-tag' + description: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway.' _400: - description: 'geometry glitches' - tooltip: '' + title: 'geometry glitches' + description: '' _401: - description: 'missing turn restriction' - tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}.' + title: 'missing turn restriction' + description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}.' _402: - description: 'impossible angles' - tooltip: 'this way bends in a very sharp angle here.' + title: 'impossible angles' + description: 'this way bends in a very sharp angle here.' _410: - description: 'website' - tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*.' + title: 'website' + description: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*.' _411: - description: 'http error' - tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2}).' + title: 'http error' + description: 'The URL cannot be opened (HTTP status code {var2}).' _412: - description: 'domain hijacking' - tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}".' + title: 'domain hijacking' + description: 'Possible domain squatting: The URL has Suspicious text: "{var2}".' _413: - description: 'non-match' - tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2}).' + title: 'non-match' + description: 'Content of the URL did not contain these keywords: ({var2}).' warnings: _20: - description: 'multiple nodes on the same spot' - tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}.' + title: 'multiple nodes on the same spot' + description: 'There is more than one node in this spot. Offending node IDs: {var1}.' _60: - description: 'depreciated tags' - tooltip: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' + title: 'depreciated tags' + description: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' _300: - description: 'missing maxspeed' - tooltip: 'missing maxspeed tag.' + title: 'missing maxspeed' + description: 'missing maxspeed tag.' _360: - description: 'language unknown' - tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}.' + title: 'language unknown' + description: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}.' _390: - description: 'missing tracktype' - tooltip: This track doesn't have a tracktype. + title: 'missing tracktype' + description: This track doesn't have a tracktype. streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index 1a861d74b..7476b3471 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1163,326 +1163,326 @@ "errorTypes": { "errors": { "_30": { - "description": "non-closed_areas", - "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop." + "title": "non-closed_areas", + "description": "This way is tagged with {var1}={var2} and should be closed-loop." }, "_40": { - "description": "dead-ended one-ways", - "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way." + "title": "dead-ended one-ways", + "description": "The first node (id {var1}) of this one-way is not connected to any other way." }, "_41": { - "description": "", - "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way." + "title": "", + "description": "The last node (id {var1}) of this one-way is not connected to any other way." }, "_42": { - "description": "", - "tooltip": "This node cannot be reached because one-ways only lead away from here." + "title": "", + "description": "This node cannot be reached because one-ways only lead away from here." }, "_43": { - "description": "", - "tooltip": "You cannot escape from this node because one-ways only lead to here." + "title": "", + "description": "You cannot escape from this node because one-ways only lead to here." }, "_50": { - "description": "almost-junctions", - "tooltip": "This node is very close but not connected to way #{var1}." + "title": "almost-junctions", + "description": "This node is very close but not connected to way #{var1}." }, "_70": { - "description": "missing tags", - "tooltip": "This {var1} has an empty tag: {var2}." + "title": "missing tags", + "description": "This {var1} has an empty tag: {var2}." }, "_71": { - "description": "way without tags", - "tooltip": "This way has no tags." + "title": "way without tags", + "description": "This way has no tags." }, "_72": { - "description": "node without tags", - "tooltip": "This node is not member of any way and doesn't have any tags." + "title": "node without tags", + "description": "This node is not member of any way and doesn't have any tags." }, "_90": { - "description": "motorways without ref", - "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag." + "title": "motorways without ref", + "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag." }, "_100": { - "description": "places of worship without religion", - "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag." + "title": "places of worship without religion", + "description": "This {var1} is tagged as place of worship and therefore needs a religion tag." }, "_110": { - "description": "point of interest without name", - "tooltip": "This node is tagged as {var1} and therefore needs a name tag." + "title": "point of interest without name", + "description": "This node is tagged as {var1} and therefore needs a name tag." }, "_120": { - "description": "ways without nodes", - "tooltip": "This way has just one single node." + "title": "ways without nodes", + "description": "This way has just one single node." }, "_130": { - "description": "floating islands", - "tooltip": "This way is not connected to the rest of the map." + "title": "floating islands", + "description": "This way is not connected to the rest of the map." }, "_150": { - "description": "railway crossing without tag", - "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing." + "title": "railway crossing without tag", + "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing." }, "_160": { - "description": "wrongly used railway tag", - "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing." + "title": "wrongly used railway tag", + "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing." }, "_170": { - "description": "FIXME tagged items", - "tooltip": "{var1}" + "title": "FIXME tagged items", + "description": "{var1}" }, "_180": { - "description": "relations without type", - "tooltip": "This relation has no type tag which is mandatory for relations." + "title": "relations without type", + "description": "This relation has no type tag which is mandatory for relations." }, "_190": { - "description": "intersections without junctions", - "tooltip": "Finds way crossings on same layer without common node as a junction." + "title": "intersections without junctions", + "description": "Finds way crossings on same layer without common node as a junction." }, "_191": { - "description": "highway-highway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." + "title": "highway-highway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_192": { - "description": "highway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}." + "title": "highway-waterway", + "description": "This {var1} intersects the {var2} #{var3}." }, "_193": { - "description": "highway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}." + "title": "highway-riverbank", + "description": "This {var1} intersects the {var2} #{var3}." }, "_194": { - "description": "waterway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." + "title": "waterway-waterway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_195": { - "description": "cycleway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." + "title": "cycleway-cycleway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_196": { - "description": "highway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node." + "title": "highway-cycleway", + "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." }, "_197": { - "description": "cycleway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}." + "title": "cycleway-waterway", + "description": "This {var1} intersects the {var2} #{var3}." }, "_198": { - "description": "cycleway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}." + "title": "cycleway-riverbank", + "description": "This {var1} intersects the {var2} #{var3}." }, "_200": { - "description": "overlapping ways", - "tooltip": "Finds overlapping ways on same layer." + "title": "overlapping ways", + "description": "Finds overlapping ways on same layer." }, "_201": { - "description": "highway-highway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "highway-highway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_202": { - "description": "highway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "highway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_203": { - "description": "highway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "highway-riverbank", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_204": { - "description": "waterway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "waterway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_205": { - "description": "cycleway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "cycleway-cycleway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_206": { - "description": "highway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "highway-cycleway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_207": { - "description": "cycleway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "cycleway-waterway", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_208": { - "description": "cycleway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}." + "title": "cycleway-riverbank", + "description": "This {var1} overlaps the {var2} #{var3}." }, "_210": { - "description": "loopings", - "tooltip": "These errors contain self intersecting ways." + "title": "loopings", + "description": "These errors contain self intersecting ways." }, "_211": { - "description": "", - "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error." + "title": "", + "description": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error." }, "_212": { - "description": "", - "tooltip": "This way has only two different nodes and contains one of them more than once." + "title": "", + "description": "This way has only two different nodes and contains one of them more than once." }, "_220": { - "description": "misspelled tags", - "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}." + "title": "misspelled tags", + "description": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}." }, "_221": { - "description": "", - "tooltip": "The key of this {var1} tag is key {var2}." + "title": "", + "description": "The key of this {var1} tag is key {var2}." }, "_230": { - "description": "layer conflicts", - "tooltip": "" + "title": "layer conflicts", + "description": "" }, "_231": { - "description": "mixed layers intersection", - "tooltip": "This node is a junction of ways on different layers: {var1}." + "title": "mixed layers intersection", + "description": "This node is a junction of ways on different layers: {var1}." }, "_232": { - "description": "strange layers", - "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange." + "title": "strange layers", + "description": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange." }, "_270": { - "description": "motorways connected directly", - "tooltip": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + "title": "motorways connected directly", + "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." }, "_280": { - "description": "boundaries", - "tooltip": "" + "title": "boundaries", + "description": "" }, "_281": { - "description": "missing name", - "tooltip": "This boundary has no name." + "title": "missing name", + "description": "This boundary has no name." }, "_282": { - "description": "missing admin level", - "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries." + "title": "missing admin level", + "description": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries." }, "_283": { - "description": "no closed loop", - "tooltip": "The boundary of {var1} is not closed-loop." + "title": "no closed loop", + "description": "The boundary of {var1} is not closed-loop." }, "_284": { - "description": "splitting boundary", - "tooltip": "The boundary of {var1} splits here." + "title": "splitting boundary", + "description": "The boundary of {var1} splits here." }, "_285": { - "description": "admin_level too high", - "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations." + "title": "admin_level too high", + "description": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations." }, "_290": { - "description": "restrictions", - "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat." + "title": "restrictions", + "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat." }, "_291": { - "description": "missing type", - "tooltip": "This turn-restriction has no known restriction type." + "title": "missing type", + "description": "This turn-restriction has no known restriction type." }, "_292": { - "description": "missing from way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}." + "title": "missing from way", + "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}." }, "_293": { - "description": "missing to way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}." + "title": "missing to way", + "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}." }, "_294": { - "description": "from or to not a way", - "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}." + "title": "from or to not a way", + "description": "From- and To-members of turn restrictions need to be ways. {var1}." }, "_295": { - "description": "via is not on the way ends", - "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})." + "title": "via is not on the way ends", + "description": "via (node #{var1}) is not the first or the last member of from (way #{var2})." }, "_296": { - "description": "wrong restriction angle", - "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?." + "title": "wrong restriction angle", + "description": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" }, "_297": { - "description": "wrong direction of to member", - "tooltip": "wrong direction of to way {var1}." + "title": "wrong direction of to member", + "description": "wrong direction of to way {var1}." }, "_298": { - "description": "already restricted by oneway", - "tooltip": "entry already prohibited by oneway tag on {var1}." + "title": "already restricted by oneway", + "description": "entry already prohibited by oneway tag on {var1}." }, "_310": { - "description": "roundabouts", - "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1." + "title": "roundabouts", + "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1." }, "_311": { - "description": "not closed loop", - "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." + "title": "not closed loop", + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." }, "_312": { - "description": "wrong direction", - "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around." + "title": "wrong direction", + "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around." }, "_313": { - "description": "faintly connected", - "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three." + "title": "faintly connected", + "description": "This roundabout has only {var1} other roads connected. Roundabouts typically have three." }, "_320": { - "description": "*_link connections", - "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link." + "title": "*_link connections", + "description": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link." }, "_350": { - "description": "bridge-tags", - "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." + "title": "bridge-tags", + "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." }, "_370": { - "description": "doubled places", - "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand." + "title": "doubled places", + "description": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand." }, "_380": { - "description": "non-physical use of sport-tag", - "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway." + "title": "non-physical use of sport-tag", + "description": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway." }, "_400": { - "description": "geometry glitches", - "tooltip": "" + "title": "geometry glitches", + "description": "" }, "_401": { - "description": "missing turn restriction", - "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}." + "title": "missing turn restriction", + "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}." }, "_402": { - "description": "impossible angles", - "tooltip": "this way bends in a very sharp angle here." + "title": "impossible angles", + "description": "this way bends in a very sharp angle here." }, "_410": { - "description": "website", - "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*." + "title": "website", + "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*." }, "_411": { - "description": "http error", - "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})." + "title": "http error", + "description": "The URL cannot be opened (HTTP status code {var2})." }, "_412": { - "description": "domain hijacking", - "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"." + "title": "domain hijacking", + "description": "Possible domain squatting: The URL has Suspicious text: \"{var2}\"." }, "_413": { - "description": "non-match", - "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})." + "title": "non-match", + "description": "Content of the URL did not contain these keywords: ({var2})." } }, "warnings": { "_20": { - "description": "multiple nodes on the same spot", - "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}." + "title": "multiple nodes on the same spot", + "description": "There is more than one node in this spot. Offending node IDs: {var1}." }, "_60": { - "description": "depreciated tags", - "tooltip": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" + "title": "depreciated tags", + "description": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" }, "_300": { - "description": "missing maxspeed", - "tooltip": "missing maxspeed tag." + "title": "missing maxspeed", + "description": "missing maxspeed tag." }, "_360": { - "description": "language unknown", - "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}." + "title": "language unknown", + "description": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}." }, "_390": { - "description": "missing tracktype", - "tooltip": "This track doesn't have a tracktype." + "title": "missing tracktype", + "description": "This track doesn't have a tracktype." } } } diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index f6d7c9967..2a330b688 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -73,12 +73,12 @@ export function uiKeepRightDetails(context) { // if this is a subtype, append it's parent title if (_parent_error_type) { - title = t(_titleBase + _parent_error_type + '.description') + ': \n'; + title = t(_titleBase + _parent_error_type + '.title') + ': \n'; } // append title if (_error.error_type) { - title += t(_titleBase + _templateErrorType + '.description'); + title += t(_titleBase + _templateErrorType + '.title'); } return title; @@ -98,7 +98,7 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details-description-text') .html(function(d) { - return t(_titleBase + _templateErrorType + '.tooltip', parseErrorDescriptions(d)); + return t(_titleBase + _templateErrorType + '.description', parseErrorDescriptions(d)); }); description.selectAll('.kr_error_description-id') diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 3bfd75b72..7df64e904 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -160,7 +160,7 @@ }, "_220": { "title": "misspelled tags", - "description": "This {$1} is tagged ''{$2}={$3}''where {$4} looks like {$5}" + "description": "This {$1} is tagged '{$2}={$3}' where {$4} looks like {$5}" }, "_221": { "title": "", diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 8986b3bd5..56c82805c 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -53,11 +53,25 @@ var keepRightSchemaFromWeb = { export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; + var _220 = false; + var _splitVar2; + var _splitVar3; + var _401 = false; + // find the matching template from the error schema var errorType = '_' + entity.error_type; var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; if (!matchingTemplate) return; + // handle special cases + // error _170 + if (errorType === '_170') { return { var1: entity.description }; } + + // error _220 + if (errorType === '_220') { _220 = true; } + + if (errorType === '_401') { _401 = true; } + // tokenize descriptions var errorDescription = entity.description.split(' '); var templateDescription = matchingTemplate.description.split(' '); @@ -66,14 +80,22 @@ export function parseErrorDescriptions(entity) { var variable_re = new RegExp(/{\$[0-9]}/); var html_re = new RegExp(/<\/[a-z][\s\S]*>/); var span_re = new RegExp(/<\/span>/); + var digit_re = new RegExp(/^\d+$/); - var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; // TODO: expand this list, or implement a different translation function + var commonEntities = [ + 'node', + 'way', + 'relation', + 'highway', + 'cycleway', + 'waterway', + 'riverbank' + ]; // TODO: expand this list, or implement a different translation function - function fillPlaceholder(d) { - return '' + d + ''; - } + function fillPlaceholder(d) { return '' + d + ''; } function getEntityBase(lastWord) { + var result; commonEntities.forEach(function(entity) { if (entity.includes(lastWord)) { result = entity; } @@ -81,11 +103,34 @@ export function parseErrorDescriptions(entity) { }); if (result) { - result = result.includes('node') ? 'n' : result.includes('way') ? 'w' : result.includes('relation') ? 'r' : null; + return result.includes('node') ? 'n' : + result.includes('way') ? 'w' : + result.includes('relation') ? 'r' : null; + } + // special handling for error _401 + else if (_401 && parsedDescriptions[0]) { return 'w'; } // hacky check to see if var1 id is entered return result; } + function isID(word, i) { + // select just numeric part of id + if (word.charAt(0) === '#' || errorDescription[i-1] === '(id') { // NOTE: hacky way of selecting the token before + return word.replace(/\D/g,''); + } + // if it's just an id (e.g., error _401) + else if (digit_re.test(word) && _401) { return word; } + return false; + } + + function getIDType(i) { + var lastWord = errorDescription[i-1]; + var secondLastWord = errorDescription[i-2]; + if (lastWord) { return getEntityBase(lastWord) || getEntityBase(lastWord.slice(0, -1)) || getEntityBase(secondLastWord); } + + return getEntityBase(parsedDescriptions.slice(-1)[0].split(' ').slice(-1)[0]); + } + templateDescription.forEach(function(word, index) { if (!variable_re.test(word)) return; @@ -99,19 +144,10 @@ export function parseErrorDescriptions(entity) { if (errorDescription[i] !== nextWord) { var currWord = errorDescription[i]; - // select just numeric part of id - if (currWord.charAt(0) === '#' || errorDescription[i-1] === '(id') { // NOTE: hacky way of selecting the token before - currWord = currWord.replace(/\D/g,''); - + // if the word is an id, clean and link it + if (isID(currWord, i)) { // get the entity type of the id - var lastWord = errorDescription[i-1]; - var secondLastWord = errorDescription[i-2]; - var base; - if (lastWord) { base = getEntityBase(lastWord) || getEntityBase(secondLastWord); } - if (!base) { - base = getEntityBase(parsedDescriptions.slice(-1)[0].split(' ').slice(-1)[0]); - } - + var base = getIDType(i); // wrap id with linking span currWord = fillPlaceholder(base + currWord); } @@ -121,6 +157,15 @@ export function parseErrorDescriptions(entity) { currWord = t('QA.keepRight.entities.' + currWord); } + // special handling for error _220 + if (_220 && index === 4) { + _splitVar2 = currWord.split('=')[0]; + _splitVar3 = currWord.split('=')[1]; + parsedDescriptions.push(_splitVar2); + parsedDescriptions.push(_splitVar3); + break; + } + // add phrase (or single word) to variable list parsedPhrase += currWord; } diff --git a/modules/util/locale.js b/modules/util/locale.js index 7396b7549..1ed2c2b7e 100644 --- a/modules/util/locale.js +++ b/modules/util/locale.js @@ -42,7 +42,9 @@ export function t(s, o, loc) { if (rep !== undefined) { if (o) { for (var k in o) { - rep = rep.replace('{' + k + '}', o[k]); + var variable = '{' + k + '}'; + var re = new RegExp(variable, 'g'); // check globally for variables + rep = rep.replace(re, o[k]); } } return rep; From bb89827ee71cc6771d3d83665cf0d1c837661a47 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Fri, 31 Aug 2018 21:28:12 +0100 Subject: [PATCH 020/114] Use regex group capture to extract error details --- modules/util/keepRight/keepRight_error.js | 140 ++++++---------------- 1 file changed, 36 insertions(+), 104 deletions(-) diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 56c82805c..b9fda053b 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -53,34 +53,13 @@ var keepRightSchemaFromWeb = { export function parseErrorDescriptions(entity) { if (!(entity instanceof krError)) return; - var _220 = false; - var _splitVar2; - var _splitVar3; - var _401 = false; - // find the matching template from the error schema var errorType = '_' + entity.error_type; var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; if (!matchingTemplate) return; - // handle special cases - // error _170 - if (errorType === '_170') { return { var1: entity.description }; } - - // error _220 - if (errorType === '_220') { _220 = true; } - - if (errorType === '_401') { _401 = true; } - - // tokenize descriptions - var errorDescription = entity.description.split(' '); - var templateDescription = matchingTemplate.description.split(' '); - - var parsedDescriptions = []; - var variable_re = new RegExp(/{\$[0-9]}/); + var parsedDetails = []; var html_re = new RegExp(/<\/[a-z][\s\S]*>/); - var span_re = new RegExp(/<\/span>/); - var digit_re = new RegExp(/^\d+$/); var commonEntities = [ 'node', @@ -94,97 +73,50 @@ export function parseErrorDescriptions(entity) { function fillPlaceholder(d) { return '' + d + ''; } - function getEntityBase(lastWord) { + // regex pattern should capture groups to extract appropriate details + var errorDescription = entity.description; + var errorRe = new RegExp(matchingTemplate.description); - var result; - commonEntities.forEach(function(entity) { - if (entity.includes(lastWord)) { result = entity; } - return; - }); + var errorMatch = errorRe.exec(errorDescription); - if (result) { - return result.includes('node') ? 'n' : - result.includes('way') ? 'w' : - result.includes('relation') ? 'r' : null; + if (!errorMatch) { + // TODO: Remove, for regex dev testing + console.log('Unmatched:', errorType, errorDescription, errorRe); + return + }; - } - // special handling for error _401 - else if (_401 && parsedDescriptions[0]) { return 'w'; } // hacky check to see if var1 id is entered - return result; - } + // index 0 is the whole match, groups start from 1 + for (var i = 1; i < errorMatch.length; i++) { + var group = errorMatch[i]; - function isID(word, i) { - // select just numeric part of id - if (word.charAt(0) === '#' || errorDescription[i-1] === '(id') { // NOTE: hacky way of selecting the token before - return word.replace(/\D/g,''); - } - // if it's just an id (e.g., error _401) - else if (digit_re.test(word) && _401) { return word; } - return false; - } + // IDs captured have an associated type + if ('IDs' in matchingTemplate && matchingTemplate.IDs[i-1]) { + var prefix = matchingTemplate.IDs[i-1]; - function getIDType(i) { - var lastWord = errorDescription[i-1]; - var secondLastWord = errorDescription[i-2]; - if (lastWord) { return getEntityBase(lastWord) || getEntityBase(lastWord.slice(0, -1)) || getEntityBase(secondLastWord); } - - return getEntityBase(parsedDescriptions.slice(-1)[0].split(' ').slice(-1)[0]); - } - - templateDescription.forEach(function(word, index) { - if (!variable_re.test(word)) return; - - // get the word at this index, and at the next index value - var nextWord = templateDescription[index + 1] ? templateDescription[index + 1] : null; - - var parsedPhrase = ''; - - // parse error description words - for (var i = index; i <= errorDescription.length - 1; i++) { - if (errorDescription[i] !== nextWord) { - var currWord = errorDescription[i]; - - // if the word is an id, clean and link it - if (isID(currWord, i)) { - // get the entity type of the id - var base = getIDType(i); - // wrap id with linking span - currWord = fillPlaceholder(base + currWord); - } - - // if any variables contain common words, like node, way, relation, translate those - if (commonEntities.includes(currWord)) { - currWord = t('QA.keepRight.entities.' + currWord); - } - - // special handling for error _220 - if (_220 && index === 4) { - _splitVar2 = currWord.split('=')[0]; - _splitVar3 = currWord.split('=')[1]; - parsedDescriptions.push(_splitVar2); - parsedDescriptions.push(_splitVar3); - break; - } - - // add phrase (or single word) to variable list - parsedPhrase += currWord; + // wrap id with linking span if valid + if (['n','w','r'].includes(prefix)) { + group = fillPlaceholder(prefix + group); } - // if any variables have html (excluding spans which are added ^), escape them - if (html_re.test(parsedPhrase) && !span_re.test(parsedPhrase)) { - parsedPhrase = '\\' + parsedPhrase + '\\'; - } - parsedDescriptions.push(parsedPhrase); - break; + } else if (html_re.test(group)) { + // escape any html + group = '\\' + group + '\\'; } - }); + + // translate common words (e.g. node, way, relation) + if (commonEntities.includes(group)) { + group = t('QA.keepRight.entities.' + group); + } + + parsedDetails.push(group); + } return { - var1: parsedDescriptions[0] || '', - var2: parsedDescriptions[1] || '', - var3: parsedDescriptions[2] || '', - var4: parsedDescriptions[3] || '', - var5: parsedDescriptions[4] || '', - var6: parsedDescriptions[5] || '', + var1: parsedDetails[0] || '', + var2: parsedDetails[1] || '', + var3: parsedDetails[2] || '', + var4: parsedDetails[3] || '', + var5: parsedDetails[4] || '', + var6: parsedDetails[5] || '', }; } From 424bebf09916d9629af1c0344e74dd3bd0ffad3f Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Fri, 31 Aug 2018 22:18:54 +0100 Subject: [PATCH 021/114] Convert preliminary amount of errors to regex This focuses on converting error types I found on the map for testing (can confirm the code is working!) as well as the more problematic cases from the old code to show that this approach can handle them easily. --- data/core.yaml | 6 ++--- modules/util/keepRight/errorSchema.json | 32 +++++++++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index b92b61509..2ab37af61 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1073,7 +1073,7 @@ en: description: 'This way has only two different nodes and contains one of them more than once.' _220: title: 'misspelled tags' - description: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}.' + description: 'This {var1} is tagged {var2}={var3} where "{var4}" looks like "{var5}".' _221: title: '' description: 'The key of this {var1} tag is key {var2}.' @@ -1145,7 +1145,7 @@ en: description: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around.' _313: title: 'faintly connected' - description: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three.' + description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' _320: title: '*_link connections' description: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link.' @@ -1163,7 +1163,7 @@ en: description: '' _401: title: 'missing turn restriction' - description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}.' + description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.' _402: title: 'impossible angles' description: 'this way bends in a very sharp angle here.' diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 7df64e904..6a33cade7 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -24,7 +24,8 @@ }, "_50": { "title": "almost-junctions", - "description": "This node is very close but not connected to way #{$1}" + "description": "This node is very close but not connected to way #(\\d+)", + "IDs": ["w"] }, "_70": { "title": "missing tags", @@ -48,7 +49,7 @@ }, "_110": { "title": "point of interest without name", - "description": "This node is tagged as {$1} and therefore needs a name tag" + "description": "This node is tagged as (\\w+) and therefore needs a name tag" }, "_120": { "title": "ways without nodes", @@ -68,7 +69,7 @@ }, "_170": { "title": "FIXME tagged items", - "description": "{$1}" + "description": "(.*)" }, "_180": { "title": "relations without type", @@ -80,7 +81,8 @@ }, "_191": { "title": "highway-highway", - "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", + "IDs": ["", "", "w"] }, "_192": { "title": "highway-waterway", @@ -88,7 +90,8 @@ }, "_193": { "title": "highway-riverbank", - "description": "This {$1} intersects the {$2} #{$3}" + "description": "This (riverbank) intersects the (highway) #(\\d+)", + "IDs": ["", "", "w"] }, "_194": { "title": "waterway-waterway", @@ -100,7 +103,8 @@ }, "_196": { "title": "highway-cycleway", - "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + "description": "This (\\w+(?:/\\w+)?) intersects the (highway) #(\\d+) but there is no junction node", + "IDs": ["", "", "w"] }, "_197": { "title": "cycleway-waterway", @@ -160,7 +164,7 @@ }, "_220": { "title": "misspelled tags", - "description": "This {$1} is tagged '{$2}={$3}' where {$4} looks like {$5}" + "description": "This (node|way|relation) is tagged '(\\w+)=(\\w+)' where "(\\w+)" looks like "(\\w+)"" }, "_221": { "title": "", @@ -232,7 +236,7 @@ }, "_296": { "title": "wrong restriction angle", - "description": "restriction type is {$1} but angle is {$2} degrees. Maybe the restriction type is not appropriate?" + "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?" }, "_297": { "title": "wrong direction of to member", @@ -256,7 +260,7 @@ }, "_313": { "title": "faintly connected", - "description": "This roundabout has only {$1} other roads connected. Roundabouts typically have three" + "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three" }, "_320": { "title": "*_link connections", @@ -272,7 +276,7 @@ }, "_380": { "title": "non-physical use of sport-tag", - "description": "This way is tagged {$1} but has no physical tag like e.g. leisure, building, amenity or highway" + "description": "This way is tagged (sport=\\w+) but has no physical tag like e.g. leisure, building, amenity or highway" }, "_400": { "title": "geometry glitches", @@ -280,7 +284,8 @@ }, "_401": { "title": "missing turn restriction", - "description": "ways {$1} and {$2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {$1} to {$2}" + "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way (\\d+) to (\\d+)", + "IDs": ["w", "w", "w", "w"] }, "_402": { "title": "impossible angles", @@ -292,7 +297,8 @@ }, "_411": { "title": "http error", - "description": "The URL ({$1}) cannot be opened (HTTP status code {$2})" + "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", + "TODO": "For some reason this regex doesn't match, possible related to quotes (see _220)" }, "_412": { "title": "domain hijacking", @@ -318,7 +324,7 @@ }, "_360": { "title": "language unknown", - "description": "It would be nice if this {$1} had an additional tag ''name:XX={$2}''where XX shows the language of its name ''{$2}''" + "description": "It would be nice if this (node|way|relation) had an additional tag ''name:XX=(\\.+?)''where XX shows the language of its name ''\\2''" }, "_390": { "title": "missing tracktype", From 1527bbb0432a514c2685ddb50f7d96daa948957f Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sat, 1 Sep 2018 17:20:42 +0100 Subject: [PATCH 022/114] Fix missing and unnecessary semicolons --- modules/util/keepRight/keepRight_error.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index b9fda053b..137e9692b 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -82,8 +82,8 @@ export function parseErrorDescriptions(entity) { if (!errorMatch) { // TODO: Remove, for regex dev testing console.log('Unmatched:', errorType, errorDescription, errorRe); - return - }; + return; + } // index 0 is the whole match, groups start from 1 for (var i = 1; i < errorMatch.length; i++) { From d054e9cf6b2256e2128df16c11bdda59d67fe79b Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sat, 1 Sep 2018 20:16:50 +0100 Subject: [PATCH 023/114] Convert all error message schema to regex --- data/core.yaml | 24 ++--- dist/locales/en.json | 26 ++--- modules/util/keepRight/errorSchema.json | 123 ++++++++++++++---------- 3 files changed, 99 insertions(+), 74 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 2ab37af61..908755cd7 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -974,7 +974,7 @@ en: description: 'This node is very close but not connected to way #{var1}.' _70: title: 'missing tags' - description: 'This {var1} has an empty tag: {var2}.' + description: 'This {var1} has an empty tag: "{var2}".' _71: title: 'way without tags' description: 'This way has no tags.' @@ -1067,22 +1067,22 @@ en: description: 'These errors contain self intersecting ways.' _211: title: '' - description: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error.' + description: 'This way contains more than one node multiple times. Nodes are {var1}, {var2}. This may or may not be an error.' _212: title: '' description: 'This way has only two different nodes and contains one of them more than once.' _220: title: 'misspelled tags' - description: 'This {var1} is tagged {var2}={var3} where "{var4}" looks like "{var5}".' + description: 'This {var1} is tagged "{var2}"="{var3}" where "{var4}" looks like "{var5}".' _221: title: '' - description: 'The key of this {var1} tag is key {var2}.' + description: 'This {var1} has a tag with key "key"="{var2}".' _230: title: 'layer conflicts' description: '' _231: title: 'mixed layers intersection' - description: 'This node is a junction of ways on different layers: {var1}.' + description: 'This node is a junction of ways on different layers: {var1}({var2}), {var3}.' _232: title: 'strange layers' description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' @@ -1115,16 +1115,16 @@ en: description: 'This turn-restriction has no known restriction type.' _292: title: 'missing from way' - description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' + description: 'A turn-restriction needs exactly one "from" member. This one has {var1}.' _293: title: 'missing to way' - description: 'A turn-restriction needs exactly one {var1} member. This one has {var2}.' + description: 'A turn-restriction needs exactly one "to" member. This one has {var1}.' _294: title: 'from or to not a way' - description: 'From- and To-members of turn restrictions need to be ways. {var1}.' + description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' _295: title: 'via is not on the way ends' - description: 'via (node #{var1}) is not the first or the last member of from (way #{var2}).' + description: '"via" (node #{var1}) is not the first or the last member of "from" (way #{var2}).' _296: title: 'wrong restriction angle' description: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' @@ -1142,7 +1142,7 @@ en: description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' _312: title: 'wrong direction' - description: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around.' + description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around' _313: title: 'faintly connected' description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' @@ -1154,10 +1154,10 @@ en: description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' _370: title: 'doubled places' - description: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand.' + description: 'This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant' _380: title: 'non-physical use of sport-tag' - description: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway.' + description: 'This way is tagged "sport"="{var1}" but has no physical tag like e.g. leisure, building, amenity or highway.' _400: title: 'geometry glitches' description: '' diff --git a/dist/locales/en.json b/dist/locales/en.json index 7476b3471..4b91ec465 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1188,7 +1188,7 @@ }, "_70": { "title": "missing tags", - "description": "This {var1} has an empty tag: {var2}." + "description": "This {var1} has an empty tag: \"{var2}\"." }, "_71": { "title": "way without tags", @@ -1312,7 +1312,7 @@ }, "_211": { "title": "", - "description": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error." + "description": "This way contains more than one node multiple times. Nodes are {var1}, {var2}. This may or may not be an error." }, "_212": { "title": "", @@ -1320,11 +1320,11 @@ }, "_220": { "title": "misspelled tags", - "description": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}." + "description": "This {var1} is tagged \"{var2}\"=\"{var3}\" where \"{var4}\" looks like \"{var5}\"." }, "_221": { "title": "", - "description": "The key of this {var1} tag is key {var2}." + "description": "This {var1} has a tag with key \"key\"=\"{var2}\"." }, "_230": { "title": "layer conflicts", @@ -1376,19 +1376,19 @@ }, "_292": { "title": "missing from way", - "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}." + "description": "A turn-restriction needs exactly one \"from\" member. This one has {var1}." }, "_293": { "title": "missing to way", - "description": "A turn-restriction needs exactly one {var1} member. This one has {var2}." + "description": "A turn-restriction needs exactly one \"to\" member. This one has {var1}." }, "_294": { "title": "from or to not a way", - "description": "From- and To-members of turn restrictions need to be ways. {var1}." + "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {var1}." }, "_295": { "title": "via is not on the way ends", - "description": "via (node #{var1}) is not the first or the last member of from (way #{var2})." + "description": "\"via\" (node #{var1}) is not the first or the last member of \"from\" (way #{var2})." }, "_296": { "title": "wrong restriction angle", @@ -1412,11 +1412,11 @@ }, "_312": { "title": "wrong direction", - "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around." + "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around" }, "_313": { "title": "faintly connected", - "description": "This roundabout has only {var1} other roads connected. Roundabouts typically have three." + "description": "This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more." }, "_320": { "title": "*_link connections", @@ -1428,11 +1428,11 @@ }, "_370": { "title": "doubled places", - "description": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand." + "description": "This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant" }, "_380": { "title": "non-physical use of sport-tag", - "description": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway." + "description": "This way is tagged \"sport\"=\"{var1}\" but has no physical tag like e.g. leisure, building, amenity or highway." }, "_400": { "title": "geometry glitches", @@ -1440,7 +1440,7 @@ }, "_401": { "title": "missing turn restriction", - "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}." + "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}." }, "_402": { "title": "impossible angles", diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 6a33cade7..cd8a859fd 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -4,15 +4,17 @@ "errors": { "_30": { "title": "non-closed_areas", - "description": "This way is tagged with ''{$1}={$2}''and should be closed-loop" + "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop" }, "_40": { "title": "dead-ended one-ways", - "description": "The first node (id {$1}) of this one-way is not connected to any other way" + "description": "The first node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "IDs": ["n"] }, "_41": { "title": "", - "description": "The last node (id {$1}) of this one-way is not connected to any other way" + "description": "The last node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "IDs": ["n"] }, "_42": { "title": "", @@ -29,7 +31,7 @@ }, "_70": { "title": "missing tags", - "description": "This {$1} has an empty tag: {$2}" + "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="" }, "_71": { "title": "", @@ -45,11 +47,11 @@ }, "_100": { "title": "places of worship without religion", - "description": "This {$1} is tagged as place of worship and therefore needs a religion tag" + "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag" }, "_110": { "title": "point of interest without name", - "description": "This node is tagged as (\\w+) and therefore needs a name tag" + "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag" }, "_120": { "title": "ways without nodes", @@ -86,33 +88,38 @@ }, "_192": { "title": "highway-waterway", - "description": "This {$1} intersects the {$2} #{$3}" + "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", + "IDs": ["", "", "w"] }, "_193": { "title": "highway-riverbank", - "description": "This (riverbank) intersects the (highway) #(\\d+)", + "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", "IDs": ["", "", "w"] }, "_194": { "title": "waterway-waterway", - "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", + "IDs": ["", "", "w"] }, "_195": { "title": "cycleway-cycleway", - "description": "This {$1} intersects the {$2} #{$3} but there is no junction node" + "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", + "IDs": ["", "", "w"] }, "_196": { "title": "highway-cycleway", - "description": "This (\\w+(?:/\\w+)?) intersects the (highway) #(\\d+) but there is no junction node", + "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", "IDs": ["", "", "w"] }, "_197": { "title": "cycleway-waterway", - "description": "This {$1} intersects the {$2} #{$3}" + "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_198": { "title": "cycleway-riverbank", - "description": "This {$1} intersects the {$2} #{$3}" + "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_200": { "title": "overlapping ways", @@ -120,35 +127,43 @@ }, "_201": { "title": "highway-highway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (highway) overlaps the (highway) #(\\d+)", + "IDs": ["", "", "w"] }, "_202": { "title": "highway-waterway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", + "IDs": ["", "", "w"] }, "_203": { "title": "highway-riverbank", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", + "IDs": ["", "", "w"] }, "_204": { "title": "waterway-waterway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (waterway) overlaps the (waterway) #(\\d+)", + "IDs": ["", "", "w"] }, "_205": { "title": "cycleway-cycleway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_206": { "title": "highway-cycleway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_207": { "title": "cycleway-waterway", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_208": { "title": "cycleway-riverbank", - "description": "This {$1} overlaps the {$2} #{$3}" + "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", + "IDs": ["", "", "w"] }, "_210": { "title": "loopings", @@ -156,7 +171,9 @@ }, "_211": { "title": "", - "description": "This way contains more than one node at least twice. Nodes are {$1}. This may or may not be an error" + "description": "This way contains more than one node at least twice. Nodes are #(\\d+), ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", + "IDs": ["n", ""], + "TODO": "Second group is arbitrary list of node IDs in form: #ID, #ID, #ID..." }, "_212": { "title": "", @@ -164,11 +181,11 @@ }, "_220": { "title": "misspelled tags", - "description": "This (node|way|relation) is tagged '(\\w+)=(\\w+)' where "(\\w+)" looks like "(\\w+)"" + "description": "This (node|way|relation) is tagged '([\\w:]+)=(.+)' where "(\\2|\\3)" looks like "([\\w\\s]+)"" }, "_221": { "title": "", - "description": "The key of this {$1}''s tag is ''key'': {$2}" + "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)" }, "_230": { "title": "layer conflicts", @@ -176,11 +193,13 @@ }, "_231": { "title": "mixed layers intersection", - "description": "This node is a junction of ways on different layers: {$1}" + "description": "This node is a junction of ways on different layers: #(\\d+)\\((-?\\d+)\\),((?:#\\d+\\(-?\\d+\\),?)+)", + "IDs": ["w", ""], + "TODO": "Third group is arbitrary list of way IDs and their layer value in form: #ID(layer),#ID(layer),#ID(layer)..." }, "_232": { "title": "strange layers", - "description": "This {$1} is tagged with layer {$2}. This need not be an error but it looks strange" + "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange" }, "_270": { "title": "motorways connected directly", @@ -189,26 +208,26 @@ "_280": { "title": "boundaries", "description": "Administrative Boundaries can be expressed either by tagging ways or by adding them to a relation. They should be closed-loop sequences of ways, they must not self-intersect or split and they must have a name and an admin_level." - }, + }, "_281": { "title": "missing name", "description": "This boundary has no name" }, "_282": { "title": "missing admin level", - "description": "The boundary of {$1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" + "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*" }, "_283": { "title": "no closed loop", - "description": "The boundary of {$1} is not closed-loop" + "description": "The boundary of (.+) is not closed-loop" }, "_284": { "title": "splitting boundary", - "description": "The boundary of {$1} splits here" + "description": "The boundary of (.+) splits here" }, "_285": { "title": "admin_level too high", - "description": "This boundary-way has admin_level {$1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" }, "_290": { "title": "restrictions", @@ -216,23 +235,25 @@ }, "_291": { "title": "missing type", - "description": "This turn-restriction has no known restriction type" + "description": "This turn-restriction has no (?:known )?restriction type" }, "_292": { "title": "missing from way", - "description": "A turn-restriction needs exactly one {$1} member. This one has {$2}" + "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)" }, "_293": { "title": "missing to way", - "description": "A turn-restriction needs exactly one {$1} member. This one has {$2}" + "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)" }, "_294": { "title": "from or to not a way", - "description": "From- and To-members of turn restrictions need to be ways. {$1}" + "description": "From- and To-members of turn restrictions need to be ways\\. (.+)", + "TODO": "Group can be any combination of to/from: to node #ID | from node #ID | to relation #ID | from relation #ID" }, "_295": { "title": "via is not on the way ends", - "description": "via (node #{$1}) is not the first or the last member of from (way #{$2})" + "description": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)", + "IDs": ["n", "w"] }, "_296": { "title": "wrong restriction angle", @@ -240,11 +261,13 @@ }, "_297": { "title": "wrong direction of to member", - "description": "wrong direction of to way {$1}" + "description": "wrong direction of to way (\\d+)", + "IDs": ["w"] }, "_298": { "title": "already restricted by oneway", - "description": "entry already prohibited by oneway tag on {$1}" + "description": "entry already prohibited by oneway tag on (\\d+)", + "IDs": ["w"] }, "_310": { "title": "roundabouts", @@ -252,11 +275,11 @@ }, "_311": { "title": "not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + "description": "This way is part of a roundabout but is not closed-loop\\. \\(split carriageways approaching a roundabout should not be tagged as roundabout\\)" }, "_312": { "title": "wrong direction", - "description": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" + "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around" }, "_313": { "title": "faintly connected", @@ -264,19 +287,21 @@ }, "_320": { "title": "*_link connections", - "description": "This way is tagged as highway={$1}_link but doesn''t have a connection to any other {$1} or {$1}_link" + "description": "This way is tagged as highway=(\\w+)_link but doesn''t have a connection to any other \\1 or \\1_link" }, "_350": { "title": "bridge-tags", - "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {$1}" + "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", + "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value..." }, "_370": { "title": "doubled places", - "description": "This node has tags in common with the surrounding way #{$1} (tah fix this-->)((including the name ''The Garage'')) and seems to be redundand | This node has tags in common with the surrounding way #{$1} (including the name ''{$2}'') and seems to be redundand" + "description": "This node has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and seems to be redundand", + "IDs": ["w", ""] }, "_380": { "title": "non-physical use of sport-tag", - "description": "This way is tagged (sport=\\w+) but has no physical tag like e.g. leisure, building, amenity or highway" + "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway" }, "_400": { "title": "geometry glitches", @@ -284,7 +309,7 @@ }, "_401": { "title": "missing turn restriction", - "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way (\\d+) to (\\d+)", + "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way (\\1|\\2) to (\\1|\\2)", "IDs": ["w", "w", "w", "w"] }, "_402": { @@ -297,16 +322,16 @@ }, "_411": { "title": "http error", - "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", - "TODO": "For some reason this regex doesn't match, possible related to quotes (see _220)" + "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", + "NOTE": "It seems the HTML attributes don't have quotes when the code reads them" }, "_412": { "title": "domain hijacking", - "description": "Possible domain squatting: {$1}. Suspicious text is: ''{$2}''" + "description": "Possible domain squatting: \\1. Suspicious text is: ''(.+)''" }, "_413": { "title": "non-match", - "description": "Content of the URL ({$1}) did not contain these keywords: ({$2})" + "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)" } }, "warnings": { From a2fe4316373aec39af026055efb928e6f20ebd21 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sun, 2 Sep 2018 09:43:25 +0100 Subject: [PATCH 024/114] Add support for arbitrary number of details --- modules/util/keepRight/keepRight_error.js | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 137e9692b..6c5341036 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -58,9 +58,6 @@ export function parseErrorDescriptions(entity) { var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; if (!matchingTemplate) return; - var parsedDetails = []; - var html_re = new RegExp(/<\/[a-z][\s\S]*>/); - var commonEntities = [ 'node', 'way', @@ -85,6 +82,9 @@ export function parseErrorDescriptions(entity) { return; } + var parsedDetails = {}; + var html_re = new RegExp(/<\/[a-z][\s\S]*>/); + // index 0 is the whole match, groups start from 1 for (var i = 1; i < errorMatch.length; i++) { var group = errorMatch[i]; @@ -107,17 +107,10 @@ export function parseErrorDescriptions(entity) { group = t('QA.keepRight.entities.' + group); } - parsedDetails.push(group); + parsedDetails['var' + i] = group; } - return { - var1: parsedDetails[0] || '', - var2: parsedDetails[1] || '', - var3: parsedDetails[2] || '', - var4: parsedDetails[3] || '', - var5: parsedDetails[4] || '', - var6: parsedDetails[5] || '', - }; + return parsedDetails; } From e0e48b75058c9903dde92113bcb7a2f276533448 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sun, 2 Sep 2018 11:05:37 +0100 Subject: [PATCH 025/114] Fix parsing of error type 231 --- data/core.yaml | 2 +- modules/util/keepRight/errorSchema.json | 5 ++-- modules/util/keepRight/keepRight_error.js | 34 +++++++++++++++++++++-- 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 908755cd7..164abb139 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1082,7 +1082,7 @@ en: description: '' _231: title: 'mixed layers intersection' - description: 'This node is a junction of ways on different layers: {var1}({var2}), {var3}.' + description: 'This node is a junction of ways on different layers: {var1}.' _232: title: 'strange layers' description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index cd8a859fd..d1ee09d7e 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -193,9 +193,8 @@ }, "_231": { "title": "mixed layers intersection", - "description": "This node is a junction of ways on different layers: #(\\d+)\\((-?\\d+)\\),((?:#\\d+\\(-?\\d+\\),?)+)", - "IDs": ["w", ""], - "TODO": "Third group is arbitrary list of way IDs and their layer value in form: #ID(layer),#ID(layer),#ID(layer)..." + "description": "This node is a junction of ways on different layers: ((?:#\\d+\\(-?\\d+\\),?)+)", + "IDs": ["231"] }, "_232": { "title": "strange layers", diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 6c5341036..0c8a92151 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -70,6 +70,31 @@ export function parseErrorDescriptions(entity) { function fillPlaceholder(d) { return '' + d + ''; } + // arbitrary list of way IDs and their layer value in form: #ID(layer),#ID(layer),#ID(layer)... + function parseError231(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var id; + var layer; + + // item of form "#ID(layer)" + item = item.split('('); + + // ID has # at the front + id = item[0].slice(1); + id = fillPlaceholder('w' + id); + + // layer has trailing ) + layer = item[1].slice(0,-1); + + newList.push(id + ' (layer: ' + layer + ')'); + }); + + return newList.join(', '); + } + // regex pattern should capture groups to extract appropriate details var errorDescription = entity.description; var errorRe = new RegExp(matchingTemplate.description); @@ -89,12 +114,15 @@ export function parseErrorDescriptions(entity) { for (var i = 1; i < errorMatch.length; i++) { var group = errorMatch[i]; - // IDs captured have an associated type + // Clean and link IDs if present in the error if ('IDs' in matchingTemplate && matchingTemplate.IDs[i-1]) { var prefix = matchingTemplate.IDs[i-1]; - // wrap id with linking span if valid - if (['n','w','r'].includes(prefix)) { + // some errors have more complex ID lists/variance + if (prefix === '231') { + group = parseError231(group); + } else if (['n','w','r'].includes(prefix)) { + // wrap with linking span if simple case group = fillPlaceholder(prefix + group); } } else if (html_re.test(group)) { From e77d7791731ceb0029ece8444b96cf8702d2ee3d Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sun, 2 Sep 2018 11:57:33 +0100 Subject: [PATCH 026/114] Adhere to style guide for variables --- modules/util/keepRight/keepRight_error.js | 61 ++++++++++++----------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 0c8a92151..e2c3a23f3 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -51,13 +51,8 @@ var keepRightSchemaFromWeb = { }; export function parseErrorDescriptions(entity) { - if (!(entity instanceof krError)) return; - - // find the matching template from the error schema - var errorType = '_' + entity.error_type; - var matchingTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; - if (!matchingTemplate) return; - + var parsedDetails = {}; + var html_re = new RegExp(/<\/[a-z][\s\S]*>/); var commonEntities = [ 'node', 'way', @@ -68,9 +63,15 @@ export function parseErrorDescriptions(entity) { 'riverbank' ]; // TODO: expand this list, or implement a different translation function + var errorType; + var errorTemplate; + var errorDescription; + var errorRegex; + var errorMatch; + function fillPlaceholder(d) { return '' + d + ''; } - // arbitrary list of way IDs and their layer value in form: #ID(layer),#ID(layer),#ID(layer)... + // arbitrary list of form: #ID(layer),#ID(layer),#ID(layer)... function parseError231(list) { var newList = []; var items = list.split(','); @@ -89,41 +90,45 @@ export function parseErrorDescriptions(entity) { // layer has trailing ) layer = item[1].slice(0,-1); + // TODO: translation newList.push(id + ' (layer: ' + layer + ')'); }); return newList.join(', '); } - // regex pattern should capture groups to extract appropriate details - var errorDescription = entity.description; - var errorRe = new RegExp(matchingTemplate.description); + if (!(entity instanceof krError)) return; - var errorMatch = errorRe.exec(errorDescription); + // find the matching template from the error schema + errorType = '_' + entity.error_type; + errorTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; + if (!errorTemplate) return; + // regex pattern should match description with variable details captured as groups + errorDescription = entity.description; + errorRegex = new RegExp(errorTemplate.description); + errorMatch = errorRegex.exec(errorDescription); if (!errorMatch) { // TODO: Remove, for regex dev testing - console.log('Unmatched:', errorType, errorDescription, errorRe); + console.log('Unmatched:', errorType, errorDescription, errorRegex); return; } - var parsedDetails = {}; - var html_re = new RegExp(/<\/[a-z][\s\S]*>/); + errorMatch.forEach(function(group, index) { + var idType; - // index 0 is the whole match, groups start from 1 - for (var i = 1; i < errorMatch.length; i++) { - var group = errorMatch[i]; - - // Clean and link IDs if present in the error - if ('IDs' in matchingTemplate && matchingTemplate.IDs[i-1]) { - var prefix = matchingTemplate.IDs[i-1]; + // index 0 is the whole match, skip it + if (!index) return; + // Clean and link IDs if present in the group + idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; + if (idType) { // some errors have more complex ID lists/variance - if (prefix === '231') { + if (idType === '231') { group = parseError231(group); - } else if (['n','w','r'].includes(prefix)) { - // wrap with linking span if simple case - group = fillPlaceholder(prefix + group); + } else if (['n','w','r'].includes(idType)) { + // simple case just needs a linking span + group = fillPlaceholder(idType + group); } } else if (html_re.test(group)) { // escape any html @@ -135,8 +140,8 @@ export function parseErrorDescriptions(entity) { group = t('QA.keepRight.entities.' + group); } - parsedDetails['var' + i] = group; - } + parsedDetails['var' + index] = group; + }); return parsedDetails; } From 2b1e37ab78caca2d026391411ced6b3e114fe466 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sun, 2 Sep 2018 12:25:36 +0100 Subject: [PATCH 027/114] Handle simple error descriptions explicitly This adds a flag to the error schema to explicitly say whether to parse the description with regex or not. Prevents us from having to escape special characters in fixed strings and is a minor optimisation. --- modules/util/keepRight/errorSchema.json | 159 ++++++++++++++-------- modules/util/keepRight/keepRight_error.js | 3 + 2 files changed, 108 insertions(+), 54 deletions(-) diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index d1ee09d7e..8c4e4c1c9 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -4,17 +4,20 @@ "errors": { "_30": { "title": "non-closed_areas", - "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop" + "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop", + "regex": true }, "_40": { "title": "dead-ended one-ways", "description": "The first node \\(id (\\d+)\\) of this one-way is not connected to any other way", - "IDs": ["n"] + "IDs": ["n"], + "regex": true }, "_41": { "title": "", "description": "The last node \\(id (\\d+)\\) of this one-way is not connected to any other way", - "IDs": ["n"] + "IDs": ["n"], + "regex": true }, "_42": { "title": "", @@ -27,11 +30,13 @@ "_50": { "title": "almost-junctions", "description": "This node is very close but not connected to way #(\\d+)", - "IDs": ["w"] + "IDs": ["w"], + "regex": true }, "_70": { "title": "missing tags", - "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="" + "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", + "regex": true }, "_71": { "title": "", @@ -47,11 +52,13 @@ }, "_100": { "title": "places of worship without religion", - "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag" + "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag", + "regex": true }, "_110": { "title": "point of interest without name", - "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag" + "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag", + "regex": true }, "_120": { "title": "ways without nodes", @@ -71,7 +78,8 @@ }, "_170": { "title": "FIXME tagged items", - "description": "(.*)" + "description": "(.*)", + "regex": true }, "_180": { "title": "relations without type", @@ -84,42 +92,50 @@ "_191": { "title": "highway-highway", "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_192": { "title": "highway-waterway", "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_193": { "title": "highway-riverbank", "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_194": { "title": "waterway-waterway", "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_195": { "title": "cycleway-cycleway", "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_196": { "title": "highway-cycleway", "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_197": { "title": "cycleway-waterway", "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_198": { "title": "cycleway-riverbank", "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_200": { "title": "overlapping ways", @@ -128,42 +144,50 @@ "_201": { "title": "highway-highway", "description": "This (highway) overlaps the (highway) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_202": { "title": "highway-waterway", "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_203": { "title": "highway-riverbank", "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_204": { "title": "waterway-waterway", "description": "This (waterway) overlaps the (waterway) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_205": { "title": "cycleway-cycleway", "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_206": { "title": "highway-cycleway", "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_207": { "title": "cycleway-waterway", "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_208": { "title": "cycleway-riverbank", "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"] + "IDs": ["", "", "w"], + "regex": true }, "_210": { "title": "loopings", @@ -172,8 +196,9 @@ "_211": { "title": "", "description": "This way contains more than one node at least twice. Nodes are #(\\d+), ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", - "IDs": ["n", ""], - "TODO": "Second group is arbitrary list of node IDs in form: #ID, #ID, #ID..." + "IDs": ["n"], + "TODO": "Second group is arbitrary list of node IDs in form: #ID, #ID, #ID...", + "regex": true }, "_212": { "title": "", @@ -181,11 +206,13 @@ }, "_220": { "title": "misspelled tags", - "description": "This (node|way|relation) is tagged '([\\w:]+)=(.+)' where "(\\2|\\3)" looks like "([\\w\\s]+)"" + "description": "This (node|way|relation) is tagged '([\\w:]+)=(.+)' where "(\\2|\\3)" looks like "([\\w\\s]+)"", + "regex": true }, "_221": { "title": "", - "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)" + "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)", + "regex": true }, "_230": { "title": "layer conflicts", @@ -194,11 +221,13 @@ "_231": { "title": "mixed layers intersection", "description": "This node is a junction of ways on different layers: ((?:#\\d+\\(-?\\d+\\),?)+)", - "IDs": ["231"] + "IDs": ["231"], + "regex": true }, "_232": { "title": "strange layers", - "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange" + "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange", + "regex": true }, "_270": { "title": "motorways connected directly", @@ -214,19 +243,23 @@ }, "_282": { "title": "missing admin level", - "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*" + "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*", + "regex": true }, "_283": { "title": "no closed loop", - "description": "The boundary of (.+) is not closed-loop" + "description": "The boundary of (.+) is not closed-loop", + "regex": true }, "_284": { "title": "splitting boundary", - "description": "The boundary of (.+) splits here" + "description": "The boundary of (.+) splits here", + "regex": true }, "_285": { "title": "admin_level too high", - "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" + "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", + "regex": true }, "_290": { "title": "restrictions", @@ -234,39 +267,47 @@ }, "_291": { "title": "missing type", - "description": "This turn-restriction has no (?:known )?restriction type" + "description": "This turn-restriction has no (?:known )?restriction type", + "regex": true }, "_292": { "title": "missing from way", - "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)" + "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)", + "regex": true }, "_293": { "title": "missing to way", - "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)" + "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)", + "regex": true }, "_294": { "title": "from or to not a way", "description": "From- and To-members of turn restrictions need to be ways\\. (.+)", - "TODO": "Group can be any combination of to/from: to node #ID | from node #ID | to relation #ID | from relation #ID" + "TODO": "Group can be any combination of to/from: to node #ID | from node #ID | to relation #ID | from relation #ID", + "regex": true }, "_295": { "title": "via is not on the way ends", "description": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)", - "IDs": ["n", "w"] + "IDs": ["n", "w"], + "regex": true }, "_296": { "title": "wrong restriction angle", - "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?" + "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?", + "regex": true }, "_297": { "title": "wrong direction of to member", "description": "wrong direction of to way (\\d+)", - "IDs": ["w"] + "IDs": ["w"], + "regex": true }, "_298": { "title": "already restricted by oneway", "description": "entry already prohibited by oneway tag on (\\d+)", - "IDs": ["w"] + "IDs": ["w"], + "regex": true }, "_310": { "title": "roundabouts", @@ -274,33 +315,39 @@ }, "_311": { "title": "not closed loop", - "description": "This way is part of a roundabout but is not closed-loop\\. \\(split carriageways approaching a roundabout should not be tagged as roundabout\\)" + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" }, "_312": { "title": "wrong direction", - "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around" + "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", + "regex": true }, "_313": { "title": "faintly connected", - "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three" + "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three", + "regex": true }, "_320": { "title": "*_link connections", - "description": "This way is tagged as highway=(\\w+)_link but doesn''t have a connection to any other \\1 or \\1_link" + "description": "This way is tagged as highway=(\\w+)_link but doesn''t have a connection to any other \\1 or \\1_link", + "regex": true }, "_350": { "title": "bridge-tags", "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", - "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value..." + "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...", + "regex": true }, "_370": { "title": "doubled places", "description": "This node has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and seems to be redundand", - "IDs": ["w", ""] + "IDs": ["w"], + "regex": true }, "_380": { "title": "non-physical use of sport-tag", - "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway" + "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway", + "regex": true }, "_400": { "title": "geometry glitches", @@ -309,7 +356,8 @@ "_401": { "title": "missing turn restriction", "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way (\\1|\\2) to (\\1|\\2)", - "IDs": ["w", "w", "w", "w"] + "IDs": ["w", "w", "w", "w"], + "regex": true }, "_402": { "title": "impossible angles", @@ -322,15 +370,17 @@ "_411": { "title": "http error", "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", - "NOTE": "It seems the HTML attributes don't have quotes when the code reads them" + "regex": true }, "_412": { "title": "domain hijacking", - "description": "Possible domain squatting: \\1. Suspicious text is: ''(.+)''" + "description": "Possible domain squatting: \\1. Suspicious text is: ''(.+)''", + "regex": true }, "_413": { "title": "non-match", - "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)" + "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)", + "regex": true } }, "warnings": { @@ -348,7 +398,8 @@ }, "_360": { "title": "language unknown", - "description": "It would be nice if this (node|way|relation) had an additional tag ''name:XX=(\\.+?)''where XX shows the language of its name ''\\2''" + "description": "It would be nice if this (node|way|relation) had an additional tag ''name:XX=(\\.+?)''where XX shows the language of its name ''\\2''", + "regex": true }, "_390": { "title": "missing tracktype", diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index e2c3a23f3..887a607e1 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -104,6 +104,9 @@ export function parseErrorDescriptions(entity) { errorTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; if (!errorTemplate) return; + // some descriptions are just fixed text + if (!('regex' in errorTemplate)) return; + // regex pattern should match description with variable details captured as groups errorDescription = entity.description; errorRegex = new RegExp(errorTemplate.description); From 005927dae3a4bf18800a446cacd1cc50cf41e335 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Sun, 2 Sep 2018 13:47:47 +0100 Subject: [PATCH 028/114] Fix parsing of error type 211 --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- modules/util/keepRight/errorSchema.json | 5 ++- modules/util/keepRight/keepRight_error.js | 37 ++++++++++++++++++----- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 164abb139..cdf3c123f 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1067,7 +1067,7 @@ en: description: 'These errors contain self intersecting ways.' _211: title: '' - description: 'This way contains more than one node multiple times. Nodes are {var1}, {var2}. This may or may not be an error.' + description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' _212: title: '' description: 'This way has only two different nodes and contains one of them more than once.' diff --git a/dist/locales/en.json b/dist/locales/en.json index 4b91ec465..08e07cbd6 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1312,7 +1312,7 @@ }, "_211": { "title": "", - "description": "This way contains more than one node multiple times. Nodes are {var1}, {var2}. This may or may not be an error." + "description": "This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error." }, "_212": { "title": "", diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 8c4e4c1c9..b1005cf3a 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -195,9 +195,8 @@ }, "_211": { "title": "", - "description": "This way contains more than one node at least twice. Nodes are #(\\d+), ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", - "IDs": ["n"], - "TODO": "Second group is arbitrary list of node IDs in form: #ID, #ID, #ID...", + "description": "This way contains more than one node at least twice\\. Nodes are ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", + "IDs": ["211"], "regex": true }, "_212": { diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 887a607e1..3e785cf4d 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -71,7 +71,21 @@ export function parseErrorDescriptions(entity) { function fillPlaceholder(d) { return '' + d + ''; } - // arbitrary list of form: #ID(layer),#ID(layer),#ID(layer)... + // arbitrary node list of form: #ID, #ID, #ID... + function parseError211(list) { + var newList = []; + var items = list.split(', '); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + + // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... function parseError231(list) { var newList = []; var items = list.split(','); @@ -123,18 +137,25 @@ export function parseErrorDescriptions(entity) { // index 0 is the whole match, skip it if (!index) return; - // Clean and link IDs if present in the group + // link IDs if present in the group idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; if (idType) { - // some errors have more complex ID lists/variance - if (idType === '231') { - group = parseError231(group); - } else if (['n','w','r'].includes(idType)) { + switch (idType) { // simple case just needs a linking span - group = fillPlaceholder(idType + group); + case 'n': + case 'w': + case 'r': + group = fillPlaceholder(idType + group); + break; + // some errors have more complex ID lists/variance + case '211': + group = parseError211(group); + break; + case '231': + group = parseError231(group); } } else if (html_re.test(group)) { - // escape any html + // escape any html in non-IDs group = '\\' + group + '\\'; } From da23bf79a87a81c41f1333d99f15f422f3844301 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Mon, 3 Sep 2018 12:24:54 +0100 Subject: [PATCH 029/114] Fix parsing of error type 294 --- modules/util/keepRight/errorSchema.json | 4 +-- modules/util/keepRight/keepRight_error.js | 33 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index b1005cf3a..d7e951319 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -281,8 +281,8 @@ }, "_294": { "title": "from or to not a way", - "description": "From- and To-members of turn restrictions need to be ways\\. (.+)", - "TODO": "Group can be any combination of to/from: to node #ID | from node #ID | to relation #ID | from relation #ID", + "description": "From- and To-members of turn restrictions need to be ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)", + "IDs": ["294"], "regex": true }, "_295": { diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 3e785cf4d..e4fec3955 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -111,6 +111,36 @@ export function parseErrorDescriptions(entity) { return newList.join(', '); } + // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... + function parseError294(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var role; + var idType; + var id; + + // item of form "from/to node/relation #ID" + item = item.split(' '); + + // to/from role is more clear in quotes + role = '"' + item[0] + '"'; + + // first letter of node/relation provides the type + idType = item[1].slice(0,1); + + // ID has # at the front + id = item[2].slice(1); + id = fillPlaceholder(idType + id); + + item = [role, item[1], id].join(' '); + newList.push(item); + }); + + return newList.join(', '); + } + if (!(entity instanceof krError)) return; // find the matching template from the error schema @@ -153,6 +183,9 @@ export function parseErrorDescriptions(entity) { break; case '231': group = parseError231(group); + break; + case '294': + group = parseError294(group); } } else if (html_re.test(group)) { // escape any html in non-IDs From 9bd772748a8d5958f54257ff75f5c2b9795254fd Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Mon, 3 Sep 2018 12:55:11 +0100 Subject: [PATCH 030/114] Convert warnings to regex These aren't shown in the layer currently, but for future use this should work --- data/core.yaml | 4 ++-- dist/locales/en.json | 4 ++-- modules/util/keepRight/errorSchema.json | 11 +++++++---- modules/util/keepRight/keepRight_error.js | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 8 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index cdf3c123f..ac4e1a4da 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1185,13 +1185,13 @@ en: description: 'There is more than one node in this spot. Offending node IDs: {var1}.' _60: title: 'depreciated tags' - description: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' + description: 'This {var1} uses deprecated tag "{var2}"="{var3}". Please use {var4} instead!' _300: title: 'missing maxspeed' description: 'missing maxspeed tag.' _360: title: 'language unknown' - description: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}.' + description: 'It would be nice if this {var1} had an additional tag "name:XX"="{var2}" where XX shows the language of its name "{var2}".' _390: title: 'missing tracktype' description: This track doesn't have a tracktype. diff --git a/dist/locales/en.json b/dist/locales/en.json index 08e07cbd6..670a7108c 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1470,7 +1470,7 @@ }, "_60": { "title": "depreciated tags", - "description": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" + "description": "This {var1} uses deprecated tag \"{var2}\"=\"{var3}\". Please use {var4} instead!" }, "_300": { "title": "missing maxspeed", @@ -1478,7 +1478,7 @@ }, "_360": { "title": "language unknown", - "description": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}." + "description": "It would be nice if this {var1} had an additional tag \"name:XX\"=\"{var2}\" where XX shows the language of its name \"{var2}\"." }, "_390": { "title": "missing tracktype", diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index d7e951319..2788ede1a 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -373,7 +373,7 @@ }, "_412": { "title": "domain hijacking", - "description": "Possible domain squatting: \\1. Suspicious text is: ''(.+)''", + "description": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''", "regex": true }, "_413": { @@ -385,11 +385,14 @@ "warnings": { "_20": { "title": "multiple nodes on the same spot", - "description": "There is more than one node in this spot. Offending node IDs: {$1}" + "description": "There is more than one node in this spot\\. Offending node IDs: ((?:#\\d+,?)+)", + "IDs": ["20"], + "regex": true }, "_60": { "title": "depreciated tags", - "description": "This {$1} uses deprecated tag {$2} = {$3}. Please use {$4} instead!" + "description": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead!", + "regex": true }, "_300": { "title": "missing maxspeed", @@ -397,7 +400,7 @@ }, "_360": { "title": "language unknown", - "description": "It would be nice if this (node|way|relation) had an additional tag ''name:XX=(\\.+?)''where XX shows the language of its name ''\\2''", + "description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'", "regex": true }, "_390": { diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index e4fec3955..7074db963 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -141,6 +141,20 @@ export function parseErrorDescriptions(entity) { return newList.join(', '); } + // arbitrary node list of form: #ID,#ID,#ID... + function parseWarning20(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + if (!(entity instanceof krError)) return; // find the matching template from the error schema @@ -186,6 +200,9 @@ export function parseErrorDescriptions(entity) { break; case '294': group = parseError294(group); + break; + case '20': + group = parseWarning20(group); } } else if (html_re.test(group)) { // escape any html in non-IDs From 1f7455fdad592c6c5baa5e3335a58c095e484de1 Mon Sep 17 00:00:00 2001 From: SilentSpike Date: Mon, 3 Sep 2018 18:45:30 +0100 Subject: [PATCH 031/114] Add missing error types 73, 74 and 75 --- data/core.yaml | 9 +++++++++ dist/locales/en.json | 12 ++++++++++++ modules/util/keepRight/errorSchema.json | 16 ++++++++++++++-- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index ac4e1a4da..0e4416504 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -981,6 +981,15 @@ en: _72: title: 'node without tags' description: 'This node is not member of any way and doesn''t have any tags.' + _73: + title: 'tracktype with no highway tag' + description: 'This way has a "tracktype" tag but no "highway" tag.' + _74: + title: 'empty tag' + description: 'This {var1} has an empty tag: "{var2}".' + _75: + title: 'name without tags' + description: 'This {var1} has a name ("{var2}") but no other tags.' _90: title: 'motorways without ref' description: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag.' diff --git a/dist/locales/en.json b/dist/locales/en.json index 670a7108c..9a7e04efb 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1198,6 +1198,18 @@ "title": "node without tags", "description": "This node is not member of any way and doesn't have any tags." }, + "_73": { + "title": "tracktype with no highway tag", + "description": "This way has a \"tracktype\" tag but no \"highway\" tag." + }, + "_74": { + "title": "empty tag", + "description": "This {var1} has an empty tag: \"{var2}\"." + }, + "_75": { + "title": "name without tags", + "description": "This {var1} has a name (\"{var2}\") but no other tags." + }, "_90": { "title": "motorways without ref", "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag." diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 2788ede1a..11c008312 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -35,8 +35,7 @@ }, "_70": { "title": "missing tags", - "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", - "regex": true + "description": "" }, "_71": { "title": "", @@ -46,6 +45,19 @@ "title": "", "description": "This node is not member of any way and does not have any tags" }, + "_73": { + "title": "", + "description": "This way has a tracktype tag but no highway tag" + }, + "_74": { + "title": "missing tags", + "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", + "regex": true + }, + "_75": { + "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", + "regex": true + }, "_90": { "title": "motorways without ref", "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" From ee00635cc42c7539c648eea3e8e5869337dd9796 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Wed, 5 Sep 2018 17:01:42 -0600 Subject: [PATCH 032/114] updated: some error colors; fixed: error 320, 220 --- css/80_app.css | 14 +-- data/core.yaml | 3 +- dist/locales/en.json | 5 +- modules/services/keepRight.js | 15 ++- modules/util/keepRight/errorSchema.json | 8 +- modules/util/keepRight/index.js | 5 +- modules/util/keepRight/keepRight_error.js | 114 +--------------------- modules/util/keepRight/parse_error.js | 113 +++++++++++++++++++++ 8 files changed, 151 insertions(+), 126 deletions(-) create mode 100644 modules/util/keepRight/parse_error.js diff --git a/css/80_app.css b/css/80_app.css index 81f46f468..206e48218 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2572,7 +2572,7 @@ input.key-trap { .kr_error_type_41, .kr_error_type_42, .kr_error_type_43 { - color: #894668; + color: #fd007f; } .kr_error_type_50 { @@ -2618,7 +2618,7 @@ input.key-trap { } .kr_error_type_180 { - color: #09ef12; + color: #01ff0a; } .kr_error_type_190, @@ -2642,7 +2642,7 @@ input.key-trap { .kr_error_type_206, .kr_error_type_207, .kr_error_type_208 { - color: #71f264; + color: #fdbf6f; } .kr_error_type_210, @@ -2659,7 +2659,7 @@ input.key-trap { .kr_error_type_230, .kr_error_type_231, .kr_error_type_232 { - color: #5f775c; + color: #b15928; } .kr_error_type_270 { @@ -2684,7 +2684,7 @@ input.key-trap { .kr_error_type_296, .kr_error_type_297, .kr_error_type_298 { - color: #d1dce7; + color: #a6cee3; } .kr_error_type_310, @@ -2699,7 +2699,7 @@ input.key-trap { } .kr_error_type_350 { - color: #4d719c; + color: #ffff99; } .kr_error_type_370 { @@ -2713,7 +2713,7 @@ input.key-trap { .kr_error_type_400, .kr_error_type_401, .kr_error_type_402 { - color: #b20e36; + color: #b64f69; } .kr_error_type_410, diff --git a/data/core.yaml b/data/core.yaml index 0e4416504..63f48dfe9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1163,7 +1163,7 @@ en: description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' _370: title: 'doubled places' - description: 'This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant' + description: 'This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant.' _380: title: 'non-physical use of sport-tag' description: 'This way is tagged "sport"="{var1}" but has no physical tag like e.g. leisure, building, amenity or highway.' @@ -1173,6 +1173,7 @@ en: _401: title: 'missing turn restriction' description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.' + addition: ' from way {var3} to {var4}.' _402: title: 'impossible angles' description: 'this way bends in a very sharp angle here.' diff --git a/dist/locales/en.json b/dist/locales/en.json index 9a7e04efb..fcae6ba4b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1440,7 +1440,7 @@ }, "_370": { "title": "doubled places", - "description": "This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant" + "description": "This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant." }, "_380": { "title": "non-physical use of sport-tag", @@ -1452,7 +1452,8 @@ }, "_401": { "title": "missing turn restriction", - "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}." + "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.", + "addition": " from way {var3} to {var4}." }, "_402": { "title": "impossible angles", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 2889c8ec5..76f799fd9 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -8,7 +8,7 @@ import rbush from 'rbush'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { request as d3_request } from 'd3-request'; -import { geoExtent } from '../geo'; +import { geoExtent, geoVecAdd } from '../geo'; import { krError } from '../osm'; @@ -158,6 +158,19 @@ export default { var features = data.features.map(function(feature) { var loc = feature.geometry.coordinates; var props = feature.properties; + + // TODO: finish implementing overlapping error offset + // // if errors are coincident, move them apart slightly + // var coincident = false; + // var epsilon = 0.00001; + // do { + // if (coincident) { + // loc = geoVecAdd(loc, [epsilon, epsilon]); + // } + // var bbox = geoExtent(loc).bbox(); + // coincident = cache.rtree.search(bbox).length; + // } while (coincident); + var d = new krError ({ loc: loc, id: props.error_id, diff --git a/modules/util/keepRight/errorSchema.json b/modules/util/keepRight/errorSchema.json index 11c008312..447a37037 100644 --- a/modules/util/keepRight/errorSchema.json +++ b/modules/util/keepRight/errorSchema.json @@ -217,7 +217,7 @@ }, "_220": { "title": "misspelled tags", - "description": "This (node|way|relation) is tagged '([\\w:]+)=(.+)' where "(\\2|\\3)" looks like "([\\w\\s]+)"", + "description": "This (node|way|relation) is tagged '([\\w]+)(:([\\w]+))?=(.+)' where "(\\2|\\3|\\4|\\5)" looks like "([\\w\\s]+)"", "regex": true }, "_221": { @@ -340,7 +340,7 @@ }, "_320": { "title": "*_link connections", - "description": "This way is tagged as highway=(\\w+)_link but doesn''t have a connection to any other \\1 or \\1_link", + "description": "This way is tagged as highway=(\\w+)_link but doesn't have a connection to any other \\1 or \\1_link", "regex": true }, "_350": { @@ -362,11 +362,11 @@ }, "_400": { "title": "geometry glitches", - "description": "Impossible sharp angles on highways and junctions. These may be caused by missing turn restrictions on junctions or glitches along the linestring of ways" + "description": "" }, "_401": { "title": "missing turn restriction", - "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way (\\1|\\2) to (\\1|\\2)", + "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?", "IDs": ["w", "w", "w", "w"], "regex": true }, diff --git a/modules/util/keepRight/index.js b/modules/util/keepRight/index.js index 8bd2ca9a6..0f95dc9a7 100644 --- a/modules/util/keepRight/index.js +++ b/modules/util/keepRight/index.js @@ -1,2 +1,3 @@ -export { parseErrorDescriptions, clickLink } from './keepRight_error'; -export { errorTypes } from './errorSchema.json'; \ No newline at end of file +export { errorTypes } from './errorSchema.json'; +export { parseError } from './parse_error'; +export { parseErrorDescriptions, clickLink } from './keepRight_error'; \ No newline at end of file diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index 7074db963..f74a49ae1 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -4,8 +4,9 @@ import { t } from '../locale'; import { krError } from '../../osm'; import { errorTypes } from './errorSchema.json'; +import { parseError } from './parse_error'; -// TODO: remove these objects, here for reference +// TODO: for reference; remove var keepRightSchema = { 'schema': '', 'id': 0, @@ -38,7 +39,7 @@ var keepRightSchema = { 'txt4': '', 'txt5': '' }; - +// TODO: for reference; remove var keepRightSchemaFromWeb = { 'error_type': '192', 'object_type': 'way', @@ -69,92 +70,6 @@ export function parseErrorDescriptions(entity) { var errorRegex; var errorMatch; - function fillPlaceholder(d) { return '' + d + ''; } - - // arbitrary node list of form: #ID, #ID, #ID... - function parseError211(list) { - var newList = []; - var items = list.split(', '); - - items.forEach(function(item) { - // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - - // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... - function parseError231(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - var id; - var layer; - - // item of form "#ID(layer)" - item = item.split('('); - - // ID has # at the front - id = item[0].slice(1); - id = fillPlaceholder('w' + id); - - // layer has trailing ) - layer = item[1].slice(0,-1); - - // TODO: translation - newList.push(id + ' (layer: ' + layer + ')'); - }); - - return newList.join(', '); - } - - // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... - function parseError294(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - var role; - var idType; - var id; - - // item of form "from/to node/relation #ID" - item = item.split(' '); - - // to/from role is more clear in quotes - role = '"' + item[0] + '"'; - - // first letter of node/relation provides the type - idType = item[1].slice(0,1); - - // ID has # at the front - id = item[2].slice(1); - id = fillPlaceholder(idType + id); - - item = [role, item[1], id].join(' '); - newList.push(item); - }); - - return newList.join(', '); - } - - // arbitrary node list of form: #ID,#ID,#ID... - function parseWarning20(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - if (!(entity instanceof krError)) return; // find the matching template from the error schema @@ -183,27 +98,8 @@ export function parseErrorDescriptions(entity) { // link IDs if present in the group idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; - if (idType) { - switch (idType) { - // simple case just needs a linking span - case 'n': - case 'w': - case 'r': - group = fillPlaceholder(idType + group); - break; - // some errors have more complex ID lists/variance - case '211': - group = parseError211(group); - break; - case '231': - group = parseError231(group); - break; - case '294': - group = parseError294(group); - break; - case '20': - group = parseWarning20(group); - } + if (idType && group) { + group = parseError(group, idType); } else if (html_re.test(group)) { // escape any html in non-IDs group = '\\' + group + '\\'; diff --git a/modules/util/keepRight/parse_error.js b/modules/util/keepRight/parse_error.js new file mode 100644 index 000000000..37eed18dc --- /dev/null +++ b/modules/util/keepRight/parse_error.js @@ -0,0 +1,113 @@ +export function parseError(group, idType) { + + function fillPlaceholder(d) { return '' + d + ''; } + + // arbitrary node list of form: #ID, #ID, #ID... + function parseError211(list) { + var newList = []; + var items = list.split(', '); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + + // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... + function parseError231(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var id; + var layer; + + // item of form "#ID(layer)" + item = item.split('('); + + // ID has # at the front + id = item[0].slice(1); + id = fillPlaceholder('w' + id); + + // layer has trailing ) + layer = item[1].slice(0,-1); + + // TODO: translation + newList.push(id + ' (layer: ' + layer + ')'); + }); + + return newList.join(', '); + } + + // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... + function parseError294(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var role; + var idType; + var id; + + // item of form "from/to node/relation #ID" + item = item.split(' '); + + // to/from role is more clear in quotes + role = '"' + item[0] + '"'; + + // first letter of node/relation provides the type + idType = item[1].slice(0,1); + + // ID has # at the front + id = item[2].slice(1); + id = fillPlaceholder(idType + id); + + item = [role, item[1], id].join(' '); + newList.push(item); + }); + + return newList.join(', '); + } + + // TODO: Handle error 401 template addition + + // arbitrary node list of form: #ID,#ID,#ID... + function parseWarning20(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + + switch (idType) { + // simple case just needs a linking span + case 'n': + case 'w': + case 'r': + group = fillPlaceholder(idType + group); + break; + // some errors have more complex ID lists/variance + case '211': + group = parseError211(group); + break; + case '231': + group = parseError231(group); + break; + case '294': + group = parseError294(group); + break; + case '20': + group = parseWarning20(group); + } + + return group; +} \ No newline at end of file From cedf6955b5df6da8ca0aa76ac2a51181bd8320af Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Sun, 18 Nov 2018 19:31:10 -0800 Subject: [PATCH 033/114] added: notes and todos for keepRight --- dist/locales/en.json | 91 +++++++- modules/services/keepRight.js | 413 ++++++++++++++++++--------------- modules/svg/keepRight.js | 1 + modules/ui/keepRight_editor.js | 5 +- 4 files changed, 314 insertions(+), 196 deletions(-) diff --git a/dist/locales/en.json b/dist/locales/en.json index fcae6ba4b..bf82c098e 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -8287,9 +8287,6 @@ "SPW_PICC": { "name": "SPW(allonie) PICC numerical imagery" }, - "US-TIGER-Roads-2012": { - "name": "TIGER Roads 2012" - }, "US-TIGER-Roads-2014": { "description": "At zoom level 16+, public domain map data from the US Census. At lower zooms, only changes since 2006 minus changes already incorporated into OpenStreetMap", "name": "TIGER Roads 2014" @@ -8298,6 +8295,10 @@ "description": "Yellow = Public domain map data from the US Census. Red = Data not found in OpenStreetMap", "name": "TIGER Roads 2017" }, + "US-TIGER-Roads-2018": { + "description": "Yellow = Public domain map data from the US Census. Red = Data not found in OpenStreetMap", + "name": "TIGER Roads 2018" + }, "US_Forest_Service_roads_overlay": { "description": "Highway: Green casing = unclassified. Brown casing = track. Surface: gravel = light brown fill, Asphalt = black, paved = gray, ground =white, concrete = blue, grass = green. Seasonal = white bars", "name": "U.S. Forest Roads Overlay" @@ -8314,6 +8315,12 @@ }, "name": "UrbIS-Ortho 2017" }, + "UrbISOrtho2018": { + "attribution": { + "text": "Realized by means of Brussels UrbIS®© - Distribution & Copyright CIRB" + }, + "name": "UrbIS-Ortho 2018" + }, "UrbisAdmFR": { "attribution": { "text": "Realized by means of Brussels UrbIS®© - Distribution & Copyright CIRB" @@ -8371,6 +8378,7 @@ "name": "basemap.at Orthofoto" }, "eufar-balaton": { +<<<<<<< HEAD "attribution": { "text": "EUFAR Balaton ortofotó 2010" }, @@ -8399,10 +8407,62 @@ "name": "Japan GSI Standard Map" }, "hike_n_bike": { +======= +>>>>>>> added: notes and todos for keepRight "attribution": { - "text": "© OpenStreetMap contributors" + "text": "EUFAR Balaton ortofotó 2010" }, - "name": "Hike & Bike" + "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" + }, + "helsingborg-orto": { + "attribution": { + "text": "© Helsingborg municipality" + }, + "description": "Orthophotos from the municipality of Helsingborg 2016, public domain", + "name": "Helsingborg Orthophoto" + }, + "kalmar-orto-2014": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for the north coast of the municipality of Kalmar 2014", + "name": "Kalmar North Orthophoto 2014" + }, + "kalmar-orto-2016": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for the south coast of the municipality of Kalmar 2016", + "name": "Kalmar South Orthophoto 2016" + }, + "kalmar-orto-2018": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for urban areas of the municipality of Kalmar 2018", + "name": "Kalmar Urban Orthophoto 2018" }, "kelkkareitit": { "attribution": { @@ -8425,6 +8485,20 @@ "description": "Mosaic of Swedish orthophotos from the period 1970–1980. Is under construction.", "name": "Lantmäteriet Historic Orthophoto 1975" }, + "lantmateriet-topowebb": { + "attribution": { + "text": "© Lantmäteriet, CC0" + }, + "description": "Topographic map of Sweden 1:50 000", + "name": "Lantmäteriet Topographic Map" + }, + "linkoping-orto": { + "attribution": { + "text": "© Linköping municipality" + }, + "description": "Orthophotos from the municipality of Linköping 2010, open data", + "name": "Linköping Orthophoto" + }, "mapbox_locator_overlay": { "attribution": { "text": "Terms & Feedback" @@ -8489,6 +8563,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" diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 76f799fd9..34a455ba6 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -8,253 +8,288 @@ import rbush from 'rbush'; import { dispatch as d3_dispatch } from 'd3-dispatch'; import { request as d3_request } from 'd3-request'; -import { geoExtent, geoVecAdd } from '../geo'; - +import { geoExtent } from '../geo'; +import { services } from './index'; import { krError } from '../osm'; -import { - utilRebind, - utilTiler, - utilQsString -} from '../util'; - +import { utilRebind, utilTiler, utilQsString } from '../util'; var tiler = utilTiler(); -var dispatch = d3_dispatch('authLoading', 'authDone', 'change', 'loading', 'loaded', 'loadedKeepRight'); +var dispatch = d3_dispatch( + 'authLoading', + 'authDone', + 'change', + 'loading', + 'loaded', + 'loadedKeepRight' +); -var _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; +var _keepRightCache = { + loaded: {}, + inflight: {}, + inflightPost: {}, + keepRight: {}, + rtree: rbush() +}; var _off; -var _keepRightZoom = 16; +var _keepRightZoom = 14; var apiBase = 'https://www.keepright.at/'; - function abortRequest(i) { - if (i) { - i.abort(); - } + if (i) { + i.abort(); + } } - function abortUnwantedRequests(cache, tiles) { - _forEach(cache.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { return k === tile.id; }); - if (!wanted) { - abortRequest(v); - delete cache.inflight[k]; - } - }); + _forEach(cache.inflight, function(v, k) { + var wanted = _find(tiles, function(tile) { + return k === tile.id; + }); + if (!wanted) { + abortRequest(v); + delete cache.inflight[k]; + } + }); } - function encodeErrorRtree(error) { - return { - minX: error.loc[0], - minY: error.loc[1], - maxX: error.loc[0], - maxY: error.loc[1], - data: error - }; + return { + minX: error.loc[0], + minY: error.loc[1], + maxX: error.loc[0], + maxY: error.loc[1], + data: error + }; } - // replace or remove error from rtree function updateRtree(item, replace) { - _keepRightCache.rtree.remove(item, function isEql(a, b) { return a.data.id === b.data.id; }); + _keepRightCache.rtree.remove(item, function isEql(a, b) { + return a.data.id === b.data.id; + }); - if (replace) { - _keepRightCache.rtree.insert(item); - } + if (replace) { + _keepRightCache.rtree.insert(item); + } } - export default { - init: function() { - if (!_keepRightCache) { - this.reset(); - } + init: function() { + if (!_keepRightCache) { + this.reset(); + } - this.event = utilRebind(this, dispatch, 'on'); - }, + this.event = utilRebind(this, dispatch, 'on'); + }, - reset: function() { - _forEach(_keepRightCache.inflight, abortRequest); + reset: function() { + _forEach(_keepRightCache.inflight, abortRequest); - _keepRightCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush()}; - }, + _keepRightCache = { + loaded: {}, + inflight: {}, + keepRight: {}, + rtree: rbush() + }; + }, - loadKeepRightErrors: function(context, projection, options, callback) { - options = _extend({ 'format': 'geojson' }, options); - if (_off) return; + loadKeepRightErrors: function(context, projection, options, callback) { + options = _extend({ format: 'geojson' }, options); // set format to geojson + if (_off) return; - var cache = _keepRightCache; + var cache = _keepRightCache; var that = this; - var path = apiBase + - 'export.php?' + - 'format=' + options.format + - '&ch=' + options.ch.join() + '&'; - // determine the needed tiles to cover the view - var tiles = tiler.zoomExtent([_keepRightZoom, _keepRightZoom]).getTiles(projection); + // NOTE: the KeepRight API doesn't seem to load + var path = + apiBase + + 'export.php?' + + 'format=' + + options.format + + '&st=' + + options.st + + '&ch=' + + options.ch.join() + + '&'; - // abort inflight requests that are no longer needed - var hadRequests = !_isEmpty(cache.inflight); - abortUnwantedRequests(cache, tiles); - if (hadRequests && _isEmpty(cache.inflight)) { - dispatch.call('loaded'); // stop the spinner - } + // determine the needed tiles to cover the view + var tiles = tiler + .zoomExtent([_keepRightZoom, _keepRightZoom]) + .getTiles(projection); - // issue new requests.. - tiles.forEach(function(tile) { - if (cache.loaded[tile.id] || cache.inflight[tile.id]) return; - if (_isEmpty(cache.inflight)) { - dispatch.call('loading'); // start the spinner - } + // abort inflight requests that are no longer needed + var hadRequests = !_isEmpty(cache.inflight); + abortUnwantedRequests(cache, tiles); + if (hadRequests && _isEmpty(cache.inflight)) { + dispatch.call('loaded'); // stop the spinner + } - var rect = tile.extent.rectangle(); - var nextPath = path + - utilQsString({ - left: rect[0], - bottom: [3], - right: rect[2], - top: rect[1] - }); + // issue new requests.. + tiles.forEach(function(tile) { + if (cache.loaded[tile.id] || cache.inflight[tile.id]) return; + if (_isEmpty(cache.inflight)) { + dispatch.call('loading'); // start the spinner + } + var rect = tile.extent.rectangle(); + var nextPath = + path + + utilQsString({ + left: rect[0], + bottom: [3], + right: rect[2], + top: rect[1] + }); - var options = {}; // TODO: implement + var options = {}; // TODO: implement - cache.inflight[tile.id] = that.loadFromAPI( - nextPath, - function(err, data) { - if (err || !data.features || !data.features.length) return; + cache.inflight[tile.id] = that.loadFromAPI( + nextPath, + function(err, data) { + if (err || !data.features || !data.features.length) return; - cache.loaded[tile.id] = true; - delete cache.inflight[tile.id]; + cache.loaded[tile.id] = true; + delete cache.inflight[tile.id]; - if (callback) { - callback(err, _extend({ data: data }, tile)); - } - if (_isEmpty(cache.inflight)) { - dispatch.call('loaded'); // stop the spinner - } - }, - options - ); - }); - }, + if (callback) { + callback(err, _extend({ data: data }, tile)); + } + if (_isEmpty(cache.inflight)) { + dispatch.call('loaded'); // stop the spinner + } + }, + options + ); + }); + }, - loadFromAPI: function(path, callback, options) { - var cache = _keepRightCache; + loadFromAPI: function(path, callback, options) { + var cache = _keepRightCache; - return d3_request(path) - .mimeType('application/json') // TODO: only have this as a response if the input format is json - .header('Content-type', 'application/x-www-form-urlencoded') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { + return d3_request(path) + .mimeType('application/json') // TODO: only have this as a response if the input format is json + .header('Content-type', 'application/x-www-form-urlencoded') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .get(function(err, data) { + var features = data.features + .map(function(feature) { + var loc = feature.geometry.coordinates; + var props = feature.properties; - var features = data.features.map(function(feature) { - var loc = feature.geometry.coordinates; - var props = feature.properties; + // TODO: finish implementing overlapping error offset + // // if errors are coincident, move them apart slightly + // var coincident = false; + // var epsilon = 0.00001; + // do { + // if (coincident) { + // loc = geoVecAdd(loc, [epsilon, epsilon]); + // } + // var bbox = geoExtent(loc).bbox(); + // coincident = cache.rtree.search(bbox).length; + // } while (coincident); - // TODO: finish implementing overlapping error offset - // // if errors are coincident, move them apart slightly - // var coincident = false; - // var epsilon = 0.00001; - // do { - // if (coincident) { - // loc = geoVecAdd(loc, [epsilon, epsilon]); - // } - // var bbox = geoExtent(loc).bbox(); - // coincident = cache.rtree.search(bbox).length; - // } while (coincident); + var d = new krError({ + loc: loc, + id: props.error_id, + comment: props.comment || null, + description: props.description || '', + error_id: props.error_id, + error_type: props.error_type, + object_id: props.object_id, + object_type: props.object_type, + schema: props.schema, + title: props.title + }); - var d = new krError ({ - loc: loc, - id: props.error_id, - comment: props.comment || null, - description: props.description || '', - error_id: props.error_id, - error_type: props.error_type, - object_id: props.object_id, - object_type: props.object_type, - schema: props.schema, - title: props.title - }); + cache.keepRight[d.id] = d; - cache.keepRight[d.id] = d; + return { + minX: loc[0], + minY: loc[1], + maxX: loc[0], + maxY: loc[1], + data: d + }; + }) + .filter(Boolean); - return { - minX: loc[0], minY: loc[1], maxX: loc[0], maxY: loc[1], data: d - }; + cache.rtree.load(features); + dispatch.call('loadedKeepRight'); - }).filter(Boolean); + callback(err, data); + }); + }, - cache.rtree.load(features); - dispatch.call('loadedKeepRight'); + postKeepRightUpdate: function(update, callback) { + if (!services.osm.authenticated()) { + return callback({ message: 'Not Authenticated', status: -3 }, update); + } + if (_keepRightCache.inflightPost[update.id]) { + return callback( + { message: 'Error update already inflight', status: -2 }, update); + } - callback(err, data); - }); - }, + var path = apiBase + 'comment.php?'; + if (update.state) { + path += '&st=' + update.state; + } + if (update.newComment) { + path += '&' + utilQsString({ co: update.newComment }); + } - postKeepRightUpdate: function(d, callback) { - // TODO: check if a user is authenticated - // if (!this.authenticated()) { - // return callback({ message: 'Not Authenticated', status: -3 }, d); - // } - // if (_keepRightCache.inflightPost[d.id]) { - // return callback({ message: 'Error update already inflight', status: -2 }, d); - // } + path += '&schema=' + update.schema + '&id=' + update.error_id; - var path = apiBase + 'comment.php?'; - if (d.state) { path += '&st=' + d.state; } - if (d.newComment) { path += '&' + utilQsString({'co': d.newComment }); } + _keepRightCache.inflightPost[update.id] = d3_request(path) + .mimeType('application/json') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .post(function(err, data) { + delete _keepRightCache.inflightPost[update.id]; + if (err) { return callback(err); } - path += '&schema=' + d.schema + '&id=' + d.error_id; + console.log('data ', data); + }); - d3_request(path) - .mimeType('application/json') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .post(function(err, data) { - console.log('error:', err); - console.log('data: ', data); - }); - }, + // NOTE: This throws a CORS error, but it seems successful? + }, - // get all cached errors covering the viewport - keepRight: 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(); + // get all cached errors covering the viewport + keepRight: function(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - return _keepRightCache.rtree.search(bbox) - .map(function(d) { return d.data; }); - }, + return _keepRightCache.rtree.search(bbox).map(function(d) { + return d.data; + }); + }, - // get a single error from the cache - getError: function(id) { - return _keepRightCache.keepRight[id]; - }, + // get a single error from the cache + getError: function(id) { + return _keepRightCache.keepRight[id]; + }, - // replace a single error in the cache - replaceError: function(error) { - if (!(error instanceof krError) || !error.id) return; + // replace a single error in the cache + replaceError: function(error) { + if (!(error instanceof krError) || !error.id) return; - _keepRightCache.keepRight[error.id] = error; - updateRtree(encodeErrorRtree(error), true); // true = replace - return error; - }, + _keepRightCache.keepRight[error.id] = error; + updateRtree(encodeErrorRtree(error), true); // true = replace + return error; + }, - // remove a single error from the cache - removeError: function(error) { - if (!(error instanceof krError) || !error.id) return; + // remove a single error from the cache + removeError: function(error) { + if (!(error instanceof krError) || !error.id) return; - delete _keepRightCache.keepRight[error.id]; - updateRtree(encodeErrorRtree(error), false); // false = remove - }, -}; \ No newline at end of file + delete _keepRightCache.keepRight[error.id]; + updateRtree(encodeErrorRtree(error), false); // false = remove + } +}; diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 33373a116..ab0c1aaf2 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -185,6 +185,7 @@ export function svgKeepRight(projection, context, dispatch) { editOn(); update(); var options = { + st: '', // NOTE: passing in 'ignore' or 'ignore_t' seems to have no effect ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413] }; diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 84ed15856..6194f4b5e 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -289,7 +289,8 @@ export function uiKeepRightEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - d.state = 'ignore_t'; + + d.state = d.state === 'ignore_t' ? '' : 'ignore_t'; keepRight.postKeepRightUpdate(d, function(err, error) { dispatch.call('change', error); }); @@ -306,7 +307,7 @@ export function uiKeepRightEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - d.state = 'ignore'; + d.state = d.state === 'ignore' ? '' : 'ignore'; keepRight.postKeepRightUpdate(d, function(err, error) { dispatch.call('change', error); }); From f4e71812ca5fe1e4c32fbc84564ff9a4be7594a6 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Mon, 19 Nov 2018 11:51:00 -0800 Subject: [PATCH 034/114] added: help documentation --- data/core.yaml | 7 +++++++ dist/locales/en.json | 8 ++++++++ modules/ui/help.js | 9 +++++++++ 3 files changed, 24 insertions(+) diff --git a/data/core.yaml b/data/core.yaml index 63f48dfe9..31d7d42c4 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -1412,6 +1412,13 @@ en: using: "To use a GPS trace for mapping, drag and drop the data file onto the map editor. If it's recognized, it will be drawn on the map as a bright purple line. Click the {data} **Map data** panel on the side of the map to enable, disable, or zoom to your GPS data." tracing: "The GPS track isn't sent to OpenStreetMap - the best way to use it is to draw on the map, using it as a guide for the new features that you add." upload: "You can also [upload your GPS data to OpenStreetMap](https://www.openstreetmap.org/trace/create) for other users to use." + qa: + title: Quality Assurance + intro: "*Quality Assurance* (Q/A) 3rd party tools help lead to better quality of OSM data. They list automatically deteted bugs, conflics, and issues with the data, which mappers can then go and fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer." + tools_h: "Tools" + tools: "The following tools are currently supported: [KeepRight](https://www.keepright.at/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/), [ImproveOSM](https://improveosm.org/en/), and more Q/A tools in the future." + issues_h: "Handling Issues" + issues: "Handling Q/A issues is similar to handling notes. Clicking an existing Q/A issue populates the sidebar with details on the issue type and related features. Each tool has its own capabilities, but generally you can comment and/or close an issue. Expect iD to support a 'fix me' button to automatically fix simple issues in the future." field: restrictions: title: Turn Restrictions Help diff --git a/dist/locales/en.json b/dist/locales/en.json index bf82c098e..340b07bb4 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1728,6 +1728,14 @@ "tracing": "The GPS track isn't sent to OpenStreetMap - the best way to use it is to draw on the map, using it as a guide for the new features that you add.", "upload": "You can also [upload your GPS data to OpenStreetMap](https://www.openstreetmap.org/trace/create) for other users to use." }, + "qa": { + "title": "Quality Assurance", + "intro": "*Quality Assurance* (Q/A) 3rd party tools help lead to better quality of OSM data. They list automatically deteted bugs, conflics, and issues with the data, which mappers can then go and fix. To view existing Q/A issues, click the {data} **Map data** panel to enable a specific Q/A layer.", + "tools_h": "Tools", + "tools": "The following tools are currently supported: [KeepRight](https://www.keepright.at/). Expect iD to support [Osmose](https://osmose.openstreetmap.fr/), [ImproveOSM](https://improveosm.org/en/), and more Q/A tools in the future.", + "issues_h": "Handling Issues", + "issues": "Handling Q/A issues is similar to handling notes. Clicking an existing Q/A issue populates the sidebar with details on the issue type and related features. Each tool has its own capabilities, but generally you can comment and/or close an issue. Expect iD to support a 'fix me' button to automatically fix simple issues in the future." + }, "field": { "restrictions": { "title": "Turn Restrictions Help", diff --git a/modules/ui/help.js b/modules/ui/help.js index 195c34819..0f930058b 100644 --- a/modules/ui/help.js +++ b/modules/ui/help.js @@ -180,6 +180,13 @@ export function uiHelp(context) { 'using', 'tracing', 'upload' + ]], + ['qa', [ + 'intro', + 'tools_h', + 'tools', + 'issues_h', + 'issues' ]] ]; @@ -227,6 +234,8 @@ export function uiHelp(context) { 'help.imagery.offsets_h': 3, 'help.streetlevel.using_h': 3, 'help.gps.using_h': 3, + 'help.qa.tools_h': 3, + 'help.qa.issues_h': 3 }; var replacements = { From c501c44f6087c2d734cda8bb025ef62d0d106589 Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Mon, 19 Nov 2018 11:59:46 -0800 Subject: [PATCH 035/114] updated: KeepRight description text --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 31d7d42c4..e02da4df8 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -928,7 +928,7 @@ en: keepRight: tooltip: automatically detected errors from keepright.at description: Keep Right - title: Edit Error + title: Edit KeepRight Error detail_title: Error detail_description: Description comment_header: Comment diff --git a/dist/locales/en.json b/dist/locales/en.json index 340b07bb4..fac72b207 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1135,7 +1135,7 @@ "keepRight": { "tooltip": "automatically detected errors from keepright.at", "description": "Keep Right", - "title": "Edit Error", + "title": "Edit KeepRight Error", "detail_title": "Error", "detail_description": "Description", "comment_header": "Comment", From 2deadd5d6c5c6c210c6fff18de4ee9fe446b201b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 19 Dec 2018 17:12:06 -0500 Subject: [PATCH 036/114] Fix merge issues --- data/core.yaml | 6 +- dist/locales/en.json | 63 +++++------ modules/modes/select_data.js | 1 - modules/modes/select_error.js | 35 +++---- modules/renderer/map.js | 2 +- modules/svg/geolocate.js | 4 +- modules/ui/keepRight_comment.js | 6 +- modules/ui/keepRight_details.js | 1 - modules/ui/map_data.js | 121 ++++++++++------------ modules/ui/sidebar.js | 6 +- modules/util/keepRight/keepRight_error.js | 44 -------- test/spec/svg/layers.js | 13 --- 12 files changed, 110 insertions(+), 192 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index e02da4df8..8c7abe536 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -480,15 +480,15 @@ en: notes: tooltip: Note data from OpenStreetMap title: OpenStreetMap notes + keepRight: + tooltip: Quality Assurance data from keepright.at + title: KeepRight Issues custom: tooltip: "Drag and drop a data file onto the page, or click the button to setup" title: Custom Map Data zoom: Zoom to data fill_area: Fill Areas map_features: Map Features - QA: - title: Quality Assurance - keepRight: KeepRight autohidden: "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them." osmhidden: "These features have been automatically hidden because the OpenStreetMap layer is hidden." feature: diff --git a/dist/locales/en.json b/dist/locales/en.json index fac72b207..818afff40 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -583,6 +583,10 @@ "tooltip": "Note data from OpenStreetMap", "title": "OpenStreetMap notes" }, + "keepRight": { + "tooltip": "Quality Assurance data from keepright.at", + "title": "KeepRight Issues" + }, "custom": { "tooltip": "Drag and drop a data file onto the page, or click the button to setup", "title": "Custom Map Data", @@ -591,10 +595,6 @@ }, "fill_area": "Fill Areas", "map_features": "Map Features", - "QA": { - "title": "Quality Assurance", - "keepRight": "KeepRight" - }, "autohidden": "These features have been automatically hidden because too many would be shown on the screen. You can zoom in to edit them.", "osmhidden": "These features have been automatically hidden because the OpenStreetMap layer is hidden." }, @@ -1130,7 +1130,27 @@ }, "_360": { "description": "language unknown", - "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}", + "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" + }, + "_390": { + "description": "missing tracktype", + "tooltip": "This track doesn't have a tracktype" + } + } + } + }, + "gpx": { + "local_layer": "Add a GPX", + "drag_drop": "Drag and drop a .gpx, .geojson or .kml file on the page, or click the button to the right to browse", + "zoom": "Zoom to layer", + "browse": "Browse for a file" + }, + "mvt": { + "local_layer": "Add a MVT", + "drag_drop": "Drag and drop a .mvt or .pbf file on the page, or click the button to the right to browse", + "zoom": "Zoom to layer", + "browse": "Browse for a file" + }, "QA": { "keepRight": { "tooltip": "automatically detected errors from keepright.at", @@ -8386,37 +8406,6 @@ "name": "basemap.at Orthofoto" }, "eufar-balaton": { -<<<<<<< HEAD - "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": { -======= ->>>>>>> added: notes and todos for keepRight "attribution": { "text": "EUFAR Balaton ortofotó 2010" }, @@ -9538,4 +9527,4 @@ } } } -} +} \ No newline at end of file diff --git a/modules/modes/select_data.js b/modules/modes/select_data.js index de9b6513e..ad257b8e6 100644 --- a/modules/modes/select_data.js +++ b/modules/modes/select_data.js @@ -1,4 +1,3 @@ - import { geoBounds as d3_geoBounds } from 'd3-geo'; import { diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js index 8af12b722..b66be72d6 100644 --- a/modules/modes/select_error.js +++ b/modules/modes/select_error.js @@ -3,8 +3,6 @@ import { select as d3_select } from 'd3-selection'; -import { d3keybinding as d3_keybinding } from '../lib/d3.keybinding.js'; - import { behaviorBreathe, behaviorHover, @@ -13,8 +11,9 @@ import { } from '../behavior'; import { services } from '../services'; -import { modeBrowse } from './browse'; +import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { uiKeepRightEditor } from '../ui'; +import { utilKeybinding } from '../util'; export function modeSelectError(context, selectedErrorID) { @@ -24,7 +23,7 @@ export function modeSelectError(context, selectedErrorID) { }; var keepRight = services.keepRight; - var keybinding = d3_keybinding('select-error'); + var keybinding = utilKeybinding('select-error'); var keepRightEditor = uiKeepRightEditor(context) .on('change', function() { context.map().pan([0,0]); // trigger a redraw @@ -39,6 +38,8 @@ export function modeSelectError(context, selectedErrorID) { behaviorHover(context), behaviorSelect(context), behaviorLasso(context), + modeDragNode(context).behavior, + modeDragNote(context).behavior ]; @@ -51,6 +52,7 @@ export function modeSelectError(context, selectedErrorID) { return error; } + mode.enter = function() { // class the error as selected, or return to browse mode if the error is gone @@ -76,48 +78,45 @@ export function modeSelectError(context, selectedErrorID) { } function esc() { + if (d3_select('.combobox').size()) return; context.enter(modeBrowse(context)); } var error = checkSelectedID(); if (!error) return; - behaviors.forEach(function(behavior) { - context.install(behavior); - }); - - keybinding - .on('⎋', esc, true); + behaviors.forEach(context.install); + keybinding.on('⎋', esc, true); d3_select(document) .call(keybinding); selectError(); - context.ui().sidebar - .show(keepRightEditor.error(error)); + var sidebar = context.ui().sidebar; + sidebar.show(keepRightEditor.error(error)); context.map() - .on('drawn.select', selectError); + .on('drawn.select-error', selectError); }; mode.exit = function() { - behaviors.forEach(function(behavior) { - context.uninstall(behavior); - }); + behaviors.forEach(context.uninstall); - keybinding.off(); + d3_select(document) + .call(keybinding.unbind); context.surface() .selectAll('.kr_error.selected') .classed('selected hover', false); context.map() - .on('drawn.select', null); + .on('drawn.select-error', null); context.ui().sidebar .hide(); + context.selectedErrorID(null); }; diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 170d4a90e..ed6895871 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -355,7 +355,7 @@ export function rendererMap(context) { var mode = context.mode(); if (mode && mode.id !== 'save' && mode.id !== 'select-note' && - mode.id !== 'select-data' && && mode.id !== 'select-error') { + mode.id !== 'select-data' && mode.id !== 'select-error') { context.enter(modeBrowse(context)); } diff --git a/modules/svg/geolocate.js b/modules/svg/geolocate.js index f5ca10f97..9b025e586 100644 --- a/modules/svg/geolocate.js +++ b/modules/svg/geolocate.js @@ -2,9 +2,9 @@ import { select as d3_select } from 'd3-selection'; import { svgPointTransform } from './helpers'; import { geoMetersToLat } from '../geo'; -import _throttle from 'lodash-es/throttle'; -export function svgGeolocate(projection, context, dispatch) { + +export function svgGeolocate(projection) { var layer = d3_select(null); var _position; diff --git a/modules/ui/keepRight_comment.js b/modules/ui/keepRight_comment.js index 5893a039a..9a868f487 100644 --- a/modules/ui/keepRight_comment.js +++ b/modules/ui/keepRight_comment.js @@ -1,5 +1,3 @@ -import { select as d3_select } from 'd3-selection'; - import { t } from '../util/locale'; import { svgIcon } from '../svg'; import { services } from '../services'; @@ -28,9 +26,9 @@ export function uiKeepRightComment() { .text(_error.comment); } - keepRightComment.error = function(_) { + keepRightComment.error = function(val) { if (!arguments.length) return _error; - _error = _; + _error = val; return keepRightComment; }; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 2a330b688..0092c535d 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,6 +1,5 @@ import { t } from '../util/locale'; import { parseErrorDescriptions, errorTypes } from '../util'; -import { select as d3_selectAll } from 'd3-selection'; import { clickLink } from '../util/keepRight'; diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index 316369abd..a96b255cf 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -22,8 +22,6 @@ export function uiMapData(context) { var key = t('map_data.key'); var features = context.features().keys(); - var QAs = ['keepRight']; - // var errors = Object.keys(errorTypes.errors); // TODO: add warnings var layers = context.layers(); var fills = ['wireframe', 'partial', 'full']; @@ -36,7 +34,6 @@ export function uiMapData(context) { var _fillList = d3_select(null); var _featureList = d3_select(null); var _QAList = d3_select(null); - // var _KeepRightList = d3_select(null); function showsFeature(d) { @@ -57,7 +54,6 @@ export function uiMapData(context) { function showsQA(d) { - var QAKeys = [d]; var QALayers = layers.all().filter(function(obj) { return QAKeys.indexOf(obj.id) !== -1; }); var data = QALayers.filter(function(obj) { return obj.layer.supported(); }); @@ -70,18 +66,8 @@ export function uiMapData(context) { } return layerEnabled(data[0]); - } - // function clickError(d) { - - // } - - - // function showsError(d) { - - // } - function showsFill(d) { return _fillSelected === d; @@ -242,6 +228,58 @@ export function uiMapData(context) { } + function drawQAItems(selection) { + var qaKeys = ['keepRight']; + var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; }); + + var ul = selection + .selectAll('.layer-list-qa') + .data([0]); + + ul = ul.enter() + .append('ul') + .attr('class', 'layer-list layer-list-qa') + .merge(ul); + + var li = ul.selectAll('.list-item') + .data(qaLayers); + + li.exit() + .remove(); + + var liEnter = li.enter() + .append('li') + .attr('class', function(d) { return 'list-item list-item-' + d.id; }); + + var labelEnter = liEnter + .append('label') + .each(function(d) { + d3_select(this) + .call(tooltip() + .title(t('map_data.layers.' + d.id + '.tooltip')) + .placement('bottom') + ); + }); + + labelEnter + .append('input') + .attr('type', 'checkbox') + .on('change', function(d) { toggleLayer(d.id); }); + + labelEnter + .append('span') + .text(function(d) { return t('map_data.layers.' + d.id + '.title'); }); + + + // Update + li + .merge(liEnter) + .classed('active', function (d) { return d.layer.enabled(); }) + .selectAll('input') + .property('checked', function (d) { return d.layer.enabled(); }); + } + + // Beta feature - sample vector layers to support Detroit Mapping Challenge // https://github.com/osmus/detroit-mapping-challenge function drawVectorItems(selection) { @@ -451,9 +489,6 @@ export function uiMapData(context) { var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); var buttonSection = selection.selectAll('.QA-buttons') .data([0]); - // function drawQAButtons(selection) { - - // var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); // var buttonSection = selection.selectAll('.QA-buttons') // .data([0]); @@ -487,7 +522,7 @@ export function uiMapData(context) { // buttonSection = buttonSection // .merge(buttonEnter); - // } + } function drawListItems(selection, data, type, name, change, active) { @@ -578,30 +613,10 @@ export function uiMapData(context) { } - function renderQAList(selection) { - var container = selection.selectAll('layer-QA') - .data([0]); - - _QAList = container.enter() - .append('ul') - .attr('class', 'layer-list layer-QA') - .merge(container); - } - - // function renderKeepRightList(selection) { - // var container = selection.selectAll('layer-keepRight') - // .data([0]); - - // _KeepRightList = container.enter() - // .append('ul') - // .attr('class', 'layer-list layer-keepRight') - // .merge(container); - // } - - function update() { _dataLayerContainer .call(drawOsmItems) + .call(drawQAItems) .call(drawPhotoItems) .call(drawCustomDataItems) .call(drawVectorItems); // Beta - Detroit mapping challenge @@ -613,12 +628,7 @@ export function uiMapData(context) { .call(drawListItems, features, 'checkbox', 'feature', clickFeature, showsFeature); _QAList - .call(drawListItems, QAs, 'checkbox', 'QA', function(d) { toggleLayer(d); }, showsQA); - - // _KeepRightList - // .call(drawListItems, errors, 'checkbox', 'QA.keepRight.errorTypes.errors', clickError, showsError); - // d3_select('.disclosure-wrap-QA') - // .call(drawQAButtons); + .call(drawListItems, ['keep-right'], 'checkbox', 'QA', function(d) { toggleLayer(d); }, showsQA); } @@ -718,6 +728,7 @@ export function uiMapData(context) { .append('div') .attr('class', 'pane-content'); + // data layers content .append('div') @@ -745,31 +756,11 @@ export function uiMapData(context) { .content(renderFeatureList) ); - // Q/A tools - content - .append('div') - .attr('class', 'map-data-QA') - .call(uiDisclosure(context, 'QA', false) - .title(t('map_data.QA.title')) - .content(renderQAList) - ); - - // // adding keepRight sublayers - // QA_list - // .append('div') - // .attr('class', 'keepRight-errors') - // .call(uiDisclosure(context, 'keepRight', false) - // .title(t('map_data.QA.keepRight')) - // .content(renderKeepRightList) - // ); - // add listeners context.features() .on('change.map_data-update', update); - // context.errors() - // .on('change.map_data-update', update); // TODO: add errors list to context? update(); setFill(_fillSelected); diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index c0b6a9a98..576ea072c 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -125,14 +125,14 @@ export function uiSidebar(context) { selection.selectAll('.sidebar-component') .classed('inspector-hover', true); - } else if (what instanceof krError) { + } else if (datum instanceof krError) { _was_krError = true; var kr_errors = d3_selectAll('.kr_error'); kr_errors - .classed('hover', function(d) { return d === what; }); + .classed('hover', function(d) { return d === datum; }); sidebar - .show(keepRightEditor.error(what)); + .show(keepRightEditor.error(datum)); selection.selectAll('.sidebar-component') .classed('inspector-hover', true); diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js index f74a49ae1..6e50728a4 100644 --- a/modules/util/keepRight/keepRight_error.js +++ b/modules/util/keepRight/keepRight_error.js @@ -6,50 +6,6 @@ import { krError } from '../../osm'; import { errorTypes } from './errorSchema.json'; import { parseError } from './parse_error'; -// TODO: for reference; remove -var keepRightSchema = { - 'schema': '', - 'id': 0, - 'error_type': 0, - 'error_name': 0, - 'object_type': [ - 'node', - 'way', - 'relation' - ], - 'object_id': 0, - 'state': [ - 'new', - 'reopened', - 'ignore_temporarily', - 'ignore' - ], - 'first_occurrence': new Date(), - 'last_checked': new Date(), - 'object_timestamp': new Date(), - 'user_name': '', - 'lat': 0, - 'lon': 0, - 'comment': '', - 'comment_timestamp': new Date(), - 'msgid': '', - 'txt1': '', - 'txt2': '', - 'txt3': '', - 'txt4': '', - 'txt5': '' - }; -// TODO: for reference; remove -var keepRightSchemaFromWeb = { - 'error_type': '192', - 'object_type': 'way', - 'object_id': '339948768', - 'comment': null, - 'error_id': '92854860', - 'schema': '58', - 'description': 'This waterway intersects the highway #450282565', - 'title': 'intersections without junctions, highway-waterway' -}; export function parseErrorDescriptions(entity) { var parsedDetails = {}; diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js index 16aeed0ed..f2277229c 100644 --- a/test/spec/svg/layers.js +++ b/test/spec/svg/layers.js @@ -27,7 +27,6 @@ describe('iD.svgLayers', function () { container.call(iD.svgLayers(projection, context)); var nodes = container.selectAll('svg .data-layer').nodes(); expect(nodes.length).to.eql(10); -<<<<<<< HEAD expect(d3.select(nodes[0]).classed('osm')).to.be.true; expect(d3.select(nodes[1]).classed('notes')).to.be.true; expect(d3.select(nodes[2]).classed('data')).to.be.true; @@ -38,18 +37,6 @@ describe('iD.svgLayers', function () { expect(d3.select(nodes[7]).classed('debug')).to.be.true; expect(d3.select(nodes[8]).classed('geolocate')).to.be.true; expect(d3.select(nodes[9]).classed('touch')).to.be.true; -======= - expect(d3.select(nodes[0]).classed('data-layer-osm')).to.be.true; - expect(d3.select(nodes[1]).classed('data-layer-notes')).to.be.true; - expect(d3.select(nodes[2]).classed('data-layer-keepRight')).to.be.true; - expect(d3.select(nodes[3]).classed('data-layer-gpx')).to.be.true; - expect(d3.select(nodes[4]).classed('data-layer-mvt')).to.be.true; - expect(d3.select(nodes[5]).classed('data-layer-streetside')).to.be.true; - expect(d3.select(nodes[6]).classed('data-layer-mapillary-images')).to.be.true; - expect(d3.select(nodes[7]).classed('data-layer-mapillary-signs')).to.be.true; - expect(d3.select(nodes[8]).classed('data-layer-openstreetcam-images')).to.be.true; - expect(d3.select(nodes[9]).classed('data-layer-debug')).to.be.true; ->>>>>>> displaying keep right (currently as notes) }); }); From ca2d4e2c34475e3c5d78ff419317425eeaba9e69 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 19 Dec 2018 17:31:55 -0500 Subject: [PATCH 037/114] Fix layer test --- test/spec/svg/layers.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/spec/svg/layers.js b/test/spec/svg/layers.js index f2277229c..5a40332d6 100644 --- a/test/spec/svg/layers.js +++ b/test/spec/svg/layers.js @@ -26,17 +26,18 @@ describe('iD.svgLayers', function () { it('creates default data layers', function () { container.call(iD.svgLayers(projection, context)); var nodes = container.selectAll('svg .data-layer').nodes(); - expect(nodes.length).to.eql(10); + expect(nodes.length).to.eql(11); expect(d3.select(nodes[0]).classed('osm')).to.be.true; expect(d3.select(nodes[1]).classed('notes')).to.be.true; expect(d3.select(nodes[2]).classed('data')).to.be.true; - expect(d3.select(nodes[3]).classed('streetside')).to.be.true; - expect(d3.select(nodes[4]).classed('mapillary-images')).to.be.true; - expect(d3.select(nodes[5]).classed('mapillary-signs')).to.be.true; - expect(d3.select(nodes[6]).classed('openstreetcam-images')).to.be.true; - expect(d3.select(nodes[7]).classed('debug')).to.be.true; - expect(d3.select(nodes[8]).classed('geolocate')).to.be.true; - expect(d3.select(nodes[9]).classed('touch')).to.be.true; + expect(d3.select(nodes[3]).classed('keepRight')).to.be.true; + expect(d3.select(nodes[4]).classed('streetside')).to.be.true; + expect(d3.select(nodes[5]).classed('mapillary-images')).to.be.true; + expect(d3.select(nodes[6]).classed('mapillary-signs')).to.be.true; + expect(d3.select(nodes[7]).classed('openstreetcam-images')).to.be.true; + expect(d3.select(nodes[8]).classed('debug')).to.be.true; + expect(d3.select(nodes[9]).classed('geolocate')).to.be.true; + expect(d3.select(nodes[10]).classed('touch')).to.be.true; }); }); From 2dab19e9ae3098d6a8ba6be76dfb93f0ace69c8e Mon Sep 17 00:00:00 2001 From: Max Grossman Date: Thu, 20 Dec 2018 08:52:16 -0500 Subject: [PATCH 038/114] default to value() if neither d and choice are null in the accept function ref #5618 --- modules/ui/combobox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/combobox.js b/modules/ui/combobox.js index f8f6f8779..66f79af10 100644 --- a/modules/ui/combobox.js +++ b/modules/ui/combobox.js @@ -341,7 +341,7 @@ export function uiCombobox(context, klass) { // Dispatches an 'accept' event if an option has been chosen. // Then hides the combobox. function accept(d) { - d = d || _choice; + d = d || _choice || value(); if (d) { utilGetSetValue(input, d.value); utilTriggerEvent(input, 'change'); From 7f669d292f12d98be1fe029e31bf2a0597440b3d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 20 Dec 2018 14:40:14 -0500 Subject: [PATCH 039/114] Simplify KeepRight tile fetching code --- modules/services/keepRight.js | 384 ++++++++++++++------------------ modules/svg/keepRight.js | 14 +- modules/ui/keepRight_comment.js | 4 - 3 files changed, 166 insertions(+), 236 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 34a455ba6..1f33efffa 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -6,6 +6,7 @@ import _isEmpty from 'lodash-es/isEmpty'; import rbush from 'rbush'; import { dispatch as d3_dispatch } from 'd3-dispatch'; +import { json as d3_json } from 'd3-request'; import { request as d3_request } from 'd3-request'; import { geoExtent } from '../geo'; @@ -15,281 +16,222 @@ import { krError } from '../osm'; import { utilRebind, utilTiler, utilQsString } from '../util'; var tiler = utilTiler(); -var dispatch = d3_dispatch( - 'authLoading', - 'authDone', - 'change', - 'loading', - 'loaded', - 'loadedKeepRight' -); +var dispatch = d3_dispatch('loaded'); -var _keepRightCache = { - loaded: {}, - inflight: {}, - inflightPost: {}, - keepRight: {}, - rtree: rbush() -}; -var _off; -var _keepRightZoom = 14; - -var apiBase = 'https://www.keepright.at/'; +var _krCache; +var _krZoom = 14; +var apibase = 'https://www.keepright.at/'; +var defaultRuleset = [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413]; function abortRequest(i) { - if (i) { - i.abort(); - } + if (i) { + i.abort(); + } } function abortUnwantedRequests(cache, tiles) { - _forEach(cache.inflight, function(v, k) { - var wanted = _find(tiles, function(tile) { - return k === tile.id; - }); - if (!wanted) { - abortRequest(v); - delete cache.inflight[k]; - } - }); + _forEach(cache.inflight, function(v, k) { + var wanted = _find(tiles, function(tile) { + return k === tile.id; + }); + if (!wanted) { + abortRequest(v); + delete cache.inflight[k]; + } + }); } -function encodeErrorRtree(error) { - return { - minX: error.loc[0], - minY: error.loc[1], - maxX: error.loc[0], - maxY: error.loc[1], - data: error - }; + +function encodeErrorRtree(d) { + return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: d }; } + // replace or remove error from rtree function updateRtree(item, replace) { - _keepRightCache.rtree.remove(item, function isEql(a, b) { - return a.data.id === b.data.id; - }); + _krCache.rtree.remove(item, function isEql(a, b) { + return a.data.id === b.data.id; + }); - if (replace) { - _keepRightCache.rtree.insert(item); - } + if (replace) { + _krCache.rtree.insert(item); + } } + export default { - init: function() { - if (!_keepRightCache) { - this.reset(); - } + init: function() { + if (!_krCache) { + this.reset(); + } - this.event = utilRebind(this, dispatch, 'on'); - }, + this.event = utilRebind(this, dispatch, 'on'); + }, - reset: function() { - _forEach(_keepRightCache.inflight, abortRequest); + reset: function() { + if (_krCache) { + _forEach(_krCache.inflight, abortRequest); + } + _krCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush() }; + }, - _keepRightCache = { - loaded: {}, - inflight: {}, - keepRight: {}, - rtree: rbush() - }; - }, - - loadKeepRightErrors: function(context, projection, options, callback) { - options = _extend({ format: 'geojson' }, options); // set format to geojson - if (_off) return; - - var cache = _keepRightCache; + // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php + loadErrors: function(context, projection) { + var options = { + format: 'geojson' + }; + var rules = defaultRuleset.join(); var that = this; - // NOTE: the KeepRight API doesn't seem to load - var path = - apiBase + - 'export.php?' + - 'format=' + - options.format + - '&st=' + - options.st + - '&ch=' + - options.ch.join() + - '&'; + // determine the needed tiles to cover the view + var tiles = tiler + .zoomExtent([_krZoom, _krZoom]) + .getTiles(projection); - // determine the needed tiles to cover the view - var tiles = tiler - .zoomExtent([_keepRightZoom, _keepRightZoom]) - .getTiles(projection); + // abort inflight requests that are no longer needed + abortUnwantedRequests(_krCache, tiles); - // abort inflight requests that are no longer needed - var hadRequests = !_isEmpty(cache.inflight); - abortUnwantedRequests(cache, tiles); - if (hadRequests && _isEmpty(cache.inflight)) { - dispatch.call('loaded'); // stop the spinner - } + // issue new requests.. + tiles.forEach(function(tile) { + if (_krCache.loaded[tile.id] || _krCache.inflight[tile.id]) return; - // issue new requests.. - tiles.forEach(function(tile) { - if (cache.loaded[tile.id] || cache.inflight[tile.id]) return; - if (_isEmpty(cache.inflight)) { - dispatch.call('loading'); // start the spinner - } + var rect = tile.extent.rectangle(); + var params = _extend({}, options, { left: rect[0], bottom: rect[3], right: rect[2], top: rect[1] }); + var url = apibase + 'export.php?' + utilQsString(params) + '&ch=' + rules; - var rect = tile.extent.rectangle(); - var nextPath = - path + - utilQsString({ - left: rect[0], - bottom: [3], - right: rect[2], - top: rect[1] - }); + _krCache.inflight[tile.id] = d3_json(url, + function(err, data) { + delete _krCache.inflight[tile.id]; - var options = {}; // TODO: implement + if (err) return; + _krCache.loaded[tile.id] = true; - cache.inflight[tile.id] = that.loadFromAPI( - nextPath, - function(err, data) { - if (err || !data.features || !data.features.length) return; + if (!data.features || !data.features.length) return; - cache.loaded[tile.id] = true; - delete cache.inflight[tile.id]; + var features = data.features + .map(function(feature) { + var loc = feature.geometry.coordinates; + var props = feature.properties; - if (callback) { - callback(err, _extend({ data: data }, tile)); - } - if (_isEmpty(cache.inflight)) { - dispatch.call('loaded'); // stop the spinner - } - }, - options - ); - }); - }, + // TODO: finish implementing overlapping error offset + // // if errors are coincident, move them apart slightly + // var coincident = false; + // var epsilon = 0.00001; + // do { + // if (coincident) { + // loc = geoVecAdd(loc, [epsilon, epsilon]); + // } + // var bbox = geoExtent(loc).bbox(); + // coincident = cache.rtree.search(bbox).length; + // } while (coincident); - loadFromAPI: function(path, callback, options) { - var cache = _keepRightCache; + var d = new krError({ + loc: loc, + id: props.error_id, + comment: props.comment || null, + description: props.description || '', + error_id: props.error_id, + error_type: props.error_type, + object_id: props.object_id, + object_type: props.object_type, + schema: props.schema, + title: props.title + }); - return d3_request(path) - .mimeType('application/json') // TODO: only have this as a response if the input format is json - .header('Content-type', 'application/x-www-form-urlencoded') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .get(function(err, data) { - var features = data.features - .map(function(feature) { - var loc = feature.geometry.coordinates; - var props = feature.properties; + _krCache.keepRight[d.id] = d; + return encodeErrorRtree(d); + }) + .filter(Boolean); - // TODO: finish implementing overlapping error offset - // // if errors are coincident, move them apart slightly - // var coincident = false; - // var epsilon = 0.00001; - // do { - // if (coincident) { - // loc = geoVecAdd(loc, [epsilon, epsilon]); - // } - // var bbox = geoExtent(loc).bbox(); - // coincident = cache.rtree.search(bbox).length; - // } while (coincident); + _krCache.rtree.load(features); + dispatch.call('loaded'); + } + ); + }); + }, - var d = new krError({ - loc: loc, - id: props.error_id, - comment: props.comment || null, - description: props.description || '', - error_id: props.error_id, - error_type: props.error_type, - object_id: props.object_id, - object_type: props.object_type, - schema: props.schema, - title: props.title - }); + // loadTile: function(url, callback) { + // var cache = _krCache; - cache.keepRight[d.id] = d; + // return d3_request(url) + // .mimeType('application/json') + // .header('Content-type', 'application/x-www-form-urlencoded') + // .response(function(xhr) { + // return JSON.parse(xhr.responseText); + // }) + // .get(function(err, data) { + // callback(err, data); + // }); + // }, - return { - minX: loc[0], - minY: loc[1], - maxX: loc[0], - maxY: loc[1], - data: d - }; - }) - .filter(Boolean); - cache.rtree.load(features); - dispatch.call('loadedKeepRight'); - - callback(err, data); - }); - }, - - postKeepRightUpdate: function(update, callback) { - if (!services.osm.authenticated()) { - return callback({ message: 'Not Authenticated', status: -3 }, update); - } - if (_keepRightCache.inflightPost[update.id]) { - return callback( + postKeepRightUpdate: function(update, callback) { + if (!services.osm.authenticated()) { + return callback({ message: 'Not Authenticated', status: -3 }, update); + } + if (_krCache.inflightPost[update.id]) { + return callback( { message: 'Error update already inflight', status: -2 }, update); - } + } - var path = apiBase + 'comment.php?'; + var path = apibase + 'comment.php?'; if (update.state) { path += '&st=' + update.state; - } - if (update.newComment) { + } + if (update.newComment) { path += '&' + utilQsString({ co: update.newComment }); - } + } path += '&schema=' + update.schema + '&id=' + update.error_id; - _keepRightCache.inflightPost[update.id] = d3_request(path) - .mimeType('application/json') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .post(function(err, data) { - delete _keepRightCache.inflightPost[update.id]; + _krCache.inflightPost[update.id] = d3_request(path) + .mimeType('application/json') + .response(function(xhr) { + return JSON.parse(xhr.responseText); + }) + .post(function(err, data) { + delete _krCache.inflightPost[update.id]; if (err) { return callback(err); } console.log('data ', data); - }); + }); - // NOTE: This throws a CORS error, but it seems successful? - }, + // NOTE: This throws a CORS error, but it seems successful? + }, - // get all cached errors covering the viewport - keepRight: function(projection) { - var viewport = projection.clipExtent(); - var min = [viewport[0][0], viewport[1][1]]; - var max = [viewport[1][0], viewport[0][1]]; - var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - return _keepRightCache.rtree.search(bbox).map(function(d) { - return d.data; - }); - }, + // get all cached errors covering the viewport + getErrors: function(projection) { + var viewport = projection.clipExtent(); + var min = [viewport[0][0], viewport[1][1]]; + var max = [viewport[1][0], viewport[0][1]]; + var bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox(); - // get a single error from the cache - getError: function(id) { - return _keepRightCache.keepRight[id]; - }, + return _krCache.rtree.search(bbox).map(function(d) { + return d.data; + }); + }, - // replace a single error in the cache - replaceError: function(error) { - if (!(error instanceof krError) || !error.id) return; + // get a single error from the cache + getError: function(id) { + return _krCache.keepRight[id]; + }, - _keepRightCache.keepRight[error.id] = error; - updateRtree(encodeErrorRtree(error), true); // true = replace - return error; - }, + // replace a single error in the cache + replaceError: function(error) { + if (!(error instanceof krError) || !error.id) return; - // remove a single error from the cache - removeError: function(error) { - if (!(error instanceof krError) || !error.id) return; + _krCache.keepRight[error.id] = error; + updateRtree(encodeErrorRtree(error), true); // true = replace + return error; + }, - delete _keepRightCache.keepRight[error.id]; - updateRtree(encodeErrorRtree(error), false); // false = remove - } + // remove a single error from the cache + removeError: function(error) { + if (!(error instanceof krError) || !error.id) return; + + delete _krCache.keepRight[error.id]; + updateRtree(encodeErrorRtree(error), false); // false = remove + } }; diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index ab0c1aaf2..60fd0bb7c 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -31,7 +31,7 @@ export function svgKeepRight(projection, context, dispatch) { function getService() { if (services.keepRight && !_keepRight) { _keepRight = services.keepRight; - _keepRight.event.on('loadedKeepRight', throttledRedraw); + _keepRight.event.on('loaded', throttledRedraw); } else if (!services.keepRight && _keepRight) { _keepRight = null; } @@ -110,7 +110,7 @@ export function svgKeepRight(projection, context, dispatch) { function update() { var service = getService(); var selectedID = context.selectedNoteID(); // TODO: update with selectedErrorID - var data = (service ? service.keepRight(projection) : []); + var data = (service ? service.getErrors(projection) : []); var visibleData = data; // getVisible(data); // TODO: only show sub-layers that are toggled on var transform = svgPointTransform(projection); var kr_errors = layer.selectAll('.kr_error') @@ -177,19 +177,11 @@ export function svgKeepRight(projection, context, dispatch) { .style('display', enabled ? 'block' : 'none') .merge(layer); - function exampleCallback(value1, value2, value3) { // TODO: rename, possibly remove function - } - if (enabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); update(); - var options = { - st: '', // NOTE: passing in 'ignore' or 'ignore_t' seems to have no effect - ch: [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413] - }; - - service.loadKeepRightErrors(context, projection, options, exampleCallback); + service.loadErrors(context, projection); } else { editOff(); } diff --git a/modules/ui/keepRight_comment.js b/modules/ui/keepRight_comment.js index 9a868f487..516847600 100644 --- a/modules/ui/keepRight_comment.js +++ b/modules/ui/keepRight_comment.js @@ -1,13 +1,9 @@ import { t } from '../util/locale'; -import { svgIcon } from '../svg'; -import { services } from '../services'; -import { utilDetect } from '../util/detect'; export function uiKeepRightComment() { var _error; - function keepRightComment(selection) { if (!_error.comment) return; var comment = selection.selectAll('.comments-container') From 177537f0cebb4d23368691187476b8e9d426a81c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 20 Dec 2018 15:23:53 -0500 Subject: [PATCH 040/114] Move coincident markers away from related geometry and from each other --- modules/services/keepRight.js | 84 ++++++++++++++--------------------- 1 file changed, 33 insertions(+), 51 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 1f33efffa..153bb1778 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -1,7 +1,6 @@ import _extend from 'lodash-es/extend'; import _find from 'lodash-es/find'; import _forEach from 'lodash-es/forEach'; -import _isEmpty from 'lodash-es/isEmpty'; import rbush from 'rbush'; @@ -9,7 +8,7 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; import { json as d3_json } from 'd3-request'; import { request as d3_request } from 'd3-request'; -import { geoExtent } from '../geo'; +import { geoExtent, geoVecAdd } from '../geo'; import { services } from './index'; import { krError } from '../osm'; @@ -109,68 +108,51 @@ export default { if (!data.features || !data.features.length) return; - var features = data.features - .map(function(feature) { - var loc = feature.geometry.coordinates; - var props = feature.properties; + data.features.forEach(function(feature) { + var loc = feature.geometry.coordinates; + var props = feature.properties; - // TODO: finish implementing overlapping error offset - // // if errors are coincident, move them apart slightly - // var coincident = false; - // var epsilon = 0.00001; - // do { - // if (coincident) { - // loc = geoVecAdd(loc, [epsilon, epsilon]); - // } - // var bbox = geoExtent(loc).bbox(); - // coincident = cache.rtree.search(bbox).length; - // } while (coincident); + // - move markers slightly so it doesn't obscure the geometry, + // - then move markers away from other coincident markers + var coincident = false; + var epsilon = 0.00001; + do { + // first time, move marker up. after that, move marker right. + var delta = coincident ? [epsilon, 0] : [0, epsilon]; + loc = geoVecAdd(loc, delta); + var bbox = geoExtent(loc).bbox(); + coincident = _krCache.rtree.search(bbox).length; + } while (coincident); - var d = new krError({ - loc: loc, - id: props.error_id, - comment: props.comment || null, - description: props.description || '', - error_id: props.error_id, - error_type: props.error_type, - object_id: props.object_id, - object_type: props.object_type, - schema: props.schema, - title: props.title - }); + var d = new krError({ + loc: loc, + id: props.error_id, + comment: props.comment || null, + description: props.description || '', + error_id: props.error_id, + error_type: props.error_type, + object_id: props.object_id, + object_type: props.object_type, + schema: props.schema, + title: props.title + }); - _krCache.keepRight[d.id] = d; - return encodeErrorRtree(d); - }) - .filter(Boolean); + _krCache.keepRight[d.id] = d; + _krCache.rtree.insert(encodeErrorRtree(d)); + }); - _krCache.rtree.load(features); dispatch.call('loaded'); } ); }); }, - // loadTile: function(url, callback) { - // var cache = _krCache; - - // return d3_request(url) - // .mimeType('application/json') - // .header('Content-type', 'application/x-www-form-urlencoded') - // .response(function(xhr) { - // return JSON.parse(xhr.responseText); - // }) - // .get(function(err, data) { - // callback(err, data); - // }); - // }, - postKeepRightUpdate: function(update, callback) { if (!services.osm.authenticated()) { return callback({ message: 'Not Authenticated', status: -3 }, update); } - if (_krCache.inflightPost[update.id]) { + if (_krCache.inflight[update.id]) { return callback( { message: 'Error update already inflight', status: -2 }, update); } @@ -185,13 +167,13 @@ export default { path += '&schema=' + update.schema + '&id=' + update.error_id; - _krCache.inflightPost[update.id] = d3_request(path) + _krCache.inflight[update.id] = d3_request(path) .mimeType('application/json') .response(function(xhr) { return JSON.parse(xhr.responseText); }) .post(function(err, data) { - delete _krCache.inflightPost[update.id]; + delete _krCache.inflight[update.id]; if (err) { return callback(err); } console.log('data ', data); From 6e1d6bcfdac7d38de9cab6d0342ae074715fb92b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 20 Dec 2018 15:38:04 -0500 Subject: [PATCH 041/114] Drop legacy translation keys --- data/core.yaml | 10 ---------- dist/locales/en.json | 12 ------------ 2 files changed, 22 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 8c7abe536..e7ad45c11 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -914,16 +914,6 @@ en: _390: description: 'missing tracktype' tooltip: This track doesn't have a tracktype - gpx: - local_layer: "Add a GPX" - drag_drop: "Drag and drop a .gpx, .geojson or .kml file on the page, or click the button to the right to browse" - zoom: "Zoom to layer" - browse: "Browse for a file" - mvt: - local_layer: "Add a MVT" - drag_drop: "Drag and drop a .mvt or .pbf file on the page, or click the button to the right to browse" - zoom: "Zoom to layer" - browse: "Browse for a file" QA: keepRight: tooltip: automatically detected errors from keepright.at diff --git a/dist/locales/en.json b/dist/locales/en.json index 818afff40..715d0e153 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1139,18 +1139,6 @@ } } }, - "gpx": { - "local_layer": "Add a GPX", - "drag_drop": "Drag and drop a .gpx, .geojson or .kml file on the page, or click the button to the right to browse", - "zoom": "Zoom to layer", - "browse": "Browse for a file" - }, - "mvt": { - "local_layer": "Add a MVT", - "drag_drop": "Drag and drop a .mvt or .pbf file on the page, or click the button to the right to browse", - "zoom": "Zoom to layer", - "browse": "Browse for a file" - }, "QA": { "keepRight": { "tooltip": "automatically detected errors from keepright.at", From 4eb0e4b3a498a3e734c3d5996b8cd92695e581a6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 20 Dec 2018 16:09:18 -0500 Subject: [PATCH 042/114] Remove more duplicate strings --- data/core.yaml | 267 -------------------------- dist/locales/en.json | 352 ---------------------------------- modules/services/keepRight.js | 3 +- 3 files changed, 1 insertion(+), 621 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index e7ad45c11..61343d5dc 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -647,273 +647,6 @@ en: out: Zoom out cannot_zoom: "Cannot zoom out further in current mode." full_screen: Toggle Full Screen - keepRight: - keepRight: Error - - tooltip: Q/A data from keepright.at - title: Edit Error - detail_title: Error - detail_description: Description - inputPlaceholder: Enter a comment to share with other users. - newComment: New Comment - upload_explanation: Your comments will be publicly visible to all keepRight.at users. - upload_explanation_with_user: "Your comments as {user} will be publicly visible to all keepRight.at users." - resolve_comment: Comment and Resolve - ignore_comment: Comment and Ignore - resolve: Resolve - ignore: Ignore - toggle-on: All on - toggle-off: All off - entities: - node: node - way: way - relation: relation - highway: highway - cycleway: cycleway - waterway: waterway - riverbank: riverbank - errorTypes: - errors: - _30: - description: 'non-closed_areas' - tooltip: 'This way is tagged with {var1}={var2} and should be closed-loop' - _40: - description: 'dead-ended one-ways' - tooltip: 'The first node (id {var1}) of this one-way is not connected to any other way' - _41: - description: '' - tooltip: 'The last node (id {var1}) of this one-way is not connected to any other way' - _42: - description: '' - tooltip: 'This node cannot be reached because one-ways only lead away from here' - _43: - description: '' - tooltip: 'You cannot escape from this node because one-ways only lead to here' - _50: - description: 'almost-junctions' - tooltip: 'This node is very close but not connected to way #{var1}' - _70: - description: 'missing tags' - tooltip: 'This {var1} has an empty tag: {var2}' - _71: - description: 'way without tags' - tooltip: 'This way has no tags' - _72: - description: 'node without tags' - tooltip: 'This node is not member of any way and doesn''t have any tags' - _90: - description: 'motorways without ref' - tooltip: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag' - _100: - description: 'places of worship without religion' - tooltip: 'This {var1} is tagged as place of worship and therefore needs a religion tag' - _110: - description: 'point of interest without name' - tooltip: 'This node is tagged as {var1} and therefore needs a name tag' - _120: - description: 'ways without nodes' - tooltip: 'This way has just one single node' - _130: - description: 'floating islands' - tooltip: 'This way is not connected to the rest of the map' - _150: - description: 'railway crossing without tag' - tooltip: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing' - _160: - description: 'wrongly used railway tag' - tooltip: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing' - _170: - description: 'FIXME tagged items' - tooltip: '{var1}' - _180: - description: 'relations without type' - tooltip: 'This relation has no type tag which is mandatory for relations' - _190: - description: 'intersections without junctions' - tooltip: 'Finds way crossings on same layer without common node as a junction' - _191: - description: 'highway-highway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _192: - description: 'highway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}' - _193: - description: 'highway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}' - _194: - description: 'waterway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _195: - description: 'cycleway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _196: - description: 'highway-cycleway' - tooltip: 'This {var1} intersects the {var2} #{var3} but there is no junction node' - _197: - description: 'cycleway-waterway' - tooltip: 'This {var1} intersects the {var2} #{var3}' - _198: - description: 'cycleway-riverbank' - tooltip: 'This {var1} intersects the {var2} #{var3}' - _200: - description: 'overlapping ways' - tooltip: 'Finds overlapping ways on same layer' - _201: - description: 'highway-highway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _202: - description: 'highway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _203: - description: 'highway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _204: - description: 'waterway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _205: - description: 'cycleway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _206: - description: 'highway-cycleway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _207: - description: 'cycleway-waterway' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _208: - description: 'cycleway-riverbank' - tooltip: 'This {var1} overlaps the {var2} #{var3}' - _210: - description: 'loopings' - tooltip: 'These errors contain self intersecting ways' - _211: - description: '' - tooltip: 'This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error' - _212: - description: '' - tooltip: 'This way has only two different nodes and contains one of them more than once' - _220: - description: 'misspelled tags' - tooltip: 'This {var1} is tagged {var2}={var3} where {var4} looks like {var5}' - _221: - description: '' - tooltip: 'The key of this {var1} tag is key {var2}' - _230: - description: 'layer conflicts' - tooltip: '' - _231: - description: 'mixed layers intersection' - tooltip: 'This node is a junction of ways on different layers: {var1}' - _232: - description: 'strange layers' - tooltip: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange' - _270: - description: 'motorways connected directly' - tooltip: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' - _280: - description: 'boundaries' - tooltip: '' - _281: - description: 'missing name' - tooltip: 'This boundary has no name' - _282: - description: 'missing admin level' - tooltip: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries' - _283: - description: 'no closed loop' - tooltip: 'The boundary of {var1} is not closed-loop' - _284: - description: 'splitting boundary' - tooltip: 'The boundary of {var1} splits here' - _285: - description: 'admin_level too high' - tooltip: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations' - _290: - description: 'restrictions' - tooltip: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat' - _291: - description: 'missing type' - tooltip: 'This turn-restriction has no known restriction type' - _292: - description: 'missing from way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' - _293: - description: 'missing to way' - tooltip: 'A turn-restriction needs exactly one {var1} member. This one has {var2}' - _294: - description: 'from or to not a way' - tooltip: 'From- and To-members of turn restrictions need to be ways. {var1}' - _295: - description: 'via is not on the way ends' - tooltip: 'via (node #{var1}) is not the first or the last member of from (way #{var2})' - _296: - description: 'wrong restriction angle' - tooltip: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' - _297: - description: 'wrong direction of to member' - tooltip: 'wrong direction of to way {var1}' - _298: - description: 'already restricted by oneway' - tooltip: 'entry already prohibited by oneway tag on {var1}' - _310: - description: 'roundabouts' - tooltip: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1' - _311: - description: 'not closed loop' - tooltip: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)' - _312: - description: 'wrong direction' - tooltip: 'If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around' - _313: - description: 'faintly connected' - tooltip: 'This roundabout has only {var1} other roads connected. Roundabouts typically have three' - _320: - description: '*_link connections' - tooltip: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link' - _350: - description: 'bridge-tags' - tooltip: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}' - _370: - description: 'doubled places' - tooltip: 'This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand' - _380: - description: 'non-physical use of sport-tag' - tooltip: 'This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway' - _400: - description: 'geometry glitches' - tooltip: '' - _401: - description: 'missing turn restriction' - tooltip: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}' - _402: - description: 'impossible angles' - tooltip: 'this way bends in a very sharp angle here' - _410: - description: 'website' - tooltip: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*' - _411: - description: 'http error' - tooltip: 'The URL ({var1}) cannot be opened (HTTP status code {var2})' - _412: - description: 'domain hijacking' - tooltip: 'Possible domain squatting: {var1}. Suspicious text is: "{var2}"' - _413: - description: 'non-match' - tooltip: 'Content of the URL ({var1}) did not contain these keywords: ({var2})' - warnings: - _20: - description: 'multiple nodes on the same spot' - tooltip: 'There is more than one node in this spot. Offending node IDs: {var1}' - _60: - description: 'depreciated tags' - tooltip: 'This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!' - _300: - description: 'missing maxspeed' - tooltip: 'missing maxspeed tag' - _360: - description: 'language unknown' - tooltip: 'It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}' - _390: - description: 'missing tracktype' - tooltip: This track doesn't have a tracktype QA: keepRight: tooltip: automatically detected errors from keepright.at diff --git a/dist/locales/en.json b/dist/locales/en.json index 715d0e153..ed5847ee5 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -787,358 +787,6 @@ }, "cannot_zoom": "Cannot zoom out further in current mode.", "full_screen": "Toggle Full Screen", - "keepRight": { - "keepRight": "Error -", - "tooltip": "Q/A data from keepright.at", - "title": "Edit Error", - "detail_title": "Error", - "detail_description": "Description", - "inputPlaceholder": "Enter a comment to share with other users.", - "newComment": "New Comment", - "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", - "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all keepRight.at users.", - "resolve_comment": "Comment and Resolve", - "ignore_comment": "Comment and Ignore", - "resolve": "Resolve", - "ignore": "Ignore", - "toggle-on": "All on", - "toggle-off": "All off", - "entities": { - "node": "node", - "way": "way", - "relation": "relation", - "highway": "highway", - "cycleway": "cycleway", - "waterway": "waterway", - "riverbank": "riverbank" - }, - "errorTypes": { - "errors": { - "_30": { - "description": "non-closed_areas", - "tooltip": "This way is tagged with {var1}={var2} and should be closed-loop" - }, - "_40": { - "description": "dead-ended one-ways", - "tooltip": "The first node (id {var1}) of this one-way is not connected to any other way" - }, - "_41": { - "description": "", - "tooltip": "The last node (id {var1}) of this one-way is not connected to any other way" - }, - "_42": { - "description": "", - "tooltip": "This node cannot be reached because one-ways only lead away from here" - }, - "_43": { - "description": "", - "tooltip": "You cannot escape from this node because one-ways only lead to here" - }, - "_50": { - "description": "almost-junctions", - "tooltip": "This node is very close but not connected to way #{var1}" - }, - "_70": { - "description": "missing tags", - "tooltip": "This {var1} has an empty tag: {var2}" - }, - "_71": { - "description": "way without tags", - "tooltip": "This way has no tags" - }, - "_72": { - "description": "node without tags", - "tooltip": "This node is not member of any way and doesn't have any tags" - }, - "_90": { - "description": "motorways without ref", - "tooltip": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" - }, - "_100": { - "description": "places of worship without religion", - "tooltip": "This {var1} is tagged as place of worship and therefore needs a religion tag" - }, - "_110": { - "description": "point of interest without name", - "tooltip": "This node is tagged as {var1} and therefore needs a name tag" - }, - "_120": { - "description": "ways without nodes", - "tooltip": "This way has just one single node" - }, - "_130": { - "description": "floating islands", - "tooltip": "This way is not connected to the rest of the map" - }, - "_150": { - "description": "railway crossing without tag", - "tooltip": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" - }, - "_160": { - "description": "wrongly used railway tag", - "tooltip": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" - }, - "_170": { - "description": "FIXME tagged items", - "tooltip": "{var1}" - }, - "_180": { - "description": "relations without type", - "tooltip": "This relation has no type tag which is mandatory for relations" - }, - "_190": { - "description": "intersections without junctions", - "tooltip": "Finds way crossings on same layer without common node as a junction" - }, - "_191": { - "description": "highway-highway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_192": { - "description": "highway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}" - }, - "_193": { - "description": "highway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}" - }, - "_194": { - "description": "waterway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_195": { - "description": "cycleway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_196": { - "description": "highway-cycleway", - "tooltip": "This {var1} intersects the {var2} #{var3} but there is no junction node" - }, - "_197": { - "description": "cycleway-waterway", - "tooltip": "This {var1} intersects the {var2} #{var3}" - }, - "_198": { - "description": "cycleway-riverbank", - "tooltip": "This {var1} intersects the {var2} #{var3}" - }, - "_200": { - "description": "overlapping ways", - "tooltip": "Finds overlapping ways on same layer" - }, - "_201": { - "description": "highway-highway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_202": { - "description": "highway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_203": { - "description": "highway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_204": { - "description": "waterway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_205": { - "description": "cycleway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_206": { - "description": "highway-cycleway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_207": { - "description": "cycleway-waterway", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_208": { - "description": "cycleway-riverbank", - "tooltip": "This {var1} overlaps the {var2} #{var3}" - }, - "_210": { - "description": "loopings", - "tooltip": "These errors contain self intersecting ways" - }, - "_211": { - "description": "", - "tooltip": "This way contains more than one node at least twice. Nodes are {var1}. This may or may not be an error" - }, - "_212": { - "description": "", - "tooltip": "This way has only two different nodes and contains one of them more than once" - }, - "_220": { - "description": "misspelled tags", - "tooltip": "This {var1} is tagged {var2}={var3} where {var4} looks like {var5}" - }, - "_221": { - "description": "", - "tooltip": "The key of this {var1} tag is key {var2}" - }, - "_230": { - "description": "layer conflicts", - "tooltip": "" - }, - "_231": { - "description": "mixed layers intersection", - "tooltip": "This node is a junction of ways on different layers: {var1}" - }, - "_232": { - "description": "strange layers", - "tooltip": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange" - }, - "_270": { - "description": "motorways connected directly", - "tooltip": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." - }, - "_280": { - "description": "boundaries", - "tooltip": "" - }, - "_281": { - "description": "missing name", - "tooltip": "This boundary has no name" - }, - "_282": { - "description": "missing admin level", - "tooltip": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries" - }, - "_283": { - "description": "no closed loop", - "tooltip": "The boundary of {var1} is not closed-loop" - }, - "_284": { - "description": "splitting boundary", - "tooltip": "The boundary of {var1} splits here" - }, - "_285": { - "description": "admin_level too high", - "tooltip": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations" - }, - "_290": { - "description": "restrictions", - "tooltip": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" - }, - "_291": { - "description": "missing type", - "tooltip": "This turn-restriction has no known restriction type" - }, - "_292": { - "description": "missing from way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" - }, - "_293": { - "description": "missing to way", - "tooltip": "A turn-restriction needs exactly one {var1} member. This one has {var2}" - }, - "_294": { - "description": "from or to not a way", - "tooltip": "From- and To-members of turn restrictions need to be ways. {var1}" - }, - "_295": { - "description": "via is not on the way ends", - "tooltip": "via (node #{var1}) is not the first or the last member of from (way #{var2})" - }, - "_296": { - "description": "wrong restriction angle", - "tooltip": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" - }, - "_297": { - "description": "wrong direction of to member", - "tooltip": "wrong direction of to way {var1}" - }, - "_298": { - "description": "already restricted by oneway", - "tooltip": "entry already prohibited by oneway tag on {var1}" - }, - "_310": { - "description": "roundabouts", - "tooltip": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" - }, - "_311": { - "description": "not closed loop", - "tooltip": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" - }, - "_312": { - "description": "wrong direction", - "tooltip": "If this roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this roundabout is in a country with left-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with right-hand traffic then its orientation goes the wrong way around | If this mini_roundabout is in a country with left-hand traffic then its orientation goes the wrong way around" - }, - "_313": { - "description": "faintly connected", - "tooltip": "This roundabout has only {var1} other roads connected. Roundabouts typically have three" - }, - "_320": { - "description": "*_link connections", - "tooltip": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link" - }, - "_350": { - "description": "bridge-tags", - "tooltip": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}" - }, - "_370": { - "description": "doubled places", - "tooltip": "This node has tags in common with the surrounding way #{var1} and seems to be redundand | This node has tags in common with the surrounding way #{var1} (including the name {var2}) and seems to be redundand" - }, - "_380": { - "description": "non-physical use of sport-tag", - "tooltip": "This way is tagged {var1} but has no physical tag like e.g. leisure, building, amenity or highway" - }, - "_400": { - "description": "geometry glitches", - "tooltip": "" - }, - "_401": { - "description": "missing turn restriction", - "tooltip": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var1} to {var2}" - }, - "_402": { - "description": "impossible angles", - "tooltip": "this way bends in a very sharp angle here" - }, - "_410": { - "description": "website", - "tooltip": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" - }, - "_411": { - "description": "http error", - "tooltip": "The URL ({var1}) cannot be opened (HTTP status code {var2})" - }, - "_412": { - "description": "domain hijacking", - "tooltip": "Possible domain squatting: {var1}. Suspicious text is: \"{var2}\"" - }, - "_413": { - "description": "non-match", - "tooltip": "Content of the URL ({var1}) did not contain these keywords: ({var2})" - } - }, - "warnings": { - "_20": { - "description": "multiple nodes on the same spot", - "tooltip": "There is more than one node in this spot. Offending node IDs: {var1}" - }, - "_60": { - "description": "depreciated tags", - "tooltip": "This {var1} uses deprecated tag {var2}={var3}. Please use {var4} instead!" - }, - "_300": { - "description": "missing maxspeed", - "tooltip": "missing maxspeed tag" - }, - "_360": { - "description": "language unknown", - "tooltip": "It would be nice if this {var1} had an additional tag name:XX={var2} where XX shows the language of its name {var2}" - }, - "_390": { - "description": "missing tracktype", - "tooltip": "This track doesn't have a tracktype" - } - } - } - }, "QA": { "keepRight": { "tooltip": "automatically detected errors from keepright.at", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 153bb1778..6aed5b8fd 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -115,10 +115,9 @@ export default { // - move markers slightly so it doesn't obscure the geometry, // - then move markers away from other coincident markers var coincident = false; - var epsilon = 0.00001; do { // first time, move marker up. after that, move marker right. - var delta = coincident ? [epsilon, 0] : [0, epsilon]; + var delta = coincident ? [0.00001, 0] : [0, 0.000005]; loc = geoVecAdd(loc, delta); var bbox = geoExtent(loc).bbox(); coincident = _krCache.rtree.search(bbox).length; From f7150004c0a5eed9a1aec74377aeef31bb5576f9 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 21 Dec 2018 11:56:55 -0500 Subject: [PATCH 043/114] Refactor things out of util with limited use, move more to service --- data/core.yaml | 26 +-- .../errorSchema.json => data/keepRight.json | 0 dist/locales/en.json | 26 +-- modules/services/keepRight.js | 188 +++++++++++++++++- modules/ui/keepRight_details.js | 14 +- modules/ui/keepRight_header.js | 11 +- modules/ui/map_data.js | 44 ---- modules/util/index.js | 2 - modules/util/keepRight/index.js | 3 - modules/util/keepRight/keepRight_error.js | 80 -------- modules/util/keepRight/parse_error.js | 113 ----------- 11 files changed, 216 insertions(+), 291 deletions(-) rename modules/util/keepRight/errorSchema.json => data/keepRight.json (100%) delete mode 100644 modules/util/keepRight/index.js delete mode 100644 modules/util/keepRight/keepRight_error.js delete mode 100644 modules/util/keepRight/parse_error.js diff --git a/data/core.yaml b/data/core.yaml index 61343d5dc..fce1134da 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -481,7 +481,7 @@ en: tooltip: Note data from OpenStreetMap title: OpenStreetMap notes keepRight: - tooltip: Quality Assurance data from keepright.at + tooltip: Automatically detected map issues from keepright.at title: KeepRight Issues custom: tooltip: "Drag and drop a data file onto the page, or click the button to setup" @@ -649,9 +649,7 @@ en: full_screen: Toggle Full Screen QA: keepRight: - tooltip: automatically detected errors from keepright.at - description: Keep Right - title: Edit KeepRight Error + title: KeepRight Error detail_title: Error detail_description: Description comment_header: Comment @@ -659,22 +657,20 @@ en: updateInputPlaceholder: Update the comment above to share with other users. newComment: New Comment updateComment: Update Comment - upload_explanation: Your comments will be publicly visible to all keepRight.at users. - upload_explanation_with_user: "Your comments as {user} will be publicly visible to all keepRight.at users." + upload_explanation: Your comments will be publicly visible on KeepRight. + upload_explanation_with_user: "Your comments as {user} will be publicly visible on KeepRight." resolve_comment: Comment and Resolve ignore_comment: Comment and Ignore resolve: Resolve ignore: Ignore - toggle-on: All on - toggle-off: All off entities: - node: node - way: way - relation: relation - highway: highway - cycleway: cycleway - waterway: waterway - riverbank: riverbank + node: "Node {id}" + way: "Way {id}" + relation: "Relation {id}" + highway: "Highway {id}" + cycleway: "Cycleway {id}" + waterway: "Waterway {id}" + riverbank: "Riverbank {id}" errorTypes: errors: _30: diff --git a/modules/util/keepRight/errorSchema.json b/data/keepRight.json similarity index 100% rename from modules/util/keepRight/errorSchema.json rename to data/keepRight.json diff --git a/dist/locales/en.json b/dist/locales/en.json index ed5847ee5..fa9999527 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -584,7 +584,7 @@ "title": "OpenStreetMap notes" }, "keepRight": { - "tooltip": "Quality Assurance data from keepright.at", + "tooltip": "Automatically detected map issues from keepright.at", "title": "KeepRight Issues" }, "custom": { @@ -789,9 +789,7 @@ "full_screen": "Toggle Full Screen", "QA": { "keepRight": { - "tooltip": "automatically detected errors from keepright.at", - "description": "Keep Right", - "title": "Edit KeepRight Error", + "title": "KeepRight Error", "detail_title": "Error", "detail_description": "Description", "comment_header": "Comment", @@ -799,22 +797,20 @@ "updateInputPlaceholder": "Update the comment above to share with other users.", "newComment": "New Comment", "updateComment": "Update Comment", - "upload_explanation": "Your comments will be publicly visible to all keepRight.at users.", - "upload_explanation_with_user": "Your comments as {user} will be publicly visible to all keepRight.at users.", + "upload_explanation": "Your comments will be publicly visible on KeepRight.", + "upload_explanation_with_user": "Your comments as {user} will be publicly visible on KeepRight.", "resolve_comment": "Comment and Resolve", "ignore_comment": "Comment and Ignore", "resolve": "Resolve", "ignore": "Ignore", - "toggle-on": "All on", - "toggle-off": "All off", "entities": { - "node": "node", - "way": "way", - "relation": "relation", - "highway": "highway", - "cycleway": "cycleway", - "waterway": "waterway", - "riverbank": "riverbank" + "node": "Node {id}", + "way": "Way {id}", + "relation": "Relation {id}", + "highway": "Highway {id}", + "cycleway": "Cycleway {id}", + "waterway": "Waterway {id}", + "riverbank": "Riverbank {id}" }, "errorTypes": { "errors": { diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 6aed5b8fd..1e74627e7 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -9,11 +9,14 @@ import { json as d3_json } from 'd3-request'; import { request as d3_request } from 'd3-request'; import { geoExtent, geoVecAdd } from '../geo'; -import { services } from './index'; import { krError } from '../osm'; - +import { services } from './index'; +import { t } from '../util/locale'; import { utilRebind, utilTiler, utilQsString } from '../util'; +import { errorTypes } from '../../data/keepRight.json'; + + var tiler = utilTiler(); var dispatch = d3_dispatch('loaded'); @@ -22,6 +25,7 @@ var _krZoom = 14; var apibase = 'https://www.keepright.at/'; var defaultRuleset = [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413]; + function abortRequest(i) { if (i) { i.abort(); @@ -58,6 +62,179 @@ function updateRtree(item, replace) { } +function tokenReplacements(datum) { + if (!(datum instanceof krError)) return; + + var replacements = {}; + var html_re = new RegExp(/<\/[a-z][\s\S]*>/); + var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; + + var errorType; + var errorTemplate; + var errorDescription; + var errorRegex; + var errorMatch; + + // find the matching template from the error schema + errorType = '_' + datum.error_type; + errorTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; + if (!errorTemplate) return; + + // some descriptions are just fixed text + if (!('regex' in errorTemplate)) return; + + // regex pattern should match description with variable details captured as groups + errorDescription = datum.description; + errorRegex = new RegExp(errorTemplate.description); + errorMatch = errorRegex.exec(errorDescription); + if (!errorMatch) { + // TODO: Remove, for regex dev testing + console.log('Unmatched:', errorType, errorDescription, errorRegex); + return; + } + + errorMatch.forEach(function(group, index) { + var idType; + + // index 0 is the whole match, skip it + if (!index) return; + + // link IDs if present in the group + idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; + if (idType && group) { + group = parseError(group, idType); + } else if (html_re.test(group)) { + // escape any html in non-IDs + group = '\\' + group + '\\'; + } + + // translate common words (e.g. node, way, relation) + if (commonEntities.includes(group)) { + group = t('QA.keepRight.entities.' + group); + } + + replacements['var' + index] = group; + }); + + return replacements; +} + + +function parseError(group, idType) { + + function fillPlaceholder(d) { return '' + d + ''; } + + // arbitrary node list of form: #ID, #ID, #ID... + function parseError211(list) { + var newList = []; + var items = list.split(', '); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + + // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... + function parseError231(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var id; + var layer; + + // item of form "#ID(layer)" + item = item.split('('); + + // ID has # at the front + id = item[0].slice(1); + id = fillPlaceholder('w' + id); + + // layer has trailing ) + layer = item[1].slice(0,-1); + + // TODO: translation + newList.push(id + ' (layer: ' + layer + ')'); + }); + + return newList.join(', '); + } + + // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... + function parseError294(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + var role; + var idType; + var id; + + // item of form "from/to node/relation #ID" + item = item.split(' '); + + // to/from role is more clear in quotes + role = '"' + item[0] + '"'; + + // first letter of node/relation provides the type + idType = item[1].slice(0,1); + + // ID has # at the front + id = item[2].slice(1); + id = fillPlaceholder(idType + id); + + item = [role, item[1], id].join(' '); + newList.push(item); + }); + + return newList.join(', '); + } + + // TODO: Handle error 401 template addition + + // arbitrary node list of form: #ID,#ID,#ID... + function parseWarning20(list) { + var newList = []; + var items = list.split(','); + + items.forEach(function(item) { + // ID has # at the front + var id = fillPlaceholder('n' + item.slice(1)); + newList.push(id); + }); + + return newList.join(', '); + } + + switch (idType) { + // simple case just needs a linking span + case 'n': + case 'w': + case 'r': + group = fillPlaceholder(idType + group); + break; + // some errors have more complex ID lists/variance + case '211': + group = parseError211(group); + break; + case '231': + group = parseError231(group); + break; + case '294': + group = parseError294(group); + break; + case '20': + group = parseWarning20(group); + } + + return group; +} + + export default { init: function() { if (!_krCache) { @@ -77,11 +254,8 @@ export default { // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php loadErrors: function(context, projection) { - var options = { - format: 'geojson' - }; + var options = { format: 'geojson' }; var rules = defaultRuleset.join(); - var that = this; // determine the needed tiles to cover the view var tiles = tiler @@ -136,6 +310,8 @@ export default { title: props.title }); + d.replacements = tokenReplacements(d); + _krCache.keepRight[d.id] = d; _krCache.rtree.insert(encodeErrorRtree(d)); }); diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 0092c535d..f5edc8a23 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,7 +1,7 @@ -import { t } from '../util/locale'; -import { parseErrorDescriptions, errorTypes } from '../util'; +import { event as d3_event } from 'd3-selection'; -import { clickLink } from '../util/keepRight'; +import { errorTypes } from '../../data/keepRight.json'; +import { t } from '../util/locale'; export function uiKeepRightDetails(context) { @@ -97,12 +97,18 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details-description-text') .html(function(d) { - return t(_titleBase + _templateErrorType + '.description', parseErrorDescriptions(d)); + return t(_titleBase + _templateErrorType + '.description', d.replacements); }); description.selectAll('.kr_error_description-id') .on('click', function() { clickLink(context, this.text); }); + + function clickLink(context, id) { + d3_event.preventDefault(); + context.layers().layer('osm').enabled(true); + context.zoomToEntity(id); + } } diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 978008572..878eb588a 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -1,10 +1,8 @@ import { t } from '../util/locale'; -import { utilEntityRoot } from '../util'; -import { clickLink } from '../util/keepRight'; import { svgIcon } from '../svg'; -export function uiKeepRightHeader(context) { +export function uiKeepRightHeader() { var _error; @@ -32,17 +30,12 @@ export function uiKeepRightHeader(context) { .attr('class', function(d) { return 'preset-icon-28 kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; }) - .call(svgIcon('#iD-icon-bolt', 'kr_error-fill')); headerEnter .append('div') .attr('class', 'kr_error-header-label') - .text(function(d) { return t('QA.keepRight.entities.' + d.object_type) + ' '; }) - .append('span') - .append('a') - .text(function(d) { return d.object_id; }) - .on('click', function(d) { clickLink(context, (utilEntityRoot(d.object_type) + d.object_id)); }); + .text(function(d) { return t('QA.keepRight.entities.' + d.object_type, { id: d.object_id }); }); } diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index a96b255cf..a6cee9fd7 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -1,11 +1,9 @@ -import { dispatch as d3_dispatch } from 'd3-dispatch'; import { event as d3_event, select as d3_select } from 'd3-selection'; import { svgIcon } from '../svg'; -import { errorTypes } from '../util'; import { t, textDirection } from '../util/locale'; import { tooltip } from '../util/tooltip'; import { geoExtent } from '../geo'; @@ -18,8 +16,6 @@ import { uiTooltipHtml } from './tooltipHtml'; export function uiMapData(context) { - var dispatch = d3_dispatch('change'); - var key = t('map_data.key'); var features = context.features().keys(); var layers = context.layers(); @@ -485,46 +481,6 @@ export function uiMapData(context) { } - function drawQAButtons(selection) { - var QAButtons = d3_select('.layer-QA').selectAll('li').select('label').select('input'); - var buttonSection = selection.selectAll('.QA-buttons') - .data([0]); - - // var buttonSection = selection.selectAll('.QA-buttons') - // .data([0]); - - // // exit - // buttonSection.exit() - // .remove(); - - // // enter - // var buttonEnter = buttonSection.enter() - // .append('div') - // .attr('class', 'QA-buttons'); - - // buttonEnter - // .append('button') - // .attr('class', 'button QA-toggle-on action') - // .text(t('QA.keepRight.toggle-on')) - // .on('click', function() { - // QAButtons.property('checked', true); - // dispatch.call('change'); - // }); - - // buttonEnter - // .append('button') - // .attr('class', 'button QA-toggle-off action') - // .text(t('QA.keepRight.toggle-off')) - // .on('click', function() { - // QAButtons.property('checked', false); - // dispatch.call('change'); - // }); - - // buttonSection = buttonSection - // .merge(buttonEnter); - } - - function drawListItems(selection, data, type, name, change, active) { var items = selection.selectAll('li') .data(data); diff --git a/modules/util/index.js b/modules/util/index.js index b386875c3..98755addd 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -14,7 +14,6 @@ export { utilExternalValidationRules } from './util'; export { utilFastMouse } from './util'; export { utilFunctor } from './util'; export { utilGetAllNodes } from './util'; -export { errorTypes, parseErrorDescriptions, clickLink } from './keepRight'; export { utilGetPrototypeOf } from './util'; export { utilGetSetValue } from './get_set_value'; export { utilHashcode } from './util'; @@ -29,7 +28,6 @@ export { utilRebind } from './rebind'; export { utilSetTransform } from './util'; export { utilSessionMutex } from './session_mutex'; export { utilStringQs } from './util'; -// export { utilSuggestNames } from './suggest_names'; export { utilTagText } from './util'; export { utilTiler } from './tiler'; export { utilTriggerEvent } from './trigger_event'; diff --git a/modules/util/keepRight/index.js b/modules/util/keepRight/index.js deleted file mode 100644 index 0f95dc9a7..000000000 --- a/modules/util/keepRight/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { errorTypes } from './errorSchema.json'; -export { parseError } from './parse_error'; -export { parseErrorDescriptions, clickLink } from './keepRight_error'; \ No newline at end of file diff --git a/modules/util/keepRight/keepRight_error.js b/modules/util/keepRight/keepRight_error.js deleted file mode 100644 index 6e50728a4..000000000 --- a/modules/util/keepRight/keepRight_error.js +++ /dev/null @@ -1,80 +0,0 @@ -import { event as d3_event } from 'd3-selection'; - -import { t } from '../locale'; -import { krError } from '../../osm'; - -import { errorTypes } from './errorSchema.json'; -import { parseError } from './parse_error'; - - -export function parseErrorDescriptions(entity) { - var parsedDetails = {}; - var html_re = new RegExp(/<\/[a-z][\s\S]*>/); - var commonEntities = [ - 'node', - 'way', - 'relation', - 'highway', - 'cycleway', - 'waterway', - 'riverbank' - ]; // TODO: expand this list, or implement a different translation function - - var errorType; - var errorTemplate; - var errorDescription; - var errorRegex; - var errorMatch; - - if (!(entity instanceof krError)) return; - - // find the matching template from the error schema - errorType = '_' + entity.error_type; - errorTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; - if (!errorTemplate) return; - - // some descriptions are just fixed text - if (!('regex' in errorTemplate)) return; - - // regex pattern should match description with variable details captured as groups - errorDescription = entity.description; - errorRegex = new RegExp(errorTemplate.description); - errorMatch = errorRegex.exec(errorDescription); - if (!errorMatch) { - // TODO: Remove, for regex dev testing - console.log('Unmatched:', errorType, errorDescription, errorRegex); - return; - } - - errorMatch.forEach(function(group, index) { - var idType; - - // index 0 is the whole match, skip it - if (!index) return; - - // link IDs if present in the group - idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; - if (idType && group) { - group = parseError(group, idType); - } else if (html_re.test(group)) { - // escape any html in non-IDs - group = '\\' + group + '\\'; - } - - // translate common words (e.g. node, way, relation) - if (commonEntities.includes(group)) { - group = t('QA.keepRight.entities.' + group); - } - - parsedDetails['var' + index] = group; - }); - - return parsedDetails; -} - - -export function clickLink(context, id) { - d3_event.preventDefault(); - context.layers().layer('osm').enabled(true); - context.zoomToEntity(id); - } diff --git a/modules/util/keepRight/parse_error.js b/modules/util/keepRight/parse_error.js deleted file mode 100644 index 37eed18dc..000000000 --- a/modules/util/keepRight/parse_error.js +++ /dev/null @@ -1,113 +0,0 @@ -export function parseError(group, idType) { - - function fillPlaceholder(d) { return '' + d + ''; } - - // arbitrary node list of form: #ID, #ID, #ID... - function parseError211(list) { - var newList = []; - var items = list.split(', '); - - items.forEach(function(item) { - // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - - // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... - function parseError231(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - var id; - var layer; - - // item of form "#ID(layer)" - item = item.split('('); - - // ID has # at the front - id = item[0].slice(1); - id = fillPlaceholder('w' + id); - - // layer has trailing ) - layer = item[1].slice(0,-1); - - // TODO: translation - newList.push(id + ' (layer: ' + layer + ')'); - }); - - return newList.join(', '); - } - - // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... - function parseError294(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - var role; - var idType; - var id; - - // item of form "from/to node/relation #ID" - item = item.split(' '); - - // to/from role is more clear in quotes - role = '"' + item[0] + '"'; - - // first letter of node/relation provides the type - idType = item[1].slice(0,1); - - // ID has # at the front - id = item[2].slice(1); - id = fillPlaceholder(idType + id); - - item = [role, item[1], id].join(' '); - newList.push(item); - }); - - return newList.join(', '); - } - - // TODO: Handle error 401 template addition - - // arbitrary node list of form: #ID,#ID,#ID... - function parseWarning20(list) { - var newList = []; - var items = list.split(','); - - items.forEach(function(item) { - // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); - newList.push(id); - }); - - return newList.join(', '); - } - - switch (idType) { - // simple case just needs a linking span - case 'n': - case 'w': - case 'r': - group = fillPlaceholder(idType + group); - break; - // some errors have more complex ID lists/variance - case '211': - group = parseError211(group); - break; - case '231': - group = parseError231(group); - break; - case '294': - group = parseError294(group); - break; - case '20': - group = parseWarning20(group); - } - - return group; -} \ No newline at end of file From 22e36be4f23eb662fd2d3ffa8c70be433c47516b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 4 Dec 2018 23:13:42 -0500 Subject: [PATCH 044/114] WIP on replacing documentation with direct calls to OSM wikibase --- modules/services/index.js | 3 ++ modules/services/osm_wikibase.js | 74 ++++++++++++++++++++++++++++++ modules/ui/tag_reference.js | 56 +++++++++++----------- test/spec/services/osm_wikibase.js | 49 ++++++++++++++++++++ 4 files changed, 155 insertions(+), 27 deletions(-) create mode 100644 modules/services/osm_wikibase.js create mode 100644 test/spec/services/osm_wikibase.js diff --git a/modules/services/index.js b/modules/services/index.js index 59c9d9524..cc2a3d729 100644 --- a/modules/services/index.js +++ b/modules/services/index.js @@ -3,6 +3,7 @@ import serviceMapRules from './maprules'; import serviceNominatim from './nominatim'; import serviceOpenstreetcam from './openstreetcam'; import serviceOsm from './osm'; +import serviceOsmWikibase from './osm_wikibase'; import serviceStreetside from './streetside'; import serviceTaginfo from './taginfo'; import serviceVectorTile from './vector_tile'; @@ -15,6 +16,7 @@ export var services = { mapillary: serviceMapillary, openstreetcam: serviceOpenstreetcam, osm: serviceOsm, + osmWikibase: serviceOsmWikibase, maprules: serviceMapRules, streetside: serviceStreetside, taginfo: serviceTaginfo, @@ -29,6 +31,7 @@ export { serviceNominatim, serviceOpenstreetcam, serviceOsm, + serviceOsmWikibase, serviceStreetside, serviceTaginfo, serviceVectorTile, diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js new file mode 100644 index 000000000..ef1d2eafa --- /dev/null +++ b/modules/services/osm_wikibase.js @@ -0,0 +1,74 @@ +import _debounce from 'lodash-es/debounce'; +import _forEach from 'lodash-es/forEach'; + +import { json as d3_json } from 'd3-request'; + +import { utilQsString } from '../util'; +import { currentLocale } from '../util/locale'; + + +var apibase = 'https://wiki.openstreetmap.org/w/api.php'; +var _inflight = {}; +var _wikibaseCache = {}; + + +var debouncedRequest = _debounce(request, 500, { leading: false }); + +function request(url, callback) { + if (_inflight[url]) return; + + _inflight[url] = d3_json(url, function (err, data) { + delete _inflight[url]; + callback(err, data); + }); +} + + +export default { + + init: function() { + _inflight = {}; + _wikibaseCache = {}; + }, + + + reset: function() { + _forEach(_inflight, function(req) { req.abort(); }); + _inflight = {}; + }, + + + docs: function(params, callback) { + var doRequest = params.debounce ? debouncedRequest : request; + + // if (params.value) path = 'tag/wiki_pages?'; + // else if (params.rtype) path = 'relation/wiki_pages?'; + + var obj = { + action: 'wbgetentities', + sites: 'wiki', + titles: 'Tag:amenity=parking', + languages: 'en', + origin: '*', + formatversion: 2, + format: 'json' + } + var url = apibase + '?' + utilQsString(obj); + doRequest(url, function(err, d) { + if (err) { + callback(err); + } else { + _wikibaseCache[url] = d.data; + callback(null, d.data); + } + }); + }, + + + apibase: function(_) { + if (!arguments.length) return apibase; + apibase = _; + return this; + } + +}; diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index 03f625019..a1dcf15d9 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -13,7 +13,8 @@ import { svgIcon } from '../svg'; export function uiTagReference(tag) { - var taginfo = services.taginfo; + // var taginfo = services.taginfo; + var wikibase = services.osmWikibase; var tagReference = {}; var _button = d3_select(null); @@ -22,44 +23,45 @@ export function uiTagReference(tag) { var _showing; - function findLocal(data) { - var locale = utilDetect().locale.toLowerCase(); - var localized; + // function findLocal(data) { + // var locale = utilDetect().locale.toLowerCase(); + // var localized; - if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br' - localized = _find(data, function(d) { - return d.lang.toLowerCase() === locale; - }); - if (localized) return localized; - } + // if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br' + // localized = _find(data, function(d) { + // return d.lang.toLowerCase() === locale; + // }); + // if (localized) return localized; + // } - // try the non-regional version of a language, like - // 'en' if the language is 'en-US' - if (locale.indexOf('-') !== -1) { - var first = locale.split('-')[0]; - localized = _find(data, function(d) { - return d.lang.toLowerCase() === first; - }); - if (localized) return localized; - } + // // try the non-regional version of a language, like + // // 'en' if the language is 'en-US' + // if (locale.indexOf('-') !== -1) { + // var first = locale.split('-')[0]; + // localized = _find(data, function(d) { + // return d.lang.toLowerCase() === first; + // }); + // if (localized) return localized; + // } - // finally fall back to english - return _find(data, function(d) { - return d.lang.toLowerCase() === 'en'; - }); - } + // // finally fall back to english + // return _find(data, function(d) { + // return d.lang.toLowerCase() === 'en'; + // }); + // } function load(param) { - if (!taginfo) return; + if (!wikibase) return; _button .classed('tag-reference-loading', true); - taginfo.docs(param, function show(err, data) { + wikibase.docs(param, function show(err, data) { var docs; if (!err && data) { - docs = findLocal(data); + // docs = findLocal(data); + debugger; } _body.html(''); diff --git a/test/spec/services/osm_wikibase.js b/test/spec/services/osm_wikibase.js new file mode 100644 index 000000000..1c87a6f8e --- /dev/null +++ b/test/spec/services/osm_wikibase.js @@ -0,0 +1,49 @@ +describe('iD.serviceOsmWikibase', function() { + var server, wikibase; + + + before(function() { + iD.services.osmWikibase = iD.serviceOsmWikibase; + }); + + after(function() { + delete iD.services.osmWikibase; + }); + + beforeEach(function() { + wikibase = iD.services.osmWikibase; + wikibase.init(); + server = sinon.fakeServer.create(); + }); + + afterEach(function() { + server.restore(); + }); + + + function query(url) { + return iD.utilStringQs(url.substring(url.indexOf('?') + 1)); + } + + + describe('#docs', function() { + it('calls the given callback with the results of the docs query', function() { + var callback = sinon.spy(); + wikibase.docs({key: 'amenity', value: 'parking'}, callback); + + server.respondWith('GET', /\/tag\/wiki_page/, + [200, { 'Content-Type': 'application/json' }, + '{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}'] + ); + server.respond(); + + expect(query(server.requests[0].url)).to.eql( + {key: 'amenity', value: 'parking'} + ); + expect(callback).to.have.been.calledWith( + null, [{'on_way':false,'lang':'en','on_area':true,'image':'File:Car park2.jpg'}] + ); + }); + }); + +}); From b2810105a5cd6d5b13df6a720c8fc8c23edf3eff Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 22 Dec 2018 00:19:10 -0500 Subject: [PATCH 045/114] Implement support for multilingual descriptions from wiki data items * Takes data directly from the Wikibase data items (OSM Wiki) https://wiki.openstreetmap.org/wiki/OpenStreetMap:Data_Items * Understands the difference in regions - e.g. will show different images depending on the local settings * Perf: Single request will get both the tag and key description --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- modules/services/osm_wikibase.js | 133 +++++++++++- modules/ui/tag_reference.js | 92 ++++---- test/index.html | 1 + test/spec/services/osm_wikibase.js | 328 +++++++++++++++++++++++++---- 6 files changed, 464 insertions(+), 94 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index c74f1a977..a06894368 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -405,7 +405,6 @@ en: inspector: no_documentation_combination: There is no documentation available for this tag combination no_documentation_key: There is no documentation available for this key - documentation_redirect: This documentation has been redirected to a new page show_more: Show More view_on_osm: View on openstreetmap.org all_fields: All fields @@ -418,6 +417,7 @@ en: choose: Select feature type results: "{n} results for {search}" reference: View on OpenStreetMap Wiki + edit_reference: Edit or translate on OSM Wiki back_tooltip: Change feature remove: Remove search: Search diff --git a/dist/locales/en.json b/dist/locales/en.json index da3671d75..f093e6e7d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -499,7 +499,6 @@ "inspector": { "no_documentation_combination": "There is no documentation available for this tag combination", "no_documentation_key": "There is no documentation available for this key", - "documentation_redirect": "This documentation has been redirected to a new page", "show_more": "Show More", "view_on_osm": "View on openstreetmap.org", "all_fields": "All fields", @@ -512,6 +511,7 @@ "choose": "Select feature type", "results": "{n} results for {search}", "reference": "View on OpenStreetMap Wiki", + "edit_reference": "Edit or translate on OSM Wiki", "back_tooltip": "Change feature", "remove": "Remove", "search": "Search", diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js index ef1d2eafa..22db05bd9 100644 --- a/modules/services/osm_wikibase.js +++ b/modules/services/osm_wikibase.js @@ -4,7 +4,6 @@ import _forEach from 'lodash-es/forEach'; import { json as d3_json } from 'd3-request'; import { utilQsString } from '../util'; -import { currentLocale } from '../util/locale'; var apibase = 'https://wiki.openstreetmap.org/w/api.php'; @@ -38,28 +37,140 @@ export default { }, - docs: function(params, callback) { + /** List of data items representing language regions. + * To regenerate, use Sophox query: http://tinyurl.com/y6v9ne2c (every instance of Q6999) + * A less accurate list can be seen here (everything that links to Q6999): + * https://wiki.openstreetmap.org/w/index.php?title=Special%3AWhatLinksHere&target=Item%3AQ6999&namespace=120 + */ + regionCodes: { + ar: 'Q7780', az: 'Q7781', bg: 'Q7782', bn: 'Q7783', ca: 'Q7784', cs: 'Q7785', da: 'Q7786', + de: 'Q6994', el: 'Q7787', es: 'Q7788', et: 'Q7789', fa: 'Q7790', fi: 'Q7791', fr: 'Q7792', + gl: 'Q7793', hr: 'Q7794', ht: 'Q7795', hu: 'Q7796', id: 'Q7797', it: 'Q7798', ja: 'Q7799', + ko: 'Q7800', lt: 'Q7801', lv: 'Q7802', ms: 'Q7803', nl: 'Q7804', no: 'Q7805', pl: 'Q7806', + pt: 'Q7807', ro: 'Q7808', ru: 'Q7809', sk: 'Q7810', sq: 'Q7811', sv: 'Q7812', tr: 'Q7813', + uk: 'Q7814', vi: 'Q7815', yue: 'Q7816', 'zh-hans': 'Q7817', 'zh-hant': 'Q7818', + }, + + + /** + * Get the best value for the property, or undefined if not found + * @param entity object from wikibase + * @param property string e.g. 'P4' for image + * @param langCode string e.g. 'fr' for French + */ + claimToValue: function(entity, property, langCode) { + if (!entity.claims[property]) return undefined; + var region = this.regionCodes[langCode]; + var preferredPick, regionPick; + _forEach(entity.claims[property], function(stmt) { + // If exists, use value limited to the needed language (has a qualifier P26 = region) + // Or if not found, use the first value with the "preferred" rank + if (!preferredPick && stmt.rank === 'preferred') { + preferredPick = stmt; + } + if (stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === region) { + regionPick = stmt; + } + }); + var result = regionPick || preferredPick; + + if (result) { + var datavalue = result.mainsnak.datavalue; + return datavalue.type === 'wikibase-entityid' ? datavalue.value.id : datavalue.value; + } else { + return undefined; + } + }, + + + getDescription: function(entity) { + if (entity.descriptions) { + // Assume that there will be at most two languages because of + // how we request it: English + possibly another one. + // Pick non-English description if available (if we have more than one) + var langs = Object.keys(entity.descriptions); + if (langs.length) { + var lng = langs.length > 1 && langs[0] === 'en' ? langs[1] : langs[0]; + return entity.descriptions[lng].value; + } + } + return undefined; + }, + + + toSitelink: function(key, value) { + var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key; + return result.replace(/_/g, ' ').trim(); + }, + + + getEntity: function(params, callback) { var doRequest = params.debounce ? debouncedRequest : request; - // if (params.value) path = 'tag/wiki_pages?'; - // else if (params.rtype) path = 'relation/wiki_pages?'; + var titles = []; + var languages = ['en']; + var result = {}; + var keySitelink = this.toSitelink(params.key); + var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false; + + if (params.langCode && params.langCode !== 'en') { + languages.push(params.langCode); + } + + if (_wikibaseCache[keySitelink]) { + result.key = _wikibaseCache[keySitelink]; + } else { + titles.push(keySitelink); + } + + if (tagSitelink) { + if (_wikibaseCache[tagSitelink]) { + result.key = _wikibaseCache[tagSitelink]; + } else { + titles.push(tagSitelink); + } + } + + if (!titles.length) { + // Nothing to do, we already had everything in the cache + return callback(null, result); + } var obj = { action: 'wbgetentities', sites: 'wiki', - titles: 'Tag:amenity=parking', - languages: 'en', + titles: titles.join('|'), + languages: languages.join('|'), origin: '*', - formatversion: 2, - format: 'json' - } + format: 'json', + // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069 + // We shouldn't use v1 until it gets fixed, but should switch to it afterwards + // formatversion: 2, + }; + var url = apibase + '?' + utilQsString(obj); doRequest(url, function(err, d) { if (err) { callback(err); + } else if (!d.success || d.error) { + callback(d.error.messages.map(function(v) { return v.html['*']; }).join('
')); } else { - _wikibaseCache[url] = d.data; - callback(null, d.data); + _forEach(d.entities, function(res) { + if (res.missing !== '') { + var title = res.sitelinks.wiki.title; + if (title === keySitelink) { + _wikibaseCache[keySitelink] = res; + result.key = res; + } else if (title === tagSitelink) { + _wikibaseCache[tagSitelink] = res; + result.tag = res; + } else { + console.log('Unexpected title ' + title); + } + } + }); + + callback(null, result); } }); }, diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index a1dcf15d9..d4ce97f5c 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -1,6 +1,3 @@ -import _find from 'lodash-es/find'; -import _omit from 'lodash-es/omit'; - import { event as d3_event, select as d3_select @@ -10,10 +7,10 @@ import { t } from '../util/locale'; import { utilDetect } from '../util/detect'; import { services } from '../services'; import { svgIcon } from '../svg'; +import { utilQsString } from '../util'; export function uiTagReference(tag) { - // var taginfo = services.taginfo; var wikibase = services.osmWikibase; var tagReference = {}; @@ -22,33 +19,40 @@ export function uiTagReference(tag) { var _loaded; var _showing; + /** + * @returns {{itemTitle: String, description: String, image: String|null}|null} + **/ + function findLocal(data) { + var entity = data.tag || data.key; + if (!entity) return null; - // function findLocal(data) { - // var locale = utilDetect().locale.toLowerCase(); - // var localized; + var result = { + title: entity.title, + description: wikibase.getDescription(entity), + }; - // if (locale !== 'pt-br') { // see #3776, prefer 'pt' over 'pt-br' - // localized = _find(data, function(d) { - // return d.lang.toLowerCase() === locale; - // }); - // if (localized) return localized; - // } + if (entity.claims) { + var langCode = utilDetect().locale.toLowerCase(); + var url; + var image = wikibase.claimToValue(entity, 'P4', langCode); + if (image) { + url = 'https://commons.wikimedia.org/w/index.php'; + } else { + image = wikibase.claimToValue(entity, 'P28', langCode); + if (image) { + url = 'https://wiki.openstreetmap.org/w/index.php'; + } + } + if (image) { + result.image = { + url: url, + title: 'Special:Redirect/file/' + image + }; + } + } - // // try the non-regional version of a language, like - // // 'en' if the language is 'en-US' - // if (locale.indexOf('-') !== -1) { - // var first = locale.split('-')[0]; - // localized = _find(data, function(d) { - // return d.lang.toLowerCase() === first; - // }); - // if (localized) return localized; - // } - - // // finally fall back to english - // return _find(data, function(d) { - // return d.lang.toLowerCase() === 'en'; - // }); - // } + return result; + } function load(param) { @@ -57,33 +61,34 @@ export function uiTagReference(tag) { _button .classed('tag-reference-loading', true); - wikibase.docs(param, function show(err, data) { + wikibase.getEntity(param, function show(err, data) { var docs; if (!err && data) { - // docs = findLocal(data); - debugger; + docs = findLocal(data); } _body.html(''); if (!docs || !docs.title) { - if (param.hasOwnProperty('value')) { - load(_omit(param, 'value')); // retry with key only - } else { - _body - .append('p') - .attr('class', 'tag-reference-description') - .text(t('inspector.no_documentation_key')); - done(); - } + _body + .append('p') + .attr('class', 'tag-reference-description') + .text(t('inspector.no_documentation_key')); + done(); return; } - if (docs.image && docs.image.thumb_url_prefix) { + if (docs.image) { + var imageUrl = docs.image.url + '?' + utilQsString({ + title: docs.image.title, + width: 100, + height: 100, + }); + _body .append('img') .attr('class', 'tag-reference-wiki-image') - .attr('src', docs.image.thumb_url_prefix + '100' + docs.image.thumb_url_suffix) + .attr('src', imageUrl) .on('load', function() { done(); }) .on('error', function() { d3_select(this).remove(); done(); }); } else { @@ -93,7 +98,7 @@ export function uiTagReference(tag) { _body .append('p') .attr('class', 'tag-reference-description') - .text(docs.description || t('inspector.documentation_redirect')); + .text(docs.description || t('inspector.no_documentation_key')); _body .append('a') @@ -173,6 +178,7 @@ export function uiTagReference(tag) { } else if (_loaded) { done(); } else { + tag.langCode = utilDetect().locale.toLowerCase(); load(tag); } }); diff --git a/test/index.html b/test/index.html index b455624b3..8e78ab96d 100644 --- a/test/index.html +++ b/test/index.html @@ -110,6 +110,7 @@ + diff --git a/test/spec/services/osm_wikibase.js b/test/spec/services/osm_wikibase.js index 1c87a6f8e..2536fd246 100644 --- a/test/spec/services/osm_wikibase.js +++ b/test/spec/services/osm_wikibase.js @@ -1,49 +1,301 @@ -describe('iD.serviceOsmWikibase', function() { - var server, wikibase; +describe('iD.serviceOsmWikibase', function () { + var server, wikibase; + + before(function () { + iD.services.osmWikibase = iD.serviceOsmWikibase; + }); + + after(function () { + delete iD.services.osmWikibase; + }); + + beforeEach(function () { + wikibase = iD.services.osmWikibase; + wikibase.init(); + server = sinon.fakeServer.create(); + }); + + afterEach(function () { + server.restore(); + }); - before(function() { - iD.services.osmWikibase = iD.serviceOsmWikibase; - }); + function query(url) { + return iD.utilStringQs(url.substring(url.indexOf('?') + 1)); + } - after(function() { - delete iD.services.osmWikibase; - }); - - beforeEach(function() { - wikibase = iD.services.osmWikibase; - wikibase.init(); - server = sinon.fakeServer.create(); - }); - - afterEach(function() { - server.restore(); - }); - - - function query(url) { - return iD.utilStringQs(url.substring(url.indexOf('?') + 1)); + var keyData = { + pageid: 205725, + ns: 120, + title: 'Item:Q61', + lastrevid: 1721242, + modified: '2018-12-18T07:00:43Z', + type: 'item', + id: 'Q61', + labels: { + en: {language: 'en', value: 'amenity'} + }, + descriptions: { + en: {language: 'en', value: 'For describing useful and important facilities for visitors and residents.'} + }, + aliases: {}, + claims: { + P2: [ // instance of + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q7'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P16: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'amenity', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ], + P25: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P9: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q8'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P6: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q15'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'preferred' + }, + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} + }, + type: 'statement', + qualifiers: { + P26: [ + { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} + } + ] + }, + rank: 'normal' + } + ], + P28: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'Mapping-Features-Parking-Lot.png', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ] + }, + sitelinks: { + wiki: { + site: 'wiki', + title: 'Key:amenity', + badges: [] + } } + }; + var tagData = { + pageid: 210934, + ns: 120, + title: 'Item:Q4904', + lastrevid: 1718041, + modified: '2018-12-18T03:51:05Z', + type: 'item', + id: 'Q4904', + labels: { + en: {language: 'en', value: 'amenity=parking'} + }, + descriptions: { + en: {language: 'en', value: 'A place for parking cars'}, + fr: {language: 'fr', value: 'Un lieu pour garer des voitures'} + }, + aliases: {}, + claims: { + P2: [ // instance of = Q2 (tag) + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q2'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P19: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'amenity=parking', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ], + P10: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q61'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P4: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'commonsMedia', + datavalue: {value: 'Primary image.jpg', type: 'string'} + }, + type: 'statement', + rank: 'preferred' + } + ], + P6: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'preferred' + }, + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q13'}, type: 'wikibase-entityid'} + }, + type: 'statement', + qualifiers: { + P26: [ + { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} + } + ] + }, + rank: 'normal' + } + ], + P25: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ] + }, + sitelinks: { + wiki: { + site: 'wiki', + title: 'Tag:amenity=parking', + badges: [] + } + } + }; - describe('#docs', function() { - it('calls the given callback with the results of the docs query', function() { - var callback = sinon.spy(); - wikibase.docs({key: 'amenity', value: 'parking'}, callback); + describe('#getEntity', function () { + it('calls the given callback with the results of the getEntity data item query', function () { + var callback = sinon.spy(); + wikibase.getEntity({key: 'amenity', value: 'parking', langCode: 'fr'}, callback); - server.respondWith('GET', /\/tag\/wiki_page/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}'] - ); - server.respond(); + server.respondWith('GET', /action=wbgetentities/, + [200, {'Content-Type': 'application/json'}, JSON.stringify({ + entities: { + Q61: keyData, + Q4904: tagData + }, + success: 1 + })] + ); + server.respond(); - expect(query(server.requests[0].url)).to.eql( - {key: 'amenity', value: 'parking'} - ); - expect(callback).to.have.been.calledWith( - null, [{'on_way':false,'lang':'en','on_area':true,'image':'File:Car park2.jpg'}] - ); - }); + expect(query(server.requests[0].url)).to.eql( + { + action: 'wbgetentities', + format: 'json', + languages: 'en|fr', + origin: '*', + sites: 'wiki', + titles: 'Key:amenity|Tag:amenity=parking', + } + ); + expect(callback).to.have.been.calledWith(null, {key: keyData, tag: tagData}); }); + }); + + + it('creates correct sitelinks', function () { + expect(wikibase.toSitelink('amenity')).to.eql('Key:amenity'); + expect(wikibase.toSitelink('amenity_')).to.eql('Key:amenity'); + expect(wikibase.toSitelink('_amenity_')).to.eql('Key: amenity'); + expect(wikibase.toSitelink('amenity or_not_')).to.eql('Key:amenity or not'); + expect(wikibase.toSitelink('amenity', 'parking')).to.eql('Tag:amenity=parking'); + expect(wikibase.toSitelink(' amenity_', '_parking_')).to.eql('Tag: amenity = parking'); + expect(wikibase.toSitelink('amenity or_not', '_park ing_')).to.eql('Tag:amenity or not= park ing'); + }); + + it('gets correct value from entity', function () { + expect(wikibase.claimToValue(tagData, 'P4', 'en')).to.eql('Primary image.jpg'); + expect(wikibase.claimToValue(keyData, 'P6', 'en')).to.eql('Q15'); + expect(wikibase.claimToValue(keyData, 'P6', 'fr')).to.eql('Q15'); + expect(wikibase.claimToValue(keyData, 'P6', 'de')).to.eql('Q14'); + }); + + it('gets correct description from entity', function () { + expect(wikibase.getDescription(tagData)).to.eql('Un lieu pour garer des voitures'); + expect(wikibase.getDescription(keyData)).to.eql('For describing useful and important facilities for visitors and residents.'); + }); }); From 3785ffb15478b7524c59e1ae0ecb6731f3581df3 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 22 Dec 2018 12:12:28 -0500 Subject: [PATCH 046/114] Simplify translation strings --- data/core.yaml | 441 +++++++-------- data/keepRight.json | 921 +++++++++++++++++--------------- dist/locales/en.json | 591 +++++++++----------- modules/services/keepRight.js | 54 +- modules/ui/keepRight_details.js | 85 ++- 5 files changed, 990 insertions(+), 1102 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index fce1134da..057ea195d 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -667,263 +667,192 @@ en: node: "Node {id}" way: "Way {id}" relation: "Relation {id}" - highway: "Highway {id}" - cycleway: "Cycleway {id}" - waterway: "Waterway {id}" - riverbank: "Riverbank {id}" errorTypes: - errors: - _30: - title: 'non-closed_areas' - description: 'This way is tagged with {var1}={var2} and should be closed-loop.' - _40: - title: 'dead-ended one-ways' - description: 'The first node (id {var1}) of this one-way is not connected to any other way.' - _41: - title: '' - description: 'The last node (id {var1}) of this one-way is not connected to any other way.' - _42: - title: '' - description: 'This node cannot be reached because one-ways only lead away from here.' - _43: - title: '' - description: 'You cannot escape from this node because one-ways only lead to here.' - _50: - title: 'almost-junctions' - description: 'This node is very close but not connected to way #{var1}.' - _70: - title: 'missing tags' - description: 'This {var1} has an empty tag: "{var2}".' - _71: - title: 'way without tags' - description: 'This way has no tags.' - _72: - title: 'node without tags' - description: 'This node is not member of any way and doesn''t have any tags.' - _73: - title: 'tracktype with no highway tag' - description: 'This way has a "tracktype" tag but no "highway" tag.' - _74: - title: 'empty tag' - description: 'This {var1} has an empty tag: "{var2}".' - _75: - title: 'name without tags' - description: 'This {var1} has a name ("{var2}") but no other tags.' - _90: - title: 'motorways without ref' - description: 'This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag.' - _100: - title: 'places of worship without religion' - description: 'This {var1} is tagged as place of worship and therefore needs a religion tag.' - _110: - title: 'point of interest without name' - description: 'This node is tagged as {var1} and therefore needs a name tag.' - _120: - title: 'ways without nodes' - description: 'This way has just one single node.' - _130: - title: 'floating islands' - description: 'This way is not connected to the rest of the map.' - _150: - title: 'railway crossing without tag' - description: 'This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing.' - _160: - title: 'wrongly used railway tag' - description: 'There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing.' - _170: - title: 'FIXME tagged items' - description: '{var1}' - _180: - title: 'relations without type' - description: 'This relation has no type tag which is mandatory for relations.' - _190: - title: 'intersections without junctions' - description: 'Finds way crossings on same layer without common node as a junction.' - _191: - title: 'highway-highway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' - _192: - title: 'highway-waterway' - description: 'This {var1} intersects the {var2} #{var3}.' - _193: - title: 'highway-riverbank' - description: 'This {var1} intersects the {var2} #{var3}.' - _194: - title: 'waterway-waterway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' - _195: - title: 'cycleway-cycleway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' - _196: - title: 'highway-cycleway' - description: 'This {var1} intersects the {var2} #{var3} but there is no junction node.' - _197: - title: 'cycleway-waterway' - description: 'This {var1} intersects the {var2} #{var3}.' - _198: - title: 'cycleway-riverbank' - description: 'This {var1} intersects the {var2} #{var3}.' - _200: - title: 'overlapping ways' - description: 'Finds overlapping ways on same layer.' - _201: - title: 'highway-highway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _202: - title: 'highway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _203: - title: 'highway-riverbank' - description: 'This {var1} overlaps the {var2} #{var3}.' - _204: - title: 'waterway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _205: - title: 'cycleway-cycleway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _206: - title: 'highway-cycleway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _207: - title: 'cycleway-waterway' - description: 'This {var1} overlaps the {var2} #{var3}.' - _208: - title: 'cycleway-riverbank' - description: 'This {var1} overlaps the {var2} #{var3}.' - _210: - title: 'loopings' - description: 'These errors contain self intersecting ways.' - _211: - title: '' - description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' - _212: - title: '' - description: 'This way has only two different nodes and contains one of them more than once.' - _220: - title: 'misspelled tags' - description: 'This {var1} is tagged "{var2}"="{var3}" where "{var4}" looks like "{var5}".' - _221: - title: '' - description: 'This {var1} has a tag with key "key"="{var2}".' - _230: - title: 'layer conflicts' - description: '' - _231: - title: 'mixed layers intersection' - description: 'This node is a junction of ways on different layers: {var1}.' - _232: - title: 'strange layers' - description: 'This {var1} is tagged with layer {var2}. This need not be an error but it looks strange.' - _270: - title: 'motorways connected directly' - description: 'This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.' - _280: - title: 'boundaries' - description: '' - _281: - title: 'missing name' - description: 'This boundary has no name.' - _282: - title: 'missing admin level' - description: 'The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries.' - _283: - title: 'no closed loop' - description: 'The boundary of {var1} is not closed-loop.' - _284: - title: 'splitting boundary' - description: 'The boundary of {var1} splits here.' - _285: - title: 'admin_level too high' - description: 'This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations.' - _290: - title: 'restrictions' - description: 'Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat.' - _291: - title: 'missing type' - description: 'This turn-restriction has no known restriction type.' - _292: - title: 'missing from way' - description: 'A turn-restriction needs exactly one "from" member. This one has {var1}.' - _293: - title: 'missing to way' - description: 'A turn-restriction needs exactly one "to" member. This one has {var1}.' - _294: - title: 'from or to not a way' - description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' - _295: - title: 'via is not on the way ends' - description: '"via" (node #{var1}) is not the first or the last member of "from" (way #{var2}).' - _296: - title: 'wrong restriction angle' - description: 'restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?' - _297: - title: 'wrong direction of to member' - description: 'wrong direction of to way {var1}.' - _298: - title: 'already restricted by oneway' - description: 'entry already prohibited by oneway tag on {var1}.' - _310: - title: 'roundabouts' - description: 'Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1.' - _311: - title: 'not closed loop' - description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' - _312: - title: 'wrong direction' - description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around' - _313: - title: 'faintly connected' - description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' - _320: - title: '*_link connections' - description: 'This way is tagged as highway={var1}_link but doesn''t have a connection to any other {var1} or {var1}_link.' - _350: - title: 'bridge-tags' - description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' - _370: - title: 'doubled places' - description: 'This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant.' - _380: - title: 'non-physical use of sport-tag' - description: 'This way is tagged "sport"="{var1}" but has no physical tag like e.g. leisure, building, amenity or highway.' - _400: - title: 'geometry glitches' - description: '' - _401: - title: 'missing turn restriction' - description: 'ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.' - addition: ' from way {var3} to {var4}.' - _402: - title: 'impossible angles' - description: 'this way bends in a very sharp angle here.' - _410: - title: 'website' - description: 'Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*.' - _411: - title: 'http error' - description: 'The URL cannot be opened (HTTP status code {var2}).' - _412: - title: 'domain hijacking' - description: 'Possible domain squatting: The URL has Suspicious text: "{var2}".' - _413: - title: 'non-match' - description: 'Content of the URL did not contain these keywords: ({var2}).' - warnings: - _20: - title: 'multiple nodes on the same spot' - description: 'There is more than one node in this spot. Offending node IDs: {var1}.' - _60: - title: 'depreciated tags' - description: 'This {var1} uses deprecated tag "{var2}"="{var3}". Please use {var4} instead!' - _300: - title: 'missing maxspeed' - description: 'missing maxspeed tag.' - _360: - title: 'language unknown' - description: 'It would be nice if this {var1} had an additional tag "name:XX"="{var2}" where XX shows the language of its name "{var2}".' - _390: - title: 'missing tracktype' - description: This track doesn't have a tracktype. + 20: + title: 'Multiple nodes on the same spot' + description: 'There is more than one node in this spot. Node IDs: {0}.' + 30: + title: 'Non-closed areas' + description: 'This way is tagged with "{0}={1}" and should be a closed loop.' + 40: + title: 'Impossible oneways' + description: 'The first node ({0}) of this oneway is not connected to any other way.' + 41: + description: 'The last node ({0}) of this oneway is not connected to any other way.' + 42: + description: 'You cannot reach this node because all ways leading from it are oneway.' + 43: + description: 'You cannot escape from this node because all ways leading to it are oneway.' + 50: + title: 'Almost-junctions' + description: 'This node is very close but not connected to way {0}.' + 60: + title: 'Deprecated tags' + description: 'This {0} uses deprecated tag "{1}={2}". Please use "{3}" instead.' + 70: + title: 'Missing tags' + description: 'This {0} has an empty tag: "{1}".' + 71: + description: 'This way has no tags.' + 72: + description: 'This node is not member of any way and doesn''t have any tags.' + 73: + description: 'This way has a "tracktype" tag but no "highway" tag.' + 74: + description: 'This {0} has an empty tag: "{1}".' + 75: + description: 'This {0} has a name "{1}" but no other tags.' + 90: + title: 'Motorway without ref tag' + description: 'This way is tagged as motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' + 100: + title: 'Place of worship without religion' + description: 'This {0} is tagged as place of worship and therefore needs a "religion" tag.' + 110: + title: 'Point of interest without name' + description: 'This node is tagged as {0} and therefore needs a name tag.' + 120: + title: 'Way without nodes' + description: 'This way has just one single node.' + 130: + title: 'Disconnected way' + description: 'This way is not connected to the rest of the map.' + 150: + title: 'Railway crossing without tag' + description: 'This crossing of a highway and a railway needs to be tagged as "railway=crossing" or "railway=level_crossing".' + 160: + title: 'Wrongly used railway tag' + description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing.' + 170: + title: 'FIXME tagged items' + description: '{0}' + 180: + title: 'Relation without type' + description: 'This relation is missing a "type" tag.' + 190: + title: 'Intersection without junctions' + description: 'This {0} intersects the {1} {2} but there is no junction node.' + 200: + title: 'Overlapping ways' + description: 'This {0} overlaps the {1} {2}.' + 210: + title: 'Self-intersecting ways' + description: 'These errors contain self intersecting ways.' + 211: + description: 'This way contains more than one node multiple times. Nodes are {0}. This may or may not be an error.' + 212: + description: 'This way has only two different nodes and contains one of them more than once.' + 220: + title: 'Misspelled tag' + description: 'This {0} is tagged "{1}={2}" where "{3}" looks like "{4}".' + 221: + description: 'This {0} has a tag with key "{1}".' + 230: + title: 'Layer Conflict' + description: 'This node is a junction of ways on different layers.' + 231: + description: 'This node is a junction of ways on different layers: {0}.' + 232: + description: 'This {0} is tagged with "layer={1}". This need not be an error but it looks strange.' + 270: + title: 'Unusual motorway connection' + description: 'This node is a junction of a motorway and a highway other than "motorway", "motorway_link", "trunk", "rest_area", or "construction". Connection to "service" or "unclassified" is only valid if it has "access=no/private", or it leads to a motorway service area, or if it is a "service=parking_aisle".' + 280: + title: 'Boundary issue' + description: 'There is an unspecified issue with this boundary.' + 281: + title: 'Boundary missing name' + description: 'This boundary has no name.' + 282: + title: 'Boundary missing admin level' + description: 'The boundary of {0} has no valid numeric admin_level. Please do not mix admin levels (e.g. "6;7"). Always tag the lowest admin_level of all boundaries.' + 283: + title: 'Boundary not a closed loop' + description: 'The boundary of {0} is not a closed loop.' + 284: + title: 'Boundary is split' + description: 'The boundary of {0} splits here.' + 285: + title: 'Boundary admin_level too high' + description: 'This boundary way has "admin_level={0}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.' + 290: + title: 'Restriction issue' + description: 'There is an unspecified issue with this restriction.' + 291: + title: 'Restriction missing type' + description: 'This turn restriction has no known restriction type.' + 292: + title: 'Restriction missing "from" way' + description: 'A turn restriction needs exactly one "from" member. This one has {0}.' + 293: + title: 'Restriction missing "to" way' + description: 'A turn restriction needs exactly one "to" member. This one has {0}.' + 294: + title: 'Restriction "from" or "to" is not a way' + description: '"from" and "to" members of turn restrictions need to be ways. {0}.' + 295: + title: 'Restriction "via" is not on the way ends' + description: '"via" (node #{0}) is not the first or the last member of "from" (way #{1}).' + 296: + title: 'Wrong restriction angle' + description: 'Restriction type is "{0}" but angle is {1} degrees. Maybe the restriction type is not appropriate?' + 297: + title: 'Wrong direction of to member' + description: 'Wrong direction of "to" way {0}.' + 298: + title: 'Redundant restriction - oneway' + description: 'Entry already prohibited by "oneway" tag on {0}.' + 300: + title: 'Missing maxspeed' + description: 'Missing maxspeed tag.' + 310: + title: 'Roundabout issue' + description: 'There is an unspecified issue with this roundabout.' + 311: + title: 'Roundabout not closed loop' + description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' + 312: + title: 'Roundabout wrong direction' + description: 'If this {0} is in a country with {1}-hand traffic then its orientation goes the wrong way around' + 313: + title: 'Roundabout weakly connected' + description: 'This roundabout has only {0} other road(s) connected. Roundabouts typically have 3 or more.' + 320: + title: 'Improper link connection' + description: 'This way is tagged as "highway={0}_link" but doesn''t have a connection to any other "{1}" or "{2}_link".' + 350: + title: 'Improper bridge tags' + description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {0}.' + 360: + title: 'Language unknown' + description: 'It would be nice if this {0} had an additional tag "name:XX"="{1}" where XX shows the language of its name "{1}".' + 370: + title: 'Doubled places' + description: 'This node has tags in common with the surrounding way #{0} {1} and seems to be redundant.' + 380: + title: 'Non-physical use of sport tag' + description: 'This way is tagged "sport={0}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' + 390: + title: 'Missing tracktype' + description: This track doesn't have a "tracktype" tag. + 400: + title: 'Geometry issue' + description: 'There is an unspecified issue with the geometry here.' + 401: + title: 'Missing turn restriction' + description: 'Ways {0} and {1} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {2} to {3}.' + 402: + title: 'Impossible angles' + description: 'This way bends in a very sharp angle here.' + 410: + title: 'Website issue' + description: 'There is an unspecified issue with a contact website or URL.' + 411: + description: 'The URL cannot be opened (HTTP status code {0}).' + 412: + description: 'Possible domain squatting: The URL has suspicious text: "{0}".' + 413: + description: 'Possible non-match. Content of the URL did not contain these keywords: ({0}).' streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/data/keepRight.json b/data/keepRight.json index 447a37037..6150e39c4 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -1,424 +1,501 @@ - { - "errorTypes": { - "errors": { - "_30": { - "title": "non-closed_areas", - "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop", - "regex": true - }, - "_40": { - "title": "dead-ended one-ways", - "description": "The first node \\(id (\\d+)\\) of this one-way is not connected to any other way", - "IDs": ["n"], - "regex": true - }, - "_41": { - "title": "", - "description": "The last node \\(id (\\d+)\\) of this one-way is not connected to any other way", - "IDs": ["n"], - "regex": true - }, - "_42": { - "title": "", - "description": "This node cannot be reached because one-ways only lead away from here" - }, - "_43": { - "title": "", - "description": "You cannot escape from this node because one-ways only lead to here" - }, - "_50": { - "title": "almost-junctions", - "description": "This node is very close but not connected to way #(\\d+)", - "IDs": ["w"], - "regex": true - }, - "_70": { - "title": "missing tags", - "description": "" - }, - "_71": { - "title": "", - "description": "This way has no tags" - }, - "_72": { - "title": "", - "description": "This node is not member of any way and does not have any tags" - }, - "_73": { - "title": "", - "description": "This way has a tracktype tag but no highway tag" - }, - "_74": { - "title": "missing tags", - "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", - "regex": true - }, - "_75": { - "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", - "regex": true - }, - "_90": { - "title": "motorways without ref", - "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" - }, - "_100": { - "title": "places of worship without religion", - "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag", - "regex": true - }, - "_110": { - "title": "point of interest without name", - "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag", - "regex": true - }, - "_120": { - "title": "ways without nodes", - "description": "This way has just one single node" - }, - "_130": { - "title": "floating islands", - "description": "This way is not connected to the rest of the map" - }, - "_150": { - "title": "railway crossing without tag", - "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" - }, - "_160": { - "title": "wrongly used railway tag", - "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" - }, - "_170": { - "title": "FIXME tagged items", - "description": "(.*)", - "regex": true - }, - "_180": { - "title": "relations without type", - "description": "This relation has no type tag which is mandatory for relations" - }, - "_190": { - "title": "intersections without junctions", - "description": "Finds way crossings on same layer without common node as a junction" - }, - "_191": { - "title": "highway-highway", - "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"], - "regex": true - }, - "_192": { - "title": "highway-waterway", - "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_193": { - "title": "highway-riverbank", - "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_194": { - "title": "waterway-waterway", - "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"], - "regex": true - }, - "_195": { - "title": "cycleway-cycleway", - "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"], - "regex": true - }, - "_196": { - "title": "highway-cycleway", - "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "", "w"], - "regex": true - }, - "_197": { - "title": "cycleway-waterway", - "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_198": { - "title": "cycleway-riverbank", - "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_200": { - "title": "overlapping ways", - "description": "Finds overlapping ways on same layer" - }, - "_201": { - "title": "highway-highway", - "description": "This (highway) overlaps the (highway) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_202": { - "title": "highway-waterway", - "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_203": { - "title": "highway-riverbank", - "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_204": { - "title": "waterway-waterway", - "description": "This (waterway) overlaps the (waterway) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_205": { - "title": "cycleway-cycleway", - "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_206": { - "title": "highway-cycleway", - "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_207": { - "title": "cycleway-waterway", - "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_208": { - "title": "cycleway-riverbank", - "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "", "w"], - "regex": true - }, - "_210": { - "title": "loopings", - "description": "These errors contain self intersecting ways" - }, - "_211": { - "title": "", - "description": "This way contains more than one node at least twice\\. Nodes are ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", - "IDs": ["211"], - "regex": true - }, - "_212": { - "title": "", - "description": "This way has only two different nodes and contains one of them more than once" - }, - "_220": { - "title": "misspelled tags", - "description": "This (node|way|relation) is tagged '([\\w]+)(:([\\w]+))?=(.+)' where "(\\2|\\3|\\4|\\5)" looks like "([\\w\\s]+)"", - "regex": true - }, - "_221": { - "title": "", - "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)", - "regex": true - }, - "_230": { - "title": "layer conflicts", - "description": "Connected ways should be on the same layer. Crossings on intermediate nodes of ways on different layers are obviously wrong. Junctions on end-nodes of ways on different layers are also deprecated, but common practice. So you may ignore this part of the check and switch them off separately. Please note that bridges are set to layer +1, and tunnels to -1, anything else to layer 0 implicitly if no layer tag is present." - }, - "_231": { - "title": "mixed layers intersection", - "description": "This node is a junction of ways on different layers: ((?:#\\d+\\(-?\\d+\\),?)+)", - "IDs": ["231"], - "regex": true - }, - "_232": { - "title": "strange layers", - "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange", - "regex": true - }, - "_270": { - "title": "motorways connected directly", - "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." - }, - "_280": { - "title": "boundaries", - "description": "Administrative Boundaries can be expressed either by tagging ways or by adding them to a relation. They should be closed-loop sequences of ways, they must not self-intersect or split and they must have a name and an admin_level." - }, - "_281": { - "title": "missing name", - "description": "This boundary has no name" - }, - "_282": { - "title": "missing admin level", - "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*", - "regex": true - }, - "_283": { - "title": "no closed loop", - "description": "The boundary of (.+) is not closed-loop", - "regex": true - }, - "_284": { - "title": "splitting boundary", - "description": "The boundary of (.+) splits here", - "regex": true - }, - "_285": { - "title": "admin_level too high", - "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", - "regex": true - }, - "_290": { - "title": "restrictions", - "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" - }, - "_291": { - "title": "missing type", - "description": "This turn-restriction has no (?:known )?restriction type", - "regex": true - }, - "_292": { - "title": "missing from way", - "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)", - "regex": true - }, - "_293": { - "title": "missing to way", - "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)", - "regex": true - }, - "_294": { - "title": "from or to not a way", - "description": "From- and To-members of turn restrictions need to be ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)", - "IDs": ["294"], - "regex": true - }, - "_295": { - "title": "via is not on the way ends", - "description": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)", - "IDs": ["n", "w"], - "regex": true - }, - "_296": { - "title": "wrong restriction angle", - "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?", - "regex": true - }, - "_297": { - "title": "wrong direction of to member", - "description": "wrong direction of to way (\\d+)", - "IDs": ["w"], - "regex": true - }, - "_298": { - "title": "already restricted by oneway", - "description": "entry already prohibited by oneway tag on (\\d+)", - "IDs": ["w"], - "regex": true - }, - "_310": { - "title": "roundabouts", - "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" - }, - "_311": { - "title": "not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" - }, - "_312": { - "title": "wrong direction", - "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", - "regex": true - }, - "_313": { - "title": "faintly connected", - "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three", - "regex": true - }, - "_320": { - "title": "*_link connections", - "description": "This way is tagged as highway=(\\w+)_link but doesn't have a connection to any other \\1 or \\1_link", - "regex": true - }, - "_350": { - "title": "bridge-tags", - "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", - "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...", - "regex": true - }, - "_370": { - "title": "doubled places", - "description": "This node has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and seems to be redundand", - "IDs": ["w"], - "regex": true - }, - "_380": { - "title": "non-physical use of sport-tag", - "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway", - "regex": true - }, - "_400": { - "title": "geometry glitches", - "description": "" - }, - "_401": { - "title": "missing turn restriction", - "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?", - "IDs": ["w", "w", "w", "w"], - "regex": true - }, - "_402": { - "title": "impossible angles", - "description": "this way bends in a very sharp angle here" - }, - "_410": { - "title": "website", - "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" - }, - "_411": { - "title": "http error", - "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", - "regex": true - }, - "_412": { - "title": "domain hijacking", - "description": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''", - "regex": true - }, - "_413": { - "title": "non-match", - "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)", - "regex": true - } - }, - "warnings": { - "_20": { - "title": "multiple nodes on the same spot", - "description": "There is more than one node in this spot\\. Offending node IDs: ((?:#\\d+,?)+)", - "IDs": ["20"], - "regex": true - }, - "_60": { - "title": "depreciated tags", - "description": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead!", - "regex": true - }, - "_300": { - "title": "missing maxspeed", - "description": "missing maxspeed tag" - }, - "_360": { - "title": "language unknown", - "description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'", - "regex": true - }, - "_390": { - "title": "missing tracktype", - "description": "This track doesn''t have a tracktype" - } - } + "errorTypes": { + "20": { + "title": "multiple nodes on the same spot", + "severity": "warning", + "description": "There is more than one node in this spot\\. Offending node IDs: ((?:#\\d+,?)+)", + "IDs": ["20"], + "regex": true + }, + "30": { + "title": "non-closed_areas", + "severity": "error", + "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop", + "regex": true + }, + "40": { + "title": "dead-ended one-ways", + "severity": "error", + "description": "The first node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "IDs": ["n"], + "regex": true + }, + "41": { + "title": "", + "severity": "error", + "description": "The last node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "IDs": ["n"], + "regex": true + }, + "42": { + "title": "", + "severity": "error", + "description": "This node cannot be reached because one-ways only lead away from here" + }, + "43": { + "title": "", + "severity": "error", + "description": "You cannot escape from this node because one-ways only lead to here" + }, + "50": { + "title": "almost-junctions", + "severity": "error", + "description": "This node is very close but not connected to way #(\\d+)", + "IDs": ["w"], + "regex": true + }, + "60": { + "title": "depreciated tags", + "severity": "warning", + "description": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead!", + "regex": true + }, + "70": { + "title": "missing tags", + "severity": "error", + "description": "" + }, + "71": { + "title": "", + "severity": "error", + "description": "This way has no tags" + }, + "72": { + "title": "", + "severity": "error", + "description": "This node is not member of any way and does not have any tags" + }, + "73": { + "title": "", + "severity": "error", + "description": "This way has a tracktype tag but no highway tag" + }, + "74": { + "title": "missing tags", + "severity": "error", + "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", + "regex": true + }, + "75": { + "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", + "regex": true + }, + "90": { + "title": "motorways without ref", + "severity": "error", + "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + }, + "100": { + "title": "places of worship without religion", + "severity": "error", + "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag", + "regex": true + }, + "110": { + "title": "point of interest without name", + "severity": "error", + "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag", + "regex": true + }, + "120": { + "title": "ways without nodes", + "severity": "error", + "description": "This way has just one single node" + }, + "130": { + "title": "floating islands", + "severity": "error", + "description": "This way is not connected to the rest of the map" + }, + "150": { + "title": "railway crossing without tag", + "severity": "error", + "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + }, + "160": { + "title": "wrongly used railway tag", + "severity": "error", + "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + }, + "170": { + "title": "FIXME tagged items", + "severity": "error", + "description": "(.*)", + "regex": true + }, + "180": { + "title": "relations without type", + "severity": "error", + "description": "This relation has no type tag which is mandatory for relations" + }, + "190": { + "title": "intersections without junctions", + "severity": "error", + "description": "Finds way crossings on same layer without common node as a junction" + }, + "191": { + "title": "highway-highway", + "severity": "error", + "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", + "IDs": ["", "", "w"], + "regex": true + }, + "192": { + "title": "highway-waterway", + "severity": "error", + "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", + "IDs": ["", "", "w"], + "regex": true + }, + "193": { + "title": "highway-riverbank", + "severity": "error", + "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", + "IDs": ["", "", "w"], + "regex": true + }, + "194": { + "title": "waterway-waterway", + "severity": "error", + "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", + "IDs": ["", "","w"], + "regex": true + }, + "195": { + "title": "cycleway-cycleway", + "severity": "error", + "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", + "IDs": ["", "","w"], + "regex": true + }, + "196": { + "title": "highway-cycleway", + "severity": "error", + "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", + "IDs": ["", "","w"], + "regex": true + }, + "197": { + "title": "cycleway-waterway", + "severity": "error", + "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "198": { + "title": "cycleway-riverbank", + "severity": "error", + "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "200": { + "title": "overlapping ways", + "severity": "error", + "description": "Finds overlapping ways on same layer" + }, + "201": { + "title": "highway-highway", + "severity": "error", + "description": "This (highway) overlaps the (highway) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "202": { + "title": "highway-waterway", + "severity": "error", + "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "203": { + "title": "highway-riverbank", + "severity": "error", + "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "204": { + "title": "waterway-waterway", + "severity": "error", + "description": "This (waterway) overlaps the (waterway) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "205": { + "title": "cycleway-cycleway", + "severity": "error", + "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "206": { + "title": "highway-cycleway", + "severity": "error", + "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "207": { + "title": "cycleway-waterway", + "severity": "error", + "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "208": { + "title": "cycleway-riverbank", + "severity": "error", + "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", + "IDs": ["", "","w"], + "regex": true + }, + "210": { + "title": "loopings", + "severity": "error", + "description": "These errors contain self intersecting ways" + }, + "211": { + "title": "", + "severity": "error", + "description": "This way contains more than one node at least twice\\. Nodes are ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", + "IDs": ["211"], + "regex": true + }, + "212": { + "title": "", + "severity": "error", + "description": "This way has only two different nodes and contains one of them more than once" + }, + "220": { + "title": "misspelled tags", + "severity": "error", + "description": "This (node|way|relation) is tagged '([\\w]+)(:([\\w]+))?=(.+)' where "(\\2|\\3|\\4|\\5)" looks like "([\\w\\s]+)"", + "regex": true + }, + "221": { + "title": "", + "severity": "error", + "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)", + "regex": true + }, + "230": { + "title": "layer conflicts", + "severity": "error", + "description": "Connected ways should be on the same layer. Crossings on intermediate nodes of ways on different layers are obviously wrong. Junctions on end-nodes of ways on different layers are also deprecated, but common practice. So you may ignore this part of the check and switch them off separately. Please note that bridges are set to layer +1, and tunnels to -1, anything else to layer 0 implicitly if no layer tag is present." + }, + "231": { + "title": "mixed layers intersection", + "severity": "error", + "description": "This node is a junction of ways on different layers: ((?:#\\d+\\(-?\\d+\\),?)+)", + "IDs": ["231"], + "regex": true + }, + "232": { + "title": "strange layers", + "severity": "error", + "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange", + "regex": true + }, + "270": { + "title": "motorways connected directly", + "severity": "error", + "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + }, + "280": { + "title": "boundaries", + "severity": "error", + "description": "Administrative Boundaries can be expressed either by tagging ways or by adding them to a relation. They should be closed-loop sequences of ways, they must not self-intersect or split and they must have a name and an admin_level." + }, + "281": { + "title": "missing name", + "severity": "error", + "description": "This boundary has no name" + }, + "282": { + "title": "missing admin level", + "severity": "error", + "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*", + "regex": true + }, + "283": { + "title": "no closed loop", + "severity": "error", + "description": "The boundary of (.+) is not closed-loop", + "regex": true + }, + "284": { + "title": "splitting boundary", + "severity": "error", + "description": "The boundary of (.+) splits here", + "regex": true + }, + "285": { + "title": "admin_level too high", + "severity": "error", + "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", + "regex": true + }, + "290": { + "title": "restrictions", + "severity": "error", + "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat" + }, + "291": { + "title": "missing type", + "severity": "error", + "description": "This turn-restriction has no (?:known )?restriction type", + "regex": true + }, + "292": { + "title": "missing from way", + "severity": "error", + "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)", + "regex": true + }, + "293": { + "title": "missing to way", + "severity": "error", + "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)", + "regex": true + }, + "294": { + "title": "from or to not a way", + "severity": "error", + "description": "From- and To-members of turn restrictions need to be ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)", + "IDs": ["294"], + "regex": true + }, + "295": { + "title": "via is not on the way ends", + "severity": "error", + "description": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)", + "IDs": ["n","w"], + "regex": true + }, + "296": { + "title": "wrong restriction angle", + "severity": "error", + "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?", + "regex": true + }, + "297": { + "title": "wrong direction of to member", + "severity": "error", + "description": "wrong direction of to way (\\d+)", + "IDs": ["w"], + "regex": true + }, + "298": { + "title": "already restricted by oneway", + "severity": "error", + "description": "entry already prohibited by oneway tag on (\\d+)", + "IDs": ["w"], + "regex": true + }, + "300": { + "title": "missing maxspeed", + "severity": "warning", + "description": "missing maxspeed tag" + }, + "310": { + "title": "roundabouts", + "severity": "error", + "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1" + }, + "311": { + "title": "not closed loop", + "severity": "error", + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + }, + "312": { + "title": "wrong direction", + "severity": "error", + "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", + "regex": true + }, + "313": { + "title": "faintly connected", + "severity": "error", + "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three", + "regex": true + }, + "320": { + "title": "*_link connections", + "severity": "error", + "description": "This way is tagged as highway=(\\w+)_link but doesn't have a connection to any other \\1 or \\1_link", + "regex": true + }, + "350": { + "title": "bridge-tags", + "severity": "error", + "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", + "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...", + "regex": true + }, + "360": { + "title": "language unknown", + "severity": "warning", + "description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'", + "regex": true + }, + "370": { + "title": "doubled places", + "severity": "error", + "description": "This node has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and seems to be redundand", + "IDs": ["w"], + "regex": true + }, + "380": { + "title": "non-physical use of sport-tag", + "severity": "error", + "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway", + "regex": true + }, + "390": { + "title": "missing tracktype", + "severity": "warning", + "description": "This track doesn''t have a tracktype" + }, + "400": { + "title": "geometry glitches", + "severity": "error", + "description": "" + }, + "401": { + "title": "missing turn restriction", + "severity": "error", + "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?", + "IDs": ["w", "w", "w", "w"], + "regex": true + }, + "402": { + "title": "impossible angles", + "severity": "error", + "description": "this way bends in a very sharp angle here" + }, + "410": { + "title": "website", + "severity": "error", + "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*" + }, + "411": { + "title": "http error", + "severity": "error", + "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", + "regex": true + }, + "412": { + "title": "domain hijacking", + "severity": "error", + "description": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''", + "regex": true + }, + "413": { + "title": "non-match", + "severity": "error", + "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)", + "regex": true } -} \ No newline at end of file + } +} diff --git a/dist/locales/en.json b/dist/locales/en.json index fa9999527..9d706e4ef 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -806,349 +806,260 @@ "entities": { "node": "Node {id}", "way": "Way {id}", - "relation": "Relation {id}", - "highway": "Highway {id}", - "cycleway": "Cycleway {id}", - "waterway": "Waterway {id}", - "riverbank": "Riverbank {id}" + "relation": "Relation {id}" }, "errorTypes": { - "errors": { - "_30": { - "title": "non-closed_areas", - "description": "This way is tagged with {var1}={var2} and should be closed-loop." - }, - "_40": { - "title": "dead-ended one-ways", - "description": "The first node (id {var1}) of this one-way is not connected to any other way." - }, - "_41": { - "title": "", - "description": "The last node (id {var1}) of this one-way is not connected to any other way." - }, - "_42": { - "title": "", - "description": "This node cannot be reached because one-ways only lead away from here." - }, - "_43": { - "title": "", - "description": "You cannot escape from this node because one-ways only lead to here." - }, - "_50": { - "title": "almost-junctions", - "description": "This node is very close but not connected to way #{var1}." - }, - "_70": { - "title": "missing tags", - "description": "This {var1} has an empty tag: \"{var2}\"." - }, - "_71": { - "title": "way without tags", - "description": "This way has no tags." - }, - "_72": { - "title": "node without tags", - "description": "This node is not member of any way and doesn't have any tags." - }, - "_73": { - "title": "tracktype with no highway tag", - "description": "This way has a \"tracktype\" tag but no \"highway\" tag." - }, - "_74": { - "title": "empty tag", - "description": "This {var1} has an empty tag: \"{var2}\"." - }, - "_75": { - "title": "name without tags", - "description": "This {var1} has a name (\"{var2}\") but no other tags." - }, - "_90": { - "title": "motorways without ref", - "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag." - }, - "_100": { - "title": "places of worship without religion", - "description": "This {var1} is tagged as place of worship and therefore needs a religion tag." - }, - "_110": { - "title": "point of interest without name", - "description": "This node is tagged as {var1} and therefore needs a name tag." - }, - "_120": { - "title": "ways without nodes", - "description": "This way has just one single node." - }, - "_130": { - "title": "floating islands", - "description": "This way is not connected to the rest of the map." - }, - "_150": { - "title": "railway crossing without tag", - "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing." - }, - "_160": { - "title": "wrongly used railway tag", - "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing." - }, - "_170": { - "title": "FIXME tagged items", - "description": "{var1}" - }, - "_180": { - "title": "relations without type", - "description": "This relation has no type tag which is mandatory for relations." - }, - "_190": { - "title": "intersections without junctions", - "description": "Finds way crossings on same layer without common node as a junction." - }, - "_191": { - "title": "highway-highway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." - }, - "_192": { - "title": "highway-waterway", - "description": "This {var1} intersects the {var2} #{var3}." - }, - "_193": { - "title": "highway-riverbank", - "description": "This {var1} intersects the {var2} #{var3}." - }, - "_194": { - "title": "waterway-waterway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." - }, - "_195": { - "title": "cycleway-cycleway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." - }, - "_196": { - "title": "highway-cycleway", - "description": "This {var1} intersects the {var2} #{var3} but there is no junction node." - }, - "_197": { - "title": "cycleway-waterway", - "description": "This {var1} intersects the {var2} #{var3}." - }, - "_198": { - "title": "cycleway-riverbank", - "description": "This {var1} intersects the {var2} #{var3}." - }, - "_200": { - "title": "overlapping ways", - "description": "Finds overlapping ways on same layer." - }, - "_201": { - "title": "highway-highway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_202": { - "title": "highway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_203": { - "title": "highway-riverbank", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_204": { - "title": "waterway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_205": { - "title": "cycleway-cycleway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_206": { - "title": "highway-cycleway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_207": { - "title": "cycleway-waterway", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_208": { - "title": "cycleway-riverbank", - "description": "This {var1} overlaps the {var2} #{var3}." - }, - "_210": { - "title": "loopings", - "description": "These errors contain self intersecting ways." - }, - "_211": { - "title": "", - "description": "This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error." - }, - "_212": { - "title": "", - "description": "This way has only two different nodes and contains one of them more than once." - }, - "_220": { - "title": "misspelled tags", - "description": "This {var1} is tagged \"{var2}\"=\"{var3}\" where \"{var4}\" looks like \"{var5}\"." - }, - "_221": { - "title": "", - "description": "This {var1} has a tag with key \"key\"=\"{var2}\"." - }, - "_230": { - "title": "layer conflicts", - "description": "" - }, - "_231": { - "title": "mixed layers intersection", - "description": "This node is a junction of ways on different layers: {var1}." - }, - "_232": { - "title": "strange layers", - "description": "This {var1} is tagged with layer {var2}. This need not be an error but it looks strange." - }, - "_270": { - "title": "motorways connected directly", - "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." - }, - "_280": { - "title": "boundaries", - "description": "" - }, - "_281": { - "title": "missing name", - "description": "This boundary has no name." - }, - "_282": { - "title": "missing admin level", - "description": "The boundary of {var1} has no valid numeric admin_level. Please do not use admin levels like for example 6;7. Always tag the lowest admin_level of all boundaries." - }, - "_283": { - "title": "no closed loop", - "description": "The boundary of {var1} is not closed-loop." - }, - "_284": { - "title": "splitting boundary", - "description": "The boundary of {var1} splits here." - }, - "_285": { - "title": "admin_level too high", - "description": "This boundary-way has admin_level {var1} but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations." - }, - "_290": { - "title": "restrictions", - "description": "Analyses all relations tagged type=restriction or following variations type=restriction:hgv type=restriction:caravan type=restriction:motorcar type=restriction:bus type=restriction:agricultural type=restriction:motorcycle type=restriction:bicycle and type=restriction:hazmat." - }, - "_291": { - "title": "missing type", - "description": "This turn-restriction has no known restriction type." - }, - "_292": { - "title": "missing from way", - "description": "A turn-restriction needs exactly one \"from\" member. This one has {var1}." - }, - "_293": { - "title": "missing to way", - "description": "A turn-restriction needs exactly one \"to\" member. This one has {var1}." - }, - "_294": { - "title": "from or to not a way", - "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {var1}." - }, - "_295": { - "title": "via is not on the way ends", - "description": "\"via\" (node #{var1}) is not the first or the last member of \"from\" (way #{var2})." - }, - "_296": { - "title": "wrong restriction angle", - "description": "restriction type is {var1} but angle is {var2} degrees. Maybe the restriction type is not appropriate?" - }, - "_297": { - "title": "wrong direction of to member", - "description": "wrong direction of to way {var1}." - }, - "_298": { - "title": "already restricted by oneway", - "description": "entry already prohibited by oneway tag on {var1}." - }, - "_310": { - "title": "roundabouts", - "description": "Analyses ways with tag junction=roundabout. More then one way can form a roundabout. It supports tag oneway=-1." - }, - "_311": { - "title": "not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." - }, - "_312": { - "title": "wrong direction", - "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around" - }, - "_313": { - "title": "faintly connected", - "description": "This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more." - }, - "_320": { - "title": "*_link connections", - "description": "This way is tagged as highway={var1}_link but doesn't have a connection to any other {var1} or {var1}_link." - }, - "_350": { - "title": "bridge-tags", - "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." - }, - "_370": { - "title": "doubled places", - "description": "This node has tags in common with the surrounding way #{var1} {var2}and seems to be redundant." - }, - "_380": { - "title": "non-physical use of sport-tag", - "description": "This way is tagged \"sport\"=\"{var1}\" but has no physical tag like e.g. leisure, building, amenity or highway." - }, - "_400": { - "title": "geometry glitches", - "description": "" - }, - "_401": { - "title": "missing turn restriction", - "description": "ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.", - "addition": " from way {var3} to {var4}." - }, - "_402": { - "title": "impossible angles", - "description": "this way bends in a very sharp angle here." - }, - "_410": { - "title": "website", - "description": "Web pages are analyzed. Web page is defined by any of the following tags website=* url=* website:mobile=* contact:website=* contact:url=* image=* source:website=* or source:url=*." - }, - "_411": { - "title": "http error", - "description": "The URL cannot be opened (HTTP status code {var2})." - }, - "_412": { - "title": "domain hijacking", - "description": "Possible domain squatting: The URL has Suspicious text: \"{var2}\"." - }, - "_413": { - "title": "non-match", - "description": "Content of the URL did not contain these keywords: ({var2})." - } + "20": { + "title": "Multiple nodes on the same spot", + "description": "There is more than one node in this spot. Node IDs: {0}." }, - "warnings": { - "_20": { - "title": "multiple nodes on the same spot", - "description": "There is more than one node in this spot. Offending node IDs: {var1}." - }, - "_60": { - "title": "depreciated tags", - "description": "This {var1} uses deprecated tag \"{var2}\"=\"{var3}\". Please use {var4} instead!" - }, - "_300": { - "title": "missing maxspeed", - "description": "missing maxspeed tag." - }, - "_360": { - "title": "language unknown", - "description": "It would be nice if this {var1} had an additional tag \"name:XX\"=\"{var2}\" where XX shows the language of its name \"{var2}\"." - }, - "_390": { - "title": "missing tracktype", - "description": "This track doesn't have a tracktype." - } + "30": { + "title": "Non-closed areas", + "description": "This way is tagged with \"{0}={1}\" and should be a closed loop." + }, + "40": { + "title": "Impossible oneways", + "description": "The first node ({0}) of this oneway is not connected to any other way." + }, + "41": { + "description": "The last node ({0}) of this oneway is not connected to any other way." + }, + "42": { + "description": "You cannot reach this node because all ways leading from it are oneway." + }, + "43": { + "description": "You cannot escape from this node because all ways leading to it are oneway." + }, + "50": { + "title": "Almost-junctions", + "description": "This node is very close but not connected to way {0}." + }, + "60": { + "title": "Deprecated tags", + "description": "This {0} uses deprecated tag \"{1}={2}\". Please use \"{3}\" instead." + }, + "70": { + "title": "Missing tags", + "description": "This {0} has an empty tag: \"{1}\"." + }, + "71": { + "description": "This way has no tags." + }, + "72": { + "description": "This node is not member of any way and doesn't have any tags." + }, + "73": { + "description": "This way has a \"tracktype\" tag but no \"highway\" tag." + }, + "74": { + "description": "This {0} has an empty tag: \"{1}\"." + }, + "75": { + "description": "This {0} has a name \"{1}\" but no other tags." + }, + "90": { + "title": "Motorway without ref tag", + "description": "This way is tagged as motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag." + }, + "100": { + "title": "Place of worship without religion", + "description": "This {0} is tagged as place of worship and therefore needs a \"religion\" tag." + }, + "110": { + "title": "Point of interest without name", + "description": "This node is tagged as {0} and therefore needs a name tag." + }, + "120": { + "title": "Way without nodes", + "description": "This way has just one single node." + }, + "130": { + "title": "Disconnected way", + "description": "This way is not connected to the rest of the map." + }, + "150": { + "title": "Railway crossing without tag", + "description": "This crossing of a highway and a railway needs to be tagged as \"railway=crossing\" or \"railway=level_crossing\"." + }, + "160": { + "title": "Wrongly used railway tag", + "description": "There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing." + }, + "170": { + "title": "FIXME tagged items", + "description": "{0}" + }, + "180": { + "title": "Relation without type", + "description": "This relation is missing a \"type\" tag." + }, + "190": { + "title": "Intersection without junctions", + "description": "This {0} intersects the {1} {2} but there is no junction node." + }, + "200": { + "title": "Overlapping ways", + "description": "This {0} overlaps the {1} {2}." + }, + "210": { + "title": "Self-intersecting ways", + "description": "These errors contain self intersecting ways." + }, + "211": { + "description": "This way contains more than one node multiple times. Nodes are {0}. This may or may not be an error." + }, + "212": { + "description": "This way has only two different nodes and contains one of them more than once." + }, + "220": { + "title": "Misspelled tag", + "description": "This {0} is tagged \"{1}={2}\" where \"{3}\" looks like \"{4}\"." + }, + "221": { + "description": "This {0} has a tag with key \"{1}\"." + }, + "230": { + "title": "Layer Conflict", + "description": "This node is a junction of ways on different layers." + }, + "231": { + "description": "This node is a junction of ways on different layers: {0}." + }, + "232": { + "description": "This {0} is tagged with \"layer={1}\". This need not be an error but it looks strange." + }, + "270": { + "title": "Unusual motorway connection", + "description": "This node is a junction of a motorway and a highway other than \"motorway\", \"motorway_link\", \"trunk\", \"rest_area\", or \"construction\". Connection to \"service\" or \"unclassified\" is only valid if it has \"access=no/private\", or it leads to a motorway service area, or if it is a \"service=parking_aisle\"." + }, + "280": { + "title": "Boundary issue", + "description": "There is an unspecified issue with this boundary." + }, + "281": { + "title": "Boundary missing name", + "description": "This boundary has no name." + }, + "282": { + "title": "Boundary missing admin level", + "description": "The boundary of {0} has no valid numeric admin_level. Please do not mix admin levels (e.g. \"6;7\"). Always tag the lowest admin_level of all boundaries." + }, + "283": { + "title": "Boundary not a closed loop", + "description": "The boundary of {0} is not a closed loop." + }, + "284": { + "title": "Boundary is split", + "description": "The boundary of {0} splits here." + }, + "285": { + "title": "Boundary admin_level too high", + "description": "This boundary way has \"admin_level={0}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations." + }, + "290": { + "title": "Restriction issue", + "description": "There is an unspecified issue with this restriction." + }, + "291": { + "title": "Restriction missing type", + "description": "This turn restriction has no known restriction type." + }, + "292": { + "title": "Restriction missing \"from\" way", + "description": "A turn restriction needs exactly one \"from\" member. This one has {0}." + }, + "293": { + "title": "Restriction missing \"to\" way", + "description": "A turn restriction needs exactly one \"to\" member. This one has {0}." + }, + "294": { + "title": "Restriction \"from\" or \"to\" is not a way", + "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {0}." + }, + "295": { + "title": "Restriction \"via\" is not on the way ends", + "description": "\"via\" (node #{0}) is not the first or the last member of \"from\" (way #{1})." + }, + "296": { + "title": "Wrong restriction angle", + "description": "Restriction type is \"{0}\" but angle is {1} degrees. Maybe the restriction type is not appropriate?" + }, + "297": { + "title": "Wrong direction of to member", + "description": "Wrong direction of \"to\" way {0}." + }, + "298": { + "title": "Redundant restriction - oneway", + "description": "Entry already prohibited by \"oneway\" tag on {0}." + }, + "300": { + "title": "Missing maxspeed", + "description": "Missing maxspeed tag." + }, + "310": { + "title": "Roundabout issue", + "description": "There is an unspecified issue with this roundabout." + }, + "311": { + "title": "Roundabout not closed loop", + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." + }, + "312": { + "title": "Roundabout wrong direction", + "description": "If this {0} is in a country with {1}-hand traffic then its orientation goes the wrong way around" + }, + "313": { + "title": "Roundabout weakly connected", + "description": "This roundabout has only {0} other road(s) connected. Roundabouts typically have 3 or more." + }, + "320": { + "title": "Improper link connection", + "description": "This way is tagged as \"highway={0}_link\" but doesn't have a connection to any other \"{1}\" or \"{2}_link\"." + }, + "350": { + "title": "Improper bridge tags", + "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {0}." + }, + "360": { + "title": "Language unknown", + "description": "It would be nice if this {0} had an additional tag \"name:XX\"=\"{1}\" where XX shows the language of its name \"{1}\"." + }, + "370": { + "title": "Doubled places", + "description": "This node has tags in common with the surrounding way #{0} {1} and seems to be redundant." + }, + "380": { + "title": "Non-physical use of sport tag", + "description": "This way is tagged \"sport={0}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." + }, + "390": { + "title": "Missing tracktype", + "description": "This track doesn't have a \"tracktype\" tag." + }, + "400": { + "title": "Geometry issue", + "description": "There is an unspecified issue with the geometry here." + }, + "401": { + "title": "Missing turn restriction", + "description": "Ways {0} and {1} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {2} to {3}." + }, + "402": { + "title": "Impossible angles", + "description": "This way bends in a very sharp angle here." + }, + "410": { + "title": "Website issue", + "description": "There is an unspecified issue with a contact website or URL." + }, + "411": { + "description": "The URL cannot be opened (HTTP status code {0})." + }, + "412": { + "description": "Possible domain squatting: The URL has suspicious text: \"{0}\"." + }, + "413": { + "description": "Possible non-match. Content of the URL did not contain these keywords: ({0})." } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 1e74627e7..b697dc51d 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -23,7 +23,13 @@ var dispatch = d3_dispatch('loaded'); var _krCache; var _krZoom = 14; var apibase = 'https://www.keepright.at/'; -var defaultRuleset = [0,30,40,50,70,90,100,110,120,130,150,160,170,180,191,192,193,194,195,196,197,198,201,202,203,204,205,206,207,208,210,220,231,232,270,281,282,283,284,285,291,292,293,294,295,296,297,298,311,312,313,320,350,370,380,401,402,411,412,413]; +var defaultRuleset = [ + 0, 30, 40, 50, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, + 191, 192, 193, 194, 195, 196, 197, 198, 201, 202, 203, 204, 205, + 206, 207, 208, 210, 220, 231, 232, 270, 281, 282, 283, 284, 285, + 291, 292 ,293, 294, 295, 296, 297, 298, 311, 312, 313, 320, 350, + 370, 380, 401, 402, 411, 412, 413 +]; function abortRequest(i) { @@ -62,45 +68,34 @@ function updateRtree(item, replace) { } -function tokenReplacements(datum) { - if (!(datum instanceof krError)) return; +function tokenReplacements(d) { + if (!(d instanceof krError)) return; var replacements = {}; var html_re = new RegExp(/<\/[a-z][\s\S]*>/); - var commonEntities = ['node', 'way', 'relation', 'highway', 'cycleway', 'waterway', 'riverbank']; - var errorType; - var errorTemplate; - var errorDescription; - var errorRegex; - var errorMatch; - - // find the matching template from the error schema - errorType = '_' + datum.error_type; - errorTemplate = errorTypes.errors[errorType] || errorTypes.warnings[errorType]; + var errorTemplate = errorTypes[d.error_type]; if (!errorTemplate) return; // some descriptions are just fixed text - if (!('regex' in errorTemplate)) return; + if (!errorTemplate.regex) return; // regex pattern should match description with variable details captured as groups - errorDescription = datum.description; - errorRegex = new RegExp(errorTemplate.description); - errorMatch = errorRegex.exec(errorDescription); + var errorDescription = d.description; + var errorRegex = new RegExp(errorTemplate.description); + var errorMatch = errorRegex.exec(errorDescription); if (!errorMatch) { // TODO: Remove, for regex dev testing - console.log('Unmatched:', errorType, errorDescription, errorRegex); + console.log('Unmatched:', d.error_type, d.description, errorRegex); return; } - errorMatch.forEach(function(group, index) { + for (var i = 1; i < errorMatch.length; i++) { // skip first + var group = errorMatch[i]; var idType; - // index 0 is the whole match, skip it - if (!index) return; - // link IDs if present in the group - idType = 'IDs' in errorTemplate ? errorTemplate.IDs[index-1] : ''; + idType = 'IDs' in errorTemplate ? errorTemplate.IDs[i-1] : ''; if (idType && group) { group = parseError(group, idType); } else if (html_re.test(group)) { @@ -108,13 +103,8 @@ function tokenReplacements(datum) { group = '\\' + group + '\\'; } - // translate common words (e.g. node, way, relation) - if (commonEntities.includes(group)) { - group = t('QA.keepRight.entities.' + group); - } - - replacements['var' + index] = group; - }); + replacements[i-1] = group; + } return replacements; } @@ -122,7 +112,9 @@ function tokenReplacements(datum) { function parseError(group, idType) { - function fillPlaceholder(d) { return '' + d + ''; } + function fillPlaceholder(d) { + return '' + d + ''; + } // arbitrary node list of form: #ID, #ID, #ID... function parseError211(list) { diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index f5edc8a23..d4475663f 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -5,43 +5,22 @@ import { t } from '../util/locale'; export function uiKeepRightDetails(context) { + var stringBase = 'QA.keepRight.errorTypes.'; var _error; - var _template; - var _templateErrorType; - var _category; - var _categoryElements; - var _parent_error_type; - var _titleBase; - - - function initDetails() { - _parent_error_type = ''; - if (errorTypes.errors['_' + _error.error_type]) { - _templateErrorType = '_' + _error.error_type; - _template = errorTypes.errors[_templateErrorType]; - _category = 'errors'; - } else if (errorTypes.warnings[_templateErrorType]) { - _template = errorTypes.warnings[_templateErrorType]; - _category = 'warnings'; - } else { return; } - - // if there is a parent, save it's error type - _categoryElements = errorTypes[_category]; - var base_error_type = (Math.round(_error.error_type / 10) * 10).toString(); - if ((_categoryElements['_' + base_error_type]) && (base_error_type !== _error.error_type) ) { - _parent_error_type = '_' + base_error_type; - } - - _titleBase = 'QA.keepRight.errorTypes.' + _category + '.'; - - } - function keepRightDetails(selection) { - if (!_error || !_error.error_type) return; + if (!_error) return; - initDetails(); - if (!_template) return; + var errorType = _error.error_type; + var template = errorTypes[errorType]; + if (!template) return; + + // if there is a parent, save its error type e.g.: + // Error 191 = "highway-highway" + // Error 190 = "intersections without junctions" (parent) + var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); + var parentTemplate = errorTypes[parentErrorType]; + if (!parentTemplate) return; var details = selection.selectAll('.kr_error-details') @@ -57,30 +36,24 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details kr_error-details-container'); - - // title - var title = detailsEnter + var titleEnter = detailsEnter .append('div') .attr('class', 'kr_error-details-title'); - title.append('h4') + titleEnter + .append('h4') .text(function() { return t('QA.keepRight.detail_title'); }); - title.append('div') + titleEnter + .append('div') .text(function() { - var title = ''; - - // if this is a subtype, append it's parent title - if (_parent_error_type) { - title = t(_titleBase + _parent_error_type + '.title') + ': \n'; + var result; + try { + result = t(stringBase + errorType + '.title'); + } catch (e) { + result = t(stringBase + parentErrorType + '.title'); } - - // append title - if (_error.error_type) { - title += t(_titleBase + _templateErrorType + '.title'); - } - - return title; + return result; }); @@ -97,17 +70,23 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details-description-text') .html(function(d) { - return t(_titleBase + _templateErrorType + '.description', d.replacements); + var result; + try { + result = t(stringBase + errorType + '.description', d.replacements); + } catch (e) { + result = t(stringBase + parentErrorType + '.description'); + } + return result; }); description.selectAll('.kr_error_description-id') .on('click', function() { clickLink(context, this.text); }); - function clickLink(context, id) { + function clickLink(context, entityID) { d3_event.preventDefault(); context.layers().layer('osm').enabled(true); - context.zoomToEntity(id); + context.zoomToEntity(entityID); } } From e06ba3d1d765e280a894e968d33bc4c8c5b99a68 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 22 Dec 2018 12:27:49 -0500 Subject: [PATCH 047/114] Move racetracks to presets/leisure/track - add horse_racing.json (closes #5620) --- data/presets.yaml | 15 ++-- data/presets/presets.json | 3 +- .../presets/leisure/track/horse_racing.json | 25 +++++++ .../running.json} | 0 data/taginfo.json | 21 ++++-- dist/locales/en.json | 74 ++++++++++++++++--- 6 files changed, 115 insertions(+), 23 deletions(-) create mode 100644 data/presets/presets/leisure/track/horse_racing.json rename data/presets/presets/leisure/{running_track.json => track/running.json} (100%) diff --git a/data/presets.yaml b/data/presets.yaml index f6b2138d0..6a033d252 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -4674,11 +4674,6 @@ en: # leisure=resort name: Resort terms: '' - leisure/running_track: - # 'leisure=track, sport=running' - name: Racetrack (Running) - # 'terms: race*,running,sprint,track' - terms: '' leisure/sauna: # leisure=sauna name: Sauna @@ -4711,6 +4706,16 @@ en: name: Racetrack (Non-Motorsport) # 'terms: cycle,dog,greyhound,horse,race*,track' terms: '' + leisure/track/horse_racing: + # 'leisure=track, sport=horse_racing' + name: Racetrack (Horse Racing) + # 'terms: race*,horse,track' + terms: '' + leisure/track/running: + # 'leisure=track, sport=running' + name: Racetrack (Running) + # 'terms: race*,running,sprint,track' + terms: '' leisure/water_park: # leisure=water_park name: Water Park diff --git a/data/presets/presets.json b/data/presets/presets.json index a46eff7b5..166265ca9 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -578,7 +578,6 @@ "leisure/pitch/volleyball": {"icon": "maki-volleyball", "fields": ["name", "surface", "lit"], "geometry": ["point", "area"], "tags": {"leisure": "pitch", "sport": "volleyball"}, "reference": {"key": "sport", "value": "volleyball"}, "terms": [], "name": "Volleyball Court"}, "leisure/playground": {"icon": "maki-playground", "fields": ["name", "operator", "surface", "playground/max_age", "playground/min_age", "access_simple"], "geometry": ["point", "area"], "terms": ["jungle gym", "play area"], "tags": {"leisure": "playground"}, "name": "Playground"}, "leisure/resort": {"icon": "maki-lodging", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "tags": {"leisure": "resort"}, "name": "Resort"}, - "leisure/running_track": {"icon": "maki-pitch", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "running"}, "terms": ["race*", "running", "sprint", "track"], "name": "Racetrack (Running)"}, "leisure/sauna": {"icon": "fas-thermometer-three-quarters", "fields": ["name", "operator", "address", "opening_hours", "access_simple", "fee"], "geometry": ["point", "area"], "tags": {"leisure": "sauna"}, "name": "Sauna"}, "leisure/slipway": {"icon": "maki-slipway", "fields": ["name", "access_simple", "fee"], "geometry": ["point", "vertex", "line"], "terms": ["boat launch", "boat ramp", "boat landing"], "tags": {"leisure": "slipway"}, "name": "Slipway"}, "leisure/sports_centre": {"icon": "maki-pitch", "fields": ["name", "sport", "building", "address", "opening_hours"], "geometry": ["point", "area"], "tags": {"leisure": "sports_centre"}, "terms": [], "name": "Sports Center / Complex"}, @@ -586,6 +585,8 @@ "leisure/stadium": {"icon": "maki-pitch", "fields": ["name", "sport", "address"], "geometry": ["point", "area"], "tags": {"leisure": "stadium"}, "name": "Stadium"}, "leisure/swimming_pool": {"icon": "maki-swimming", "fields": ["name", "access_simple", "operator", "address", "lit", "location_pool", "length", "swimming_pool"], "geometry": ["point", "area"], "terms": ["dive", "water", "aquatics"], "tags": {"leisure": "swimming_pool"}, "name": "Swimming Pool"}, "leisure/track": {"icon": "iD-highway-road", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track"}, "terms": ["cycle", "dog", "greyhound", "horse", "race*", "track"], "name": "Racetrack (Non-Motorsport)"}, + "leisure/track/horse_racing": {"icon": "maki-horse-riding", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "horse_racing"}, "terms": ["race*", "horse", "track"], "name": "Racetrack (Horse Racing)"}, + "leisure/track/running": {"icon": "maki-pitch", "fields": ["surface", "sport_racing_nonmotor", "lit", "width", "lanes"], "geometry": ["point", "line", "area"], "tags": {"leisure": "track", "sport": "running"}, "terms": ["race*", "running", "sprint", "track"], "name": "Racetrack (Running)"}, "leisure/water_park": {"icon": "maki-swimming", "fields": ["name", "operator", "address"], "geometry": ["point", "area"], "terms": ["swim", "pool", "dive"], "tags": {"leisure": "water_park"}, "name": "Water Park"}, "line": {"fields": ["name"], "geometry": ["line"], "tags": {}, "name": "Line", "matchScore": 0.1}, "man_made": {"icon": "temaki-storage_tank", "fields": ["name", "man_made"], "geometry": ["point", "vertex", "line", "area"], "tags": {"man_made": "*"}, "name": "Man Made"}, diff --git a/data/presets/presets/leisure/track/horse_racing.json b/data/presets/presets/leisure/track/horse_racing.json new file mode 100644 index 000000000..6801993c8 --- /dev/null +++ b/data/presets/presets/leisure/track/horse_racing.json @@ -0,0 +1,25 @@ +{ + "icon": "maki-horse-riding", + "fields": [ + "surface", + "sport_racing_nonmotor", + "lit", + "width", + "lanes" + ], + "geometry": [ + "point", + "line", + "area" + ], + "tags": { + "leisure": "track", + "sport": "horse_racing" + }, + "terms": [ + "race*", + "horse", + "track" + ], + "name": "Racetrack (Horse Racing)" +} diff --git a/data/presets/presets/leisure/running_track.json b/data/presets/presets/leisure/track/running.json similarity index 100% rename from data/presets/presets/leisure/running_track.json rename to data/presets/presets/leisure/track/running.json diff --git a/data/taginfo.json b/data/taginfo.json index c76bf847f..22e6bf284 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -3876,13 +3876,6 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/lodging-15.svg?sanitize=true" }, - { - "key": "sport", - "value": "running", - "description": "🄿 Racetrack (Running)", - "object_types": ["node", "way", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true" - }, { "key": "leisure", "value": "sauna", @@ -3932,6 +3925,20 @@ "object_types": ["node", "way", "area"], "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/iD-sprite/presets/highway-road.svg?sanitize=true" }, + { + "key": "sport", + "value": "horse_racing", + "description": "🄿 Racetrack (Horse Racing)", + "object_types": ["node", "way", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/horse-riding-15.svg?sanitize=true" + }, + { + "key": "sport", + "value": "running", + "description": "🄿 Racetrack (Running)", + "object_types": ["node", "way", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true" + }, { "key": "leisure", "value": "water_park", diff --git a/dist/locales/en.json b/dist/locales/en.json index da3671d75..193b3ff19 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -5531,10 +5531,6 @@ "name": "Resort", "terms": "" }, - "leisure/running_track": { - "name": "Racetrack (Running)", - "terms": "race*,running,sprint,track" - }, "leisure/sauna": { "name": "Sauna", "terms": "" @@ -5563,6 +5559,14 @@ "name": "Racetrack (Non-Motorsport)", "terms": "cycle,dog,greyhound,horse,race*,track" }, + "leisure/track/horse_racing": { + "name": "Racetrack (Horse Racing)", + "terms": "race*,horse,track" + }, + "leisure/track/running": { + "name": "Racetrack (Running)", + "terms": "race*,running,sprint,track" + }, "leisure/water_park": { "name": "Water Park", "terms": "swim,pool,dive" @@ -7568,9 +7572,6 @@ "SPW_PICC": { "name": "SPW(allonie) PICC numerical imagery" }, - "US-TIGER-Roads-2012": { - "name": "TIGER Roads 2012" - }, "US-TIGER-Roads-2014": { "description": "At zoom level 16+, public domain map data from the US Census. At lower zooms, only changes since 2006 minus changes already incorporated into OpenStreetMap", "name": "TIGER Roads 2014" @@ -7579,6 +7580,10 @@ "description": "Yellow = Public domain map data from the US Census. Red = Data not found in OpenStreetMap", "name": "TIGER Roads 2017" }, + "US-TIGER-Roads-2018": { + "description": "Yellow = Public domain map data from the US Census. Red = Data not found in OpenStreetMap", + "name": "TIGER Roads 2018" + }, "US_Forest_Service_roads_overlay": { "description": "Highway: Green casing = unclassified. Brown casing = track. Surface: gravel = light brown fill, Asphalt = black, paved = gray, ground =white, concrete = blue, grass = green. Seasonal = white bars", "name": "U.S. Forest Roads Overlay" @@ -7595,6 +7600,12 @@ }, "name": "UrbIS-Ortho 2017" }, + "UrbISOrtho2018": { + "attribution": { + "text": "Realized by means of Brussels UrbIS®© - Distribution & Copyright CIRB" + }, + "name": "UrbIS-Ortho 2018" + }, "UrbisAdmFR": { "attribution": { "text": "Realized by means of Brussels UrbIS®© - Distribution & Copyright CIRB" @@ -7679,11 +7690,33 @@ "description": "Japan GSI Standard Map. Widely covered.", "name": "Japan GSI Standard Map" }, - "hike_n_bike": { + "helsingborg-orto": { "attribution": { - "text": "© OpenStreetMap contributors" + "text": "© Helsingborg municipality" }, - "name": "Hike & Bike" + "description": "Orthophotos from the municipality of Helsingborg 2016, public domain", + "name": "Helsingborg Orthophoto" + }, + "kalmar-orto-2014": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for the north coast of the municipality of Kalmar 2014", + "name": "Kalmar North Orthophoto 2014" + }, + "kalmar-orto-2016": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for the south coast of the municipality of Kalmar 2016", + "name": "Kalmar South Orthophoto 2016" + }, + "kalmar-orto-2018": { + "attribution": { + "text": "© Kalmar municipality" + }, + "description": "Orthophotos for urban areas of the municipality of Kalmar 2018", + "name": "Kalmar Urban Orthophoto 2018" }, "kelkkareitit": { "attribution": { @@ -7706,6 +7739,20 @@ "description": "Mosaic of Swedish orthophotos from the period 1970–1980. Is under construction.", "name": "Lantmäteriet Historic Orthophoto 1975" }, + "lantmateriet-topowebb": { + "attribution": { + "text": "© Lantmäteriet, CC0" + }, + "description": "Topographic map of Sweden 1:50 000", + "name": "Lantmäteriet Topographic Map" + }, + "linkoping-orto": { + "attribution": { + "text": "© Linköping municipality" + }, + "description": "Orthophotos from the municipality of Linköping 2010, open data", + "name": "Linköping Orthophoto" + }, "mapbox_locator_overlay": { "attribution": { "text": "Terms & Feedback" @@ -7770,6 +7817,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 6d327b3effb256afea8a0bea366928f365de1b3b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 22 Dec 2018 13:02:46 -0500 Subject: [PATCH 048/114] Upgrade FontAwesome icons, add more icons for winter sports --- build_data.js | 7 ++++- data/presets.yaml | 5 ++++ data/presets/presets.json | 17 ++++++----- data/presets/presets/leisure/ice_rink.json | 2 +- data/presets/presets/piste/downhill.json | 2 +- .../presets/piste/downhill/halfpipe.json | 29 +++++++++++++++++++ data/presets/presets/piste/hike.json | 2 +- data/presets/presets/piste/ice_skate.json | 2 +- data/presets/presets/piste/nordic.json | 2 +- data/presets/presets/piste/piste.json | 2 +- data/presets/presets/piste/skitour.json | 2 +- data/presets/presets/piste/sleigh.json | 2 +- data/taginfo.json | 23 ++++++++++----- dist/locales/en.json | 4 +++ package.json | 8 ++--- svg/fontawesome/fas-skating.svg | 1 + svg/fontawesome/fas-skiing-nordic.svg | 1 + svg/fontawesome/fas-skiing.svg | 1 + svg/fontawesome/fas-sleigh.svg | 1 + svg/fontawesome/fas-snowboarding.svg | 1 + 20 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 data/presets/presets/piste/downhill/halfpipe.json create mode 100644 svg/fontawesome/fas-skating.svg create mode 100644 svg/fontawesome/fas-skiing-nordic.svg create mode 100644 svg/fontawesome/fas-skiing.svg create mode 100644 svg/fontawesome/fas-sleigh.svg create mode 100644 svg/fontawesome/fas-snowboarding.svg diff --git a/build_data.js b/build_data.js index 512c497c2..7d80cac3a 100644 --- a/build_data.js +++ b/build_data.js @@ -533,7 +533,12 @@ function writeFaIcons(faIcons) { var prefix = key.substring(0, 3); // `fas`, `far`, `fab` var name = key.substring(4); var def = fontawesome.findIconDefinition({ prefix: prefix, iconName: name }); - writeFileProm('svg/fontawesome/' + key + '.svg', fontawesome.icon(def).html); + try { + writeFileProm('svg/fontawesome/' + key + '.svg', fontawesome.icon(def).html); + } catch (error) { + console.error('Error: No FontAwesome icon for ' + key); + throw(error); + } } } diff --git a/data/presets.yaml b/data/presets.yaml index 6a033d252..d35eede43 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -5254,6 +5254,11 @@ en: name: Downhill Piste/Ski Run # 'terms: ski,alpine,snowboard,downhill,piste' terms: '' + piste/downhill/halfpipe: + # 'piste:type=downhill, man_made=piste:halfpipe' + name: Halfpipe + # 'terms: ski,alpine,halfpipe,half pipe,snowboard,downhill,piste' + terms: '' piste/hike: # 'piste:type=hike' name: Snowshoeing or Winter Hiking Trail diff --git a/data/presets/presets.json b/data/presets/presets.json index 166265ca9..5e198c0a8 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -549,7 +549,7 @@ "leisure/golf_course": {"icon": "maki-golf", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "terms": ["links"], "tags": {"leisure": "golf_course"}, "name": "Golf Course"}, "leisure/hackerspace": {"icon": "maki-commercial", "fields": ["name", "address", "building_area", "opening_hours", "website"], "geometry": ["point", "area"], "terms": ["makerspace", "hackspace", "hacklab"], "tags": {"leisure": "hackerspace"}, "name": "Hackerspace"}, "leisure/horse_riding": {"icon": "maki-horse-riding", "fields": ["name", "access_simple", "operator", "address", "building"], "geometry": ["point", "area"], "terms": ["equestrian", "stable"], "tags": {"leisure": "horse_riding"}, "name": "Horseback Riding Facility"}, - "leisure/ice_rink": {"icon": "maki-pitch", "fields": ["name", "seasonal", "sport_ice", "operator", "address", "building", "opening_hours"], "geometry": ["point", "area"], "terms": ["hockey", "skating", "curling"], "tags": {"leisure": "ice_rink"}, "name": "Ice Rink"}, + "leisure/ice_rink": {"icon": "fas-skating", "fields": ["name", "seasonal", "sport_ice", "operator", "address", "building", "opening_hours"], "geometry": ["point", "area"], "terms": ["hockey", "skating", "curling"], "tags": {"leisure": "ice_rink"}, "name": "Ice Rink"}, "leisure/marina": {"icon": "maki-harbor", "fields": ["name", "operator", "address", "capacity", "fee", "sanitary_dump_station", "power_supply", "internet_access", "internet_access/fee"], "moreFields": ["internet_access/ssid"], "geometry": ["point", "vertex", "area"], "terms": ["boat"], "tags": {"leisure": "marina"}, "name": "Marina"}, "leisure/miniature_golf": {"icon": "maki-golf", "fields": ["name", "operator", "address", "opening_hours"], "geometry": ["point", "area"], "terms": ["crazy golf", "mini golf", "putt-putt"], "tags": {"leisure": "miniature_golf"}, "name": "Miniature Golf"}, "leisure/nature_reserve": {"icon": "maki-park", "geometry": ["point", "area"], "fields": ["name"], "tags": {"leisure": "nature_reserve"}, "terms": ["protected", "wildlife"], "name": "Nature Reserve"}, @@ -704,14 +704,15 @@ "office/telecommunication": {"icon": "maki-telephone", "fields": ["name", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "tags": {"office": "telecommunication"}, "terms": ["communication", "internet", "phone", "voice"], "name": "Telecom Office"}, "office/therapist": {"icon": "maki-suitcase", "fields": ["name", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "tags": {"office": "therapist"}, "terms": ["therapy"], "name": "Therapist Office"}, "office/water_utility": {"icon": "maki-suitcase", "fields": ["name", "address", "building_area", "opening_hours", "operator"], "geometry": ["point", "area"], "tags": {"office": "water_utility"}, "terms": ["water board", "utility"], "name": "Water Utility Office"}, - "piste/downhill": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill"}, "name": "Downhill Piste/Ski Run"}, - "piste/hike": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming_hike", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["hike", "winter hiking", "snowshoe", "snowshoeing", "piste", "ski"], "tags": {"piste:type": "hike"}, "name": "Snowshoeing or Winter Hiking Trail"}, - "piste/ice_skate": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "sport_ice", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ice", "skating", "ski", "piste"], "tags": {"piste:type": "ice_skate"}, "name": "Ice Skating Piste"}, - "piste/nordic": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_nordic", "piste/grooming_nordic", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "skating", "piste"], "tags": {"piste:type": "nordic"}, "name": "Nordic or Crosscountry Piste/Ski Trail"}, - "piste/piste": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "downhill", "alpine", "snowboard", "skitour", "ski touring", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse", "winter hiking", "snowshoe", "snowshoeing", "ice", "skating"], "tags": {"piste:type": "*"}, "name": "Winter Sport Trails"}, - "piste/skitour": {"icon": "maki-skiing", "fields": ["name", "piste/type", "piste/difficulty_skitour", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "skitour", "crosscountry", "ski touring", "piste"], "tags": {"piste:type": "skitour"}, "name": "Ski Touring Trail"}, + "piste/downhill": {"icon": "fas-skiing", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill"}, "name": "Downhill Piste/Ski Run"}, + "piste/downhill/halfpipe": {"icon": "fas-snowboarding", "fields": ["name", "piste/type", "piste/difficulty_downhill", "piste/grooming_downhill", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "alpine", "halfpipe", "half pipe", "snowboard", "downhill", "piste"], "tags": {"piste:type": "downhill", "man_made": "piste:halfpipe"}, "name": "Halfpipe"}, + "piste/hike": {"icon": "fas-skating", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming_hike", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["hike", "winter hiking", "snowshoe", "snowshoeing", "piste", "ski"], "tags": {"piste:type": "hike"}, "name": "Snowshoeing or Winter Hiking Trail"}, + "piste/ice_skate": {"icon": "fas-skating", "fields": ["name", "piste/type", "sport_ice", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ice", "skating", "ski", "piste"], "tags": {"piste:type": "ice_skate"}, "name": "Ice Skating Piste"}, + "piste/nordic": {"icon": "fas-skiing-nordic", "fields": ["name", "piste/type", "piste/difficulty_nordic", "piste/grooming_nordic", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "skating", "piste"], "tags": {"piste:type": "nordic"}, "name": "Nordic or Crosscountry Piste/Ski Trail"}, + "piste/piste": {"icon": "fas-skiing", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "nordic", "crosscountry", "downhill", "alpine", "snowboard", "skitour", "ski touring", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse", "winter hiking", "snowshoe", "snowshoeing", "ice", "skating"], "tags": {"piste:type": "*"}, "name": "Winter Sport Trails"}, + "piste/skitour": {"icon": "fas-skiing-nordic", "fields": ["name", "piste/type", "piste/difficulty_skitour", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "skitour", "crosscountry", "ski touring", "piste"], "tags": {"piste:type": "skitour"}, "name": "Ski Touring Trail"}, "piste/sled": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "sled", "luge", "sleigh", "sledge", "piste"], "tags": {"piste:type": "sled"}, "name": "Sled Piste"}, - "piste/sleigh": {"icon": "fas-snowflake", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "piste", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse"], "tags": {"piste:type": "sleigh"}, "name": "Sleigh Piste"}, + "piste/sleigh": {"icon": "fas-sleigh", "fields": ["name", "piste/type", "piste/difficulty", "piste/grooming", "oneway", "lit"], "geometry": ["line", "area"], "terms": ["ski", "piste", "sled", "luge", "sleigh", "sledge", "ski-joring", "husky", "horse"], "tags": {"piste:type": "sleigh"}, "name": "Sleigh Piste"}, "place/farm": {"icon": "maki-farm", "geometry": ["point", "area"], "fields": ["name"], "tags": {"place": "farm"}, "name": "Farm", "searchable": false}, "place/city_block": {"icon": "maki-triangle-stroked", "fields": ["name"], "geometry": ["point", "area"], "tags": {"place": "city_block"}, "name": "City Block"}, "place/city": {"icon": "maki-city", "fields": ["name", "population"], "geometry": ["point", "area"], "tags": {"place": "city"}, "name": "City"}, diff --git a/data/presets/presets/leisure/ice_rink.json b/data/presets/presets/leisure/ice_rink.json index 84a715e64..567dd8ec0 100644 --- a/data/presets/presets/leisure/ice_rink.json +++ b/data/presets/presets/leisure/ice_rink.json @@ -1,5 +1,5 @@ { - "icon": "maki-pitch", + "icon": "fas-skating", "fields": [ "name", "seasonal", diff --git a/data/presets/presets/piste/downhill.json b/data/presets/presets/piste/downhill.json index c8e453def..454724ee5 100644 --- a/data/presets/presets/piste/downhill.json +++ b/data/presets/presets/piste/downhill.json @@ -1,5 +1,5 @@ { - "icon": "maki-skiing", + "icon": "fas-skiing", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/downhill/halfpipe.json b/data/presets/presets/piste/downhill/halfpipe.json new file mode 100644 index 000000000..da347d44d --- /dev/null +++ b/data/presets/presets/piste/downhill/halfpipe.json @@ -0,0 +1,29 @@ +{ + "icon": "fas-snowboarding", + "fields": [ + "name", + "piste/type", + "piste/difficulty_downhill", + "piste/grooming_downhill", + "oneway", + "lit" + ], + "geometry": [ + "line", + "area" + ], + "terms": [ + "ski", + "alpine", + "halfpipe", + "half pipe", + "snowboard", + "downhill", + "piste" + ], + "tags": { + "piste:type": "downhill", + "man_made": "piste:halfpipe" + }, + "name": "Halfpipe" +} diff --git a/data/presets/presets/piste/hike.json b/data/presets/presets/piste/hike.json index 79aaa6183..0801dec6f 100644 --- a/data/presets/presets/piste/hike.json +++ b/data/presets/presets/piste/hike.json @@ -1,5 +1,5 @@ { - "icon": "fas-snowflake", + "icon": "fas-skating", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/ice_skate.json b/data/presets/presets/piste/ice_skate.json index a64e4854c..c5f8650d7 100644 --- a/data/presets/presets/piste/ice_skate.json +++ b/data/presets/presets/piste/ice_skate.json @@ -1,5 +1,5 @@ { - "icon": "fas-snowflake", + "icon": "fas-skating", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/nordic.json b/data/presets/presets/piste/nordic.json index 99d4d17f2..5774c79c3 100644 --- a/data/presets/presets/piste/nordic.json +++ b/data/presets/presets/piste/nordic.json @@ -1,5 +1,5 @@ { - "icon": "maki-skiing", + "icon": "fas-skiing-nordic", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/piste.json b/data/presets/presets/piste/piste.json index c6c38f060..72b40659d 100644 --- a/data/presets/presets/piste/piste.json +++ b/data/presets/presets/piste/piste.json @@ -1,5 +1,5 @@ { - "icon": "maki-skiing", + "icon": "fas-skiing", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/skitour.json b/data/presets/presets/piste/skitour.json index 831b84fe9..aaaf5c7b4 100644 --- a/data/presets/presets/piste/skitour.json +++ b/data/presets/presets/piste/skitour.json @@ -1,5 +1,5 @@ { - "icon": "maki-skiing", + "icon": "fas-skiing-nordic", "fields": [ "name", "piste/type", diff --git a/data/presets/presets/piste/sleigh.json b/data/presets/presets/piste/sleigh.json index dc7c7cd28..061b4e412 100644 --- a/data/presets/presets/piste/sleigh.json +++ b/data/presets/presets/piste/sleigh.json @@ -1,5 +1,5 @@ { - "icon": "fas-snowflake", + "icon": "fas-sleigh", "fields": [ "name", "piste/type", diff --git a/data/taginfo.json b/data/taginfo.json index 22e6bf284..bbdea1d71 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -3678,7 +3678,7 @@ "value": "ice_rink", "description": "🄿 Ice Rink", "object_types": ["node", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pitch-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true" }, { "key": "leisure", @@ -4717,41 +4717,48 @@ "value": "downhill", "description": "🄿 Downhill Piste/Ski Run, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing.svg?sanitize=true" + }, + { + "key": "man_made", + "value": "piste:halfpipe", + "description": "🄿 Halfpipe", + "object_types": ["way", "area"], + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowboarding.svg?sanitize=true" }, { "key": "piste:type", "value": "hike", "description": "🄿 Snowshoeing or Winter Hiking Trail, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true" }, { "key": "piste:type", "value": "ice_skate", "description": "🄿 Ice Skating Piste, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skating.svg?sanitize=true" }, { "key": "piste:type", "value": "nordic", "description": "🄿 Nordic or Crosscountry Piste/Ski Trail, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing-nordic.svg?sanitize=true" }, { "key": "piste:type", "description": "🄿 Winter Sport Trails", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing.svg?sanitize=true" }, { "key": "piste:type", "value": "skitour", "description": "🄿 Ski Touring Trail, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/skiing-15.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-skiing-nordic.svg?sanitize=true" }, { "key": "piste:type", @@ -4765,7 +4772,7 @@ "value": "sleigh", "description": "🄿 Sleigh Piste, 🄵 Type", "object_types": ["way", "area"], - "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-snowflake.svg?sanitize=true" + "icon_url": "https://raw.githubusercontent.com/openstreetmap/iD/master/svg/fontawesome/fas-sleigh.svg?sanitize=true" }, { "key": "place", diff --git a/dist/locales/en.json b/dist/locales/en.json index 193b3ff19..9bc2d64a7 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6039,6 +6039,10 @@ "name": "Downhill Piste/Ski Run", "terms": "ski,alpine,snowboard,downhill,piste" }, + "piste/downhill/halfpipe": { + "name": "Halfpipe", + "terms": "ski,alpine,halfpipe,half pipe,snowboard,downhill,piste" + }, "piste/hike": { "name": "Snowshoeing or Winter Hiking Trail", "terms": "hike,winter hiking,snowshoe,snowshoeing,piste,ski" diff --git a/package.json b/package.json index 927a8033f..233276883 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,10 @@ "wmf-sitematrix": "0.1.4" }, "devDependencies": { - "@fortawesome/fontawesome-svg-core": "~1.2.9", - "@fortawesome/free-brands-svg-icons": "~5.6.0", - "@fortawesome/free-regular-svg-icons": "~5.6.0", - "@fortawesome/free-solid-svg-icons": "~5.6.0", + "@fortawesome/fontawesome-svg-core": "~1.2.12", + "@fortawesome/free-brands-svg-icons": "~5.6.3", + "@fortawesome/free-regular-svg-icons": "~5.6.3", + "@fortawesome/free-solid-svg-icons": "~5.6.3", "@mapbox/maki": "^6.0.0", "chai": "^4.1.0", "colors": "^1.1.2", diff --git a/svg/fontawesome/fas-skating.svg b/svg/fontawesome/fas-skating.svg new file mode 100644 index 000000000..aa80f7c2f --- /dev/null +++ b/svg/fontawesome/fas-skating.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-skiing-nordic.svg b/svg/fontawesome/fas-skiing-nordic.svg new file mode 100644 index 000000000..efe3a9d59 --- /dev/null +++ b/svg/fontawesome/fas-skiing-nordic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-skiing.svg b/svg/fontawesome/fas-skiing.svg new file mode 100644 index 000000000..75422fa8f --- /dev/null +++ b/svg/fontawesome/fas-skiing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-sleigh.svg b/svg/fontawesome/fas-sleigh.svg new file mode 100644 index 000000000..91ffea4e7 --- /dev/null +++ b/svg/fontawesome/fas-sleigh.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-snowboarding.svg b/svg/fontawesome/fas-snowboarding.svg new file mode 100644 index 000000000..721620e03 --- /dev/null +++ b/svg/fontawesome/fas-snowboarding.svg @@ -0,0 +1 @@ + \ No newline at end of file From c97b659a34f20006e05ae76c93e5858153cccda9 Mon Sep 17 00:00:00 2001 From: Tobias Jordans Date: Sat, 22 Dec 2018 19:10:50 +0100 Subject: [PATCH 049/114] Allow locale and language being set via URL param With the hash-url `locale=en-US` or `locale=de-DE` one can force a locale and language regardless of the given language from the osm-website-settings. --- modules/util/detect.js | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/modules/util/detect.js b/modules/util/detect.js index 19af93b5d..9265e2d83 100644 --- a/modules/util/detect.js +++ b/modules/util/detect.js @@ -59,24 +59,31 @@ export function utilDetect(force) { // Added due to incomplete svg style support. See #715 detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 ); - detected.locale = (navigator.language || navigator.userLanguage || 'en-US'); - detected.language = detected.locale.split('-')[0]; + // Set locale based on url param (format 'en-US') or browser lang (default) + var q = utilStringQs(window.location.hash.substring(1)); + if (q.hasOwnProperty('locale')) { + detected.locale = q.locale; + detected.language = q.locale.split('-')[0]; + } else { + detected.locale = (navigator.language || navigator.userLanguage || 'en-US'); + detected.language = detected.locale.split('-')[0]; - // Search `navigator.languages` for a better locale.. Prefer the first language, - // unless the second language is a culture-specific version of the first one, see #3842 - if (navigator.languages && navigator.languages.length > 0) { - var code0 = navigator.languages[0], - parts0 = code0.split('-'); + // Search `navigator.languages` for a better locale. Prefer the first language, + // unless the second language is a culture-specific version of the first one, see #3842 + if (navigator.languages && navigator.languages.length > 0) { + var code0 = navigator.languages[0], + parts0 = code0.split('-'); - detected.locale = code0; - detected.language = parts0[0]; + detected.locale = code0; + detected.language = parts0[0]; - if (navigator.languages.length > 1 && parts0.length === 1) { - var code1 = navigator.languages[1], - parts1 = code1.split('-'); + if (navigator.languages.length > 1 && parts0.length === 1) { + var code1 = navigator.languages[1], + parts1 = code1.split('-'); - if (parts1[0] === parts0[0]) { - detected.locale = code1; + if (parts1[0] === parts0[0]) { + detected.locale = code1; + } } } } From e51f2eb1e706e369d8e8518818d6ee5747a9cf15 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 22 Dec 2018 13:42:09 -0500 Subject: [PATCH 050/114] pacify eslint --- build_data.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build_data.js b/build_data.js index 7d80cac3a..ad5e92033 100644 --- a/build_data.js +++ b/build_data.js @@ -537,7 +537,7 @@ function writeFaIcons(faIcons) { writeFileProm('svg/fontawesome/' + key + '.svg', fontawesome.icon(def).html); } catch (error) { console.error('Error: No FontAwesome icon for ' + key); - throw(error); + throw (error); } } } From aa78cd10a3d233222a3cdc0469152af3915dde9b Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 22 Dec 2018 14:58:15 -0500 Subject: [PATCH 051/114] Data items - remove hardcoded locale IDs Locale IDs are now fetched together with the other data when the language code is provided. --- modules/services/osm_wikibase.js | 54 ++++++++++++++++++------------ test/spec/services/osm_wikibase.js | 13 +++++-- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js index 22db05bd9..7570c5a56 100644 --- a/modules/services/osm_wikibase.js +++ b/modules/services/osm_wikibase.js @@ -9,6 +9,7 @@ import { utilQsString } from '../util'; var apibase = 'https://wiki.openstreetmap.org/w/api.php'; var _inflight = {}; var _wikibaseCache = {}; +var _localeIds = {}; var debouncedRequest = _debounce(request, 500, { leading: false }); @@ -28,6 +29,7 @@ export default { init: function() { _inflight = {}; _wikibaseCache = {}; + _localeIds = {}; }, @@ -37,21 +39,6 @@ export default { }, - /** List of data items representing language regions. - * To regenerate, use Sophox query: http://tinyurl.com/y6v9ne2c (every instance of Q6999) - * A less accurate list can be seen here (everything that links to Q6999): - * https://wiki.openstreetmap.org/w/index.php?title=Special%3AWhatLinksHere&target=Item%3AQ6999&namespace=120 - */ - regionCodes: { - ar: 'Q7780', az: 'Q7781', bg: 'Q7782', bn: 'Q7783', ca: 'Q7784', cs: 'Q7785', da: 'Q7786', - de: 'Q6994', el: 'Q7787', es: 'Q7788', et: 'Q7789', fa: 'Q7790', fi: 'Q7791', fr: 'Q7792', - gl: 'Q7793', hr: 'Q7794', ht: 'Q7795', hu: 'Q7796', id: 'Q7797', it: 'Q7798', ja: 'Q7799', - ko: 'Q7800', lt: 'Q7801', lv: 'Q7802', ms: 'Q7803', nl: 'Q7804', no: 'Q7805', pl: 'Q7806', - pt: 'Q7807', ro: 'Q7808', ru: 'Q7809', sk: 'Q7810', sq: 'Q7811', sv: 'Q7812', tr: 'Q7813', - uk: 'Q7814', vi: 'Q7815', yue: 'Q7816', 'zh-hans': 'Q7817', 'zh-hant': 'Q7818', - }, - - /** * Get the best value for the property, or undefined if not found * @param entity object from wikibase @@ -60,19 +47,21 @@ export default { */ claimToValue: function(entity, property, langCode) { if (!entity.claims[property]) return undefined; - var region = this.regionCodes[langCode]; - var preferredPick, regionPick; + var locale = _localeIds[langCode]; + var preferredPick, localePick; _forEach(entity.claims[property], function(stmt) { - // If exists, use value limited to the needed language (has a qualifier P26 = region) + // If exists, use value limited to the needed language (has a qualifier P26 = locale) // Or if not found, use the first value with the "preferred" rank if (!preferredPick && stmt.rank === 'preferred') { preferredPick = stmt; } - if (stmt.qualifiers && stmt.qualifiers.P26 && stmt.qualifiers.P26[0].datavalue.value.id === region) { - regionPick = stmt; + if (locale && stmt.qualifiers && stmt.qualifiers.P26 && + stmt.qualifiers.P26[0].datavalue.value.id === locale + ) { + localePick = stmt; } }); - var result = regionPick || preferredPick; + var result = localePick || preferredPick; if (result) { var datavalue = result.mainsnak.datavalue; @@ -106,15 +95,23 @@ export default { getEntity: function(params, callback) { var doRequest = params.debounce ? debouncedRequest : request; - + var self = this; var titles = []; var languages = ['en']; var result = {}; var keySitelink = this.toSitelink(params.key); var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false; + var localeSitelink; if (params.langCode && params.langCode !== 'en') { languages.push(params.langCode); + if (!_localeIds[params.langCode]) { + // This is the first time we are asking about this locale + // Fetch corresponding entity (if it exists), and cache it. + // If there is no such entry, cache `true` to avoid re-requesting it. + localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim(); + titles.push(localeSitelink); + } } if (_wikibaseCache[keySitelink]) { @@ -155,6 +152,7 @@ export default { } else if (!d.success || d.error) { callback(d.error.messages.map(function(v) { return v.html['*']; }).join('
')); } else { + var localeId = true; _forEach(d.entities, function(res) { if (res.missing !== '') { var title = res.sitelinks.wiki.title; @@ -164,18 +162,30 @@ export default { } else if (title === tagSitelink) { _wikibaseCache[tagSitelink] = res; result.tag = res; + } else if (title === localeSitelink) { + localeId = res.id; } else { console.log('Unexpected title ' + title); } } }); + if (localeSitelink) { + // If locale ID is not found, set cache to true to prevent repeated queries + self.addLocale(params.langCode, localeId); + } + callback(null, result); } }); }, + addLocale: function(langCode, qid) { + // Makes it easier to unit test + _localeIds[langCode] = qid; + }, + apibase: function(_) { if (!arguments.length) return apibase; apibase = _; diff --git a/test/spec/services/osm_wikibase.js b/test/spec/services/osm_wikibase.js index 2536fd246..aaccc16f4 100644 --- a/test/spec/services/osm_wikibase.js +++ b/test/spec/services/osm_wikibase.js @@ -245,6 +245,12 @@ describe('iD.serviceOsmWikibase', function () { } }; + + var localeData = { + id: 'Q7792', + sitelinks: {wiki: {site: 'wiki', title: 'Locale:fr'}} + }; + describe('#getEntity', function () { it('calls the given callback with the results of the getEntity data item query', function () { var callback = sinon.spy(); @@ -254,7 +260,8 @@ describe('iD.serviceOsmWikibase', function () { [200, {'Content-Type': 'application/json'}, JSON.stringify({ entities: { Q61: keyData, - Q4904: tagData + Q4904: tagData, + Q7792: localeData, }, success: 1 })] @@ -268,7 +275,7 @@ describe('iD.serviceOsmWikibase', function () { languages: 'en|fr', origin: '*', sites: 'wiki', - titles: 'Key:amenity|Tag:amenity=parking', + titles: 'Locale:fr|Key:amenity|Tag:amenity=parking', } ); expect(callback).to.have.been.calledWith(null, {key: keyData, tag: tagData}); @@ -287,6 +294,8 @@ describe('iD.serviceOsmWikibase', function () { }); it('gets correct value from entity', function () { + wikibase.addLocale('de', 'Q6994'); + wikibase.addLocale('fr', 'Q7792'); expect(wikibase.claimToValue(tagData, 'P4', 'en')).to.eql('Primary image.jpg'); expect(wikibase.claimToValue(keyData, 'P6', 'en')).to.eql('Q15'); expect(wikibase.claimToValue(keyData, 'P6', 'fr')).to.eql('Q15'); From 8b931f3e6c50bc447f39d469aadf814332d8a775 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 22 Dec 2018 15:03:22 -0500 Subject: [PATCH 052/114] Use new message to encourage editing and translation --- modules/ui/tag_reference.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index d4ce97f5c..8fa05a1b8 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -108,7 +108,7 @@ export function uiTagReference(tag) { .attr('href', 'https://wiki.openstreetmap.org/wiki/' + docs.title) .call(svgIcon('#iD-icon-out-link', 'inline')) .append('span') - .text(t('inspector.reference')); + .text(t('inspector.edit_reference')); // Add link to info about "good changeset comments" - #2923 if (param.key === 'comment') { From 5009edee4bee6130d4854d2abfd1585325562642 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 23 Dec 2018 07:38:00 +0100 Subject: [PATCH 053/114] Move variable to the top of the file and reuse As suggested in https://github.com/openstreetmap/iD/pull/5650#issuecomment-449589375 --- modules/util/detect.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/util/detect.js b/modules/util/detect.js index 9265e2d83..c3a98c185 100644 --- a/modules/util/detect.js +++ b/modules/util/detect.js @@ -9,7 +9,8 @@ export function utilDetect(force) { detected = {}; var ua = navigator.userAgent, - m = null; + m = null, + q = utilStringQs(window.location.hash.substring(1)); m = ua.match(/(edge)\/?\s*(\.?\d+(\.\d+)*)/i); // Edge if (m !== null) { @@ -60,7 +61,6 @@ export function utilDetect(force) { detected.opera = (detected.browser.toLowerCase() === 'opera' && parseFloat(detected.version) < 15 ); // Set locale based on url param (format 'en-US') or browser lang (default) - var q = utilStringQs(window.location.hash.substring(1)); if (q.hasOwnProperty('locale')) { detected.locale = q.locale; detected.language = q.locale.split('-')[0]; @@ -97,7 +97,6 @@ export function utilDetect(force) { } // detect text direction - var q = utilStringQs(window.location.hash.substring(1)); var lang = dataLocales[detected.locale]; if ((lang && lang.rtl) || (q.rtl === 'true')) { detected.textDirection = 'rtl'; From 9f7f4aa3fcf95eb3ceb86e9965ee286d1d94f0c0 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sun, 23 Dec 2018 01:04:35 -0500 Subject: [PATCH 054/114] Fix language fallback processing, caching bug. --- modules/services/osm_wikibase.js | 66 ++-- modules/ui/tag_reference.js | 2 +- test/spec/services/osm_wikibase.js | 470 +++++++++++++++-------------- 3 files changed, 278 insertions(+), 260 deletions(-) diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js index 7570c5a56..366f6c5fc 100644 --- a/modules/services/osm_wikibase.js +++ b/modules/services/osm_wikibase.js @@ -9,7 +9,7 @@ import { utilQsString } from '../util'; var apibase = 'https://wiki.openstreetmap.org/w/api.php'; var _inflight = {}; var _wikibaseCache = {}; -var _localeIds = {}; +var _localeIds = { en: false }; var debouncedRequest = _debounce(request, 500, { leading: false }); @@ -24,6 +24,22 @@ function request(url, callback) { } +/** + * Get the best string value from the descriptions/labels result + * Note that if mediawiki doesn't recognize language code, it will return all values. + * In that case, fallback to use English. + * @param values object - either descriptions or labels + * @param langCode String + * @returns localized string + */ +function localizedToString(values, langCode) { + if (values) { + values = values[langCode] || values.en; + } + return values ? values.value : ''; +} + + export default { init: function() { @@ -72,21 +88,6 @@ export default { }, - getDescription: function(entity) { - if (entity.descriptions) { - // Assume that there will be at most two languages because of - // how we request it: English + possibly another one. - // Pick non-English description if available (if we have more than one) - var langs = Object.keys(entity.descriptions); - if (langs.length) { - var lng = langs.length > 1 && langs[0] === 'en' ? langs[1] : langs[0]; - return entity.descriptions[lng].value; - } - } - return undefined; - }, - - toSitelink: function(key, value) { var result = value ? 'Tag:' + key + '=' + value : 'Key:' + key; return result.replace(/_/g, ' ').trim(); @@ -97,21 +98,17 @@ export default { var doRequest = params.debounce ? debouncedRequest : request; var self = this; var titles = []; - var languages = ['en']; var result = {}; var keySitelink = this.toSitelink(params.key); var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false; var localeSitelink; - if (params.langCode && params.langCode !== 'en') { - languages.push(params.langCode); - if (!_localeIds[params.langCode]) { - // This is the first time we are asking about this locale - // Fetch corresponding entity (if it exists), and cache it. - // If there is no such entry, cache `true` to avoid re-requesting it. - localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim(); - titles.push(localeSitelink); - } + if (params.langCode && _localeIds[params.langCode] === undefined) { + // If this is the first time we are asking about this locale, + // fetch corresponding entity (if it exists), and cache it. + // If there is no such entry, cache `false` value to avoid re-requesting it. + localeSitelink = ('Locale:' + params.langCode).replace(/_/g, ' ').trim(); + titles.push(localeSitelink); } if (_wikibaseCache[keySitelink]) { @@ -122,7 +119,7 @@ export default { if (tagSitelink) { if (_wikibaseCache[tagSitelink]) { - result.key = _wikibaseCache[tagSitelink]; + result.tag = _wikibaseCache[tagSitelink]; } else { titles.push(tagSitelink); } @@ -133,11 +130,17 @@ export default { return callback(null, result); } + // Requesting just the user language code + // If backend recognizes the code, it will perform proper fallbacks, + // and the result will contain the requested code. If not, all values are returned: + // {"zh-tw":{"value":"...","language":"zh-tw","source-language":"zh-hant"} + // {"pt-br":{"value":"...","language":"pt","for-language":"pt-br"}} var obj = { action: 'wbgetentities', sites: 'wiki', titles: titles.join('|'), - languages: languages.join('|'), + languages: params.langCode, + languagefallback: 1, origin: '*', format: 'json', // There is an MW Wikibase API bug https://phabricator.wikimedia.org/T212069 @@ -152,10 +155,13 @@ export default { } else if (!d.success || d.error) { callback(d.error.messages.map(function(v) { return v.html['*']; }).join('
')); } else { - var localeId = true; + var localeId = false; _forEach(d.entities, function(res) { if (res.missing !== '') { var title = res.sitelinks.wiki.title; + // Simplify access to the localized values + res.description = localizedToString(res.descriptions, params.langCode); + res.label = localizedToString(res.labels, params.langCode); if (title === keySitelink) { _wikibaseCache[keySitelink] = res; result.key = res; @@ -171,7 +177,7 @@ export default { }); if (localeSitelink) { - // If locale ID is not found, set cache to true to prevent repeated queries + // If locale ID is not found, store false to prevent repeated queries self.addLocale(params.langCode, localeId); } diff --git a/modules/ui/tag_reference.js b/modules/ui/tag_reference.js index 8fa05a1b8..6215250b9 100644 --- a/modules/ui/tag_reference.js +++ b/modules/ui/tag_reference.js @@ -28,7 +28,7 @@ export function uiTagReference(tag) { var result = { title: entity.title, - description: wikibase.getDescription(entity), + description: entity.description, }; if (entity.claims) { diff --git a/test/spec/services/osm_wikibase.js b/test/spec/services/osm_wikibase.js index aaccc16f4..61951abba 100644 --- a/test/spec/services/osm_wikibase.js +++ b/test/spec/services/osm_wikibase.js @@ -24,226 +24,239 @@ describe('iD.serviceOsmWikibase', function () { return iD.utilStringQs(url.substring(url.indexOf('?') + 1)); } - var keyData = { - pageid: 205725, - ns: 120, - title: 'Item:Q61', - lastrevid: 1721242, - modified: '2018-12-18T07:00:43Z', - type: 'item', - id: 'Q61', - labels: { - en: {language: 'en', value: 'amenity'} - }, - descriptions: { - en: {language: 'en', value: 'For describing useful and important facilities for visitors and residents.'} - }, - aliases: {}, - claims: { - P2: [ // instance of - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q7'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'normal' - } - ], - P16: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'string', - datavalue: {value: 'amenity', type: 'string'} - }, - type: 'statement', - rank: 'normal' - } - ], - P25: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'normal' - } - ], - P9: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q8'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'normal' - } - ], - P6: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q15'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'preferred' - }, - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} - }, - type: 'statement', - qualifiers: { - P26: [ - { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} - } - ] - }, - rank: 'normal' - } - ], - P28: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'string', - datavalue: {value: 'Mapping-Features-Parking-Lot.png', type: 'string'} - }, - type: 'statement', - rank: 'normal' - } - ] - }, - sitelinks: { - wiki: { - site: 'wiki', - title: 'Key:amenity', - badges: [] + function adjust(params, data) { + if (params) { + if (params.norm) { + data.description = data.descriptions.fr.value; + data.label = data.labels.fr.value; } } - }; + return data; + } - var tagData = { - pageid: 210934, - ns: 120, - title: 'Item:Q4904', - lastrevid: 1718041, - modified: '2018-12-18T03:51:05Z', - type: 'item', - id: 'Q4904', - labels: { - en: {language: 'en', value: 'amenity=parking'} - }, - descriptions: { - en: {language: 'en', value: 'A place for parking cars'}, - fr: {language: 'fr', value: 'Un lieu pour garer des voitures'} - }, - aliases: {}, - claims: { - P2: [ // instance of = Q2 (tag) - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q2'}, type: 'wikibase-entityid'} + function keyData(params) { + return adjust(params, { + pageid: 205725, + ns: 120, + title: 'Item:Q42', + lastrevid: 1721242, + modified: '2018-12-18T07:00:43Z', + type: 'item', + id: 'Q42', + labels: { + fr: {language: 'en', value: 'amenity', 'for-language': 'fr'} + }, + descriptions: { + fr: {language: 'en', value: 'English description', 'for-language': 'fr'} + }, + aliases: {}, + claims: { + P2: [ // instance of + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q7'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P16: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'amenity', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ], + P25: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P9: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q8'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P6: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q15'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'preferred' }, - type: 'statement', - rank: 'normal' + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} + }, + type: 'statement', + qualifiers: { + P26: [ + { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} + } + ] + }, + rank: 'normal' + } + ], + P28: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'Mapping-Features-Parking-Lot.png', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ] + }, + sitelinks: { + wiki: { + site: 'wiki', + title: 'Key:amenity', + badges: [] } - ], - P19: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'string', - datavalue: {value: 'amenity=parking', type: 'string'} - }, - type: 'statement', - rank: 'normal' - } - ], - P10: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q61'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'normal' - } - ], - P4: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'commonsMedia', - datavalue: {value: 'Primary image.jpg', type: 'string'} - }, - type: 'statement', - rank: 'preferred' - } - ], - P6: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'preferred' - }, - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q13'}, type: 'wikibase-entityid'} - }, - type: 'statement', - qualifiers: { - P26: [ - { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} - } - ] - }, - rank: 'normal' - } - ], - P25: [ - { - mainsnak: { - snaktype: 'value', - datatype: 'wikibase-item', - datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} - }, - type: 'statement', - rank: 'normal' - } - ] - }, - sitelinks: { - wiki: { - site: 'wiki', - title: 'Tag:amenity=parking', - badges: [] } - } - }; + }); + } + + function tagData(params) { + return adjust(params, { + pageid: 210934, + ns: 120, + title: 'Item:Q13', + lastrevid: 1718041, + modified: '2018-12-18T03:51:05Z', + type: 'item', + id: 'Q13', + labels: { + fr: {language: 'en', value: 'amenity=parking', 'for-language': 'fr'} + }, + descriptions: { + fr: {language: 'fr', value: 'French description'} + }, + aliases: {}, + claims: { + P2: [ // instance of = Q2 (tag) + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q2'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P19: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'string', + datavalue: {value: 'amenity=parking', type: 'string'} + }, + type: 'statement', + rank: 'normal' + } + ], + P10: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q42'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ], + P4: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'commonsMedia', + datavalue: {value: 'Primary image.jpg', type: 'string'} + }, + type: 'statement', + rank: 'preferred' + } + ], + P6: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q14'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'preferred' + }, + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q13'}, type: 'wikibase-entityid'} + }, + type: 'statement', + qualifiers: { + P26: [ + { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q6994'}, type: 'wikibase-entityid'} + } + ] + }, + rank: 'normal' + } + ], + P25: [ + { + mainsnak: { + snaktype: 'value', + datatype: 'wikibase-item', + datavalue: {value: {'entity-type': 'item', id: 'Q4679'}, type: 'wikibase-entityid'} + }, + type: 'statement', + rank: 'normal' + } + ] + }, + sitelinks: { + wiki: { + site: 'wiki', + title: 'Tag:amenity=parking', + badges: [] + } + } + }); + } var localeData = { @@ -259,8 +272,8 @@ describe('iD.serviceOsmWikibase', function () { server.respondWith('GET', /action=wbgetentities/, [200, {'Content-Type': 'application/json'}, JSON.stringify({ entities: { - Q61: keyData, - Q4904: tagData, + Q42: keyData(), + Q13: tagData(), Q7792: localeData, }, success: 1 @@ -271,14 +284,18 @@ describe('iD.serviceOsmWikibase', function () { expect(query(server.requests[0].url)).to.eql( { action: 'wbgetentities', - format: 'json', - languages: 'en|fr', - origin: '*', sites: 'wiki', titles: 'Locale:fr|Key:amenity|Tag:amenity=parking', + languages: 'fr', + languagefallback: '1', + origin: '*', + format: 'json', } ); - expect(callback).to.have.been.calledWith(null, {key: keyData, tag: tagData}); + expect(callback).to.have.been.calledWith(null, { + key: keyData({norm: true}), + tag: tagData({norm: true}) + }); }); }); @@ -296,15 +313,10 @@ describe('iD.serviceOsmWikibase', function () { it('gets correct value from entity', function () { wikibase.addLocale('de', 'Q6994'); wikibase.addLocale('fr', 'Q7792'); - expect(wikibase.claimToValue(tagData, 'P4', 'en')).to.eql('Primary image.jpg'); - expect(wikibase.claimToValue(keyData, 'P6', 'en')).to.eql('Q15'); - expect(wikibase.claimToValue(keyData, 'P6', 'fr')).to.eql('Q15'); - expect(wikibase.claimToValue(keyData, 'P6', 'de')).to.eql('Q14'); - }); - - it('gets correct description from entity', function () { - expect(wikibase.getDescription(tagData)).to.eql('Un lieu pour garer des voitures'); - expect(wikibase.getDescription(keyData)).to.eql('For describing useful and important facilities for visitors and residents.'); + expect(wikibase.claimToValue(tagData(), 'P4', 'en')).to.eql('Primary image.jpg'); + expect(wikibase.claimToValue(keyData(), 'P6', 'en')).to.eql('Q15'); + expect(wikibase.claimToValue(keyData(), 'P6', 'fr')).to.eql('Q15'); + expect(wikibase.claimToValue(keyData(), 'P6', 'de')).to.eql('Q14'); }); }); From da95d1ee5ad49bc7605df6cfe9ead97629118500 Mon Sep 17 00:00:00 2001 From: hikemaniac <31667811+hikemaniac@users.noreply.github.com> Date: Sun, 23 Dec 2018 17:33:41 +0100 Subject: [PATCH 055/114] Added a preset for shop=fuel --- data/presets.yaml | 4 ++++ data/presets/presets.json | 1 + data/presets/presets/shop/fuel.json | 20 ++++++++++++++++++++ data/taginfo.json | 7 +++++++ dist/locales/en.json | 4 ++++ 5 files changed, 36 insertions(+) create mode 100644 data/presets/presets/shop/fuel.json diff --git a/data/presets.yaml b/data/presets.yaml index d3733f9ad..ed14b0576 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -6048,6 +6048,10 @@ en: name: Framing Shop # 'terms: art*,paint*,photo*,frame' terms: '' + shop/fuel: + # shop=fuel + name: Fuel Shop + terms: '' shop/funeral_directors: # shop=funeral_directors name: Funeral Home diff --git a/data/presets/presets.json b/data/presets/presets.json index 4c29da297..8a07f281e 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -883,6 +883,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/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-center", "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/shop/fuel.json b/data/presets/presets/shop/fuel.json new file mode 100644 index 000000000..e0a3c7dbb --- /dev/null +++ b/data/presets/presets/shop/fuel.json @@ -0,0 +1,20 @@ +{ + "icon": "maki-shop", + "fields": [ + "name", + "operator", + "address", + "fuel_multi", + "building_area", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "tags": { + "shop": "fuel" + }, + "name": "Fuel Shop" +} diff --git a/data/taginfo.json b/data/taginfo.json index 4ff721d38..5b59ce07e 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -5735,6 +5735,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" }, + { + "key": "shop", + "value": "fuel", + "description": "🄿 Fuel Shop", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/shop-15.svg?sanitize=true" + }, { "key": "shop", "value": "funeral_directors", diff --git a/dist/locales/en.json b/dist/locales/en.json index 3af9e01d3..2a23d4f84 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6721,6 +6721,10 @@ "name": "Framing Shop", "terms": "art*,paint*,photo*,frame" }, + "shop/fuel": { + "name": "Fuel Shop", + "terms": "" + }, "shop/funeral_directors": { "name": "Funeral Home", "terms": "undertaker,memorial home" From 4fc0e5500dd4578b9d4a06671a250217be2c1b95 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 27 Dec 2018 11:55:31 -0500 Subject: [PATCH 056/114] Add community index to transifex config file This doesn't really affect anything, but it allows us to `tx pull -a` down all the strings to a temporary location and look at them. --- .tx/config | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.tx/config b/.tx/config index 4f3831038..21389fb4a 100644 --- a/.tx/config +++ b/.tx/config @@ -2,6 +2,11 @@ host = https://www.transifex.com minimum_perc = 1 +[id-editor.community] +file_filter = .tx/tmp/community/.yaml +source_lang = en +type = YAML + [id-editor.core] file_filter = .tx/tmp/core/.yaml source_file = data/core.yaml From 495793929f46a1ab1d321054f405e4c3b0e82883 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 27 Dec 2018 12:06:53 -0500 Subject: [PATCH 057/114] Skip wikidata field test, too time sensitive to work reliably --- data/presets/presets.json | 8 +++++-- dist/locales/en.json | 37 ++++++++++++++++++++++++-------- test/spec/ui/fields/wikipedia.js | 2 +- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/data/presets/presets.json b/data/presets/presets.json index e70b37bdf..09de5cce3 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -1755,6 +1755,7 @@ "amenity/fuel/Slovnaft": {"name": "Slovnaft", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1587563"}, "addTags": {"amenity": "fuel", "brand": "Slovnaft", "brand:wikidata": "Q1587563", "brand:wikipedia": "en:Slovnaft", "name": "Slovnaft"}, "removeTags": {"amenity": "fuel", "brand": "Slovnaft", "brand:wikidata": "Q1587563", "brand:wikipedia": "en:Slovnaft", "name": "Slovnaft"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Socar": {"name": "Socar", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1622293"}, "addTags": {"amenity": "fuel", "brand": "Socar", "brand:wikidata": "Q1622293", "brand:wikipedia": "en:SOCAR", "name": "Socar"}, "removeTags": {"amenity": "fuel", "brand": "Socar", "brand:wikidata": "Q1622293", "brand:wikipedia": "en:SOCAR", "name": "Socar"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Sokimex": {"name": "Sokimex", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q1149575"}, "addTags": {"amenity": "fuel", "brand": "Sokimex", "brand:wikidata": "Q1149575", "brand:wikipedia": "en:Sokimex", "name": "Sokimex"}, "removeTags": {"amenity": "fuel", "brand": "Sokimex", "brand:wikidata": "Q1149575", "brand:wikipedia": "en:Sokimex", "name": "Sokimex"}, "matchScore": 2, "suggestion": true}, + "amenity/fuel/Speedway": {"name": "Speedway", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q7575683"}, "addTags": {"amenity": "fuel", "brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway"}, "removeTags": {"amenity": "fuel", "brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Sprint": {"name": "Sprint", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q57588452"}, "addTags": {"amenity": "fuel", "brand": "Sprint", "brand:wikidata": "Q57588452", "name": "Sprint"}, "removeTags": {"amenity": "fuel", "brand": "Sprint", "brand:wikidata": "Q57588452", "name": "Sprint"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/St1": {"name": "St1", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q7592214"}, "addTags": {"amenity": "fuel", "brand": "St1", "brand:wikidata": "Q7592214", "brand:wikipedia": "en:St1", "name": "St1"}, "removeTags": {"amenity": "fuel", "brand": "St1", "brand:wikidata": "Q7592214", "brand:wikipedia": "en:St1", "name": "St1"}, "matchScore": 2, "suggestion": true}, "amenity/fuel/Star": {"name": "Star", "icon": "maki-fuel", "fields": ["name", "brand", "operator", "address", "fuel_multi", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"amenity": "fuel", "brand:wikidata": "Q2031095"}, "addTags": {"amenity": "fuel", "brand": "Star", "brand:wikidata": "Q2031095", "brand:wikipedia": "de:Orlen Deutschland", "name": "Star"}, "removeTags": {"amenity": "fuel", "brand": "Star", "brand:wikidata": "Q2031095", "brand:wikipedia": "de:Orlen Deutschland", "name": "Star"}, "matchScore": 2, "suggestion": true}, @@ -2182,7 +2183,7 @@ "shop/clothes/Hot Topic": {"name": "Hot Topic", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q9294032"}, "addTags": {"brand": "Hot Topic", "brand:wikidata": "Q9294032", "brand:wikipedia": "en:Hot Topic", "name": "Hot Topic", "shop": "clothes"}, "removeTags": {"brand": "Hot Topic", "brand:wikidata": "Q9294032", "brand:wikipedia": "en:Hot Topic", "name": "Hot Topic", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Hugo Boss": {"name": "Hugo Boss", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q491627"}, "addTags": {"brand": "Hugo Boss", "brand:wikidata": "Q491627", "brand:wikipedia": "en:Hugo Boss", "name": "Hugo Boss", "shop": "clothes"}, "removeTags": {"brand": "Hugo Boss", "brand:wikidata": "Q491627", "brand:wikipedia": "en:Hugo Boss", "name": "Hugo Boss", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Hunkemöller": {"name": "Hunkemöller", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q2604175"}, "addTags": {"brand": "Hunkemöller", "brand:wikidata": "Q2604175", "brand:wikipedia": "en:Hunkemöller", "name": "Hunkemöller", "shop": "clothes"}, "removeTags": {"brand": "Hunkemöller", "brand:wikidata": "Q2604175", "brand:wikipedia": "en:Hunkemöller", "name": "Hunkemöller", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, - "shop/clothes/Intimissimi": {"name": "Intimissimi", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q305404"}, "addTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "name": "Intimissimi", "shop": "clothes"}, "removeTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "name": "Intimissimi", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, + "shop/clothes/Intimissimi": {"name": "Intimissimi", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q305404"}, "addTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "clothes": "underwear", "name": "Intimissimi", "shop": "clothes"}, "removeTags": {"brand": "Intimissimi", "brand:wikidata": "Q305404", "brand:wikipedia": "en:Intimissimi", "clothes": "underwear", "name": "Intimissimi", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Jack & Jones": {"name": "Jack & Jones", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q6077665"}, "addTags": {"brand": "Jack & Jones", "brand:wikidata": "Q6077665", "brand:wikipedia": "en:Jack & Jones", "name": "Jack & Jones", "shop": "clothes"}, "removeTags": {"brand": "Jack & Jones", "brand:wikidata": "Q6077665", "brand:wikipedia": "en:Jack & Jones", "name": "Jack & Jones", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/Joules": {"name": "Joules", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q25351738"}, "addTags": {"brand": "Joules", "brand:wikidata": "Q25351738", "brand:wikipedia": "en:Joules (clothing)", "name": "Joules", "shop": "clothes"}, "removeTags": {"brand": "Joules", "brand:wikidata": "Q25351738", "brand:wikipedia": "en:Joules (clothing)", "name": "Joules", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, "shop/clothes/KiK": {"name": "KiK", "icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes", "brand:wikidata": "Q883965"}, "addTags": {"brand": "KiK", "brand:wikidata": "Q883965", "brand:wikipedia": "en:KiK", "name": "KiK", "shop": "clothes"}, "removeTags": {"brand": "KiK", "brand:wikidata": "Q883965", "brand:wikipedia": "en:KiK", "name": "KiK", "shop": "clothes"}, "matchScore": 2, "suggestion": true}, @@ -2285,6 +2286,7 @@ "shop/convenience/Plaid Pantry": {"name": "Plaid Pantry", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7200591"}, "addTags": {"brand": "Plaid Pantry", "brand:wikidata": "Q7200591", "brand:wikipedia": "en:Plaid Pantry", "name": "Plaid Pantry", "shop": "convenience"}, "removeTags": {"brand": "Plaid Pantry", "brand:wikidata": "Q7200591", "brand:wikipedia": "en:Plaid Pantry", "name": "Plaid Pantry", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Sheetz": {"name": "Sheetz", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7492551"}, "addTags": {"brand": "Sheetz", "brand:wikidata": "Q7492551", "brand:wikipedia": "en:Sheetz", "name": "Sheetz", "shop": "convenience"}, "removeTags": {"brand": "Sheetz", "brand:wikidata": "Q7492551", "brand:wikipedia": "en:Sheetz", "name": "Sheetz", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Shell Select": {"name": "Shell Select", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q154950"}, "addTags": {"brand": "Shell Select", "brand:wikidata": "Q154950", "brand:wikipedia": "en:Royal Dutch Shell", "name": "Shell Select", "shop": "convenience"}, "removeTags": {"brand": "Shell Select", "brand:wikidata": "Q154950", "brand:wikipedia": "en:Royal Dutch Shell", "name": "Shell Select", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, + "shop/convenience/Speedway": {"name": "Speedway", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7575683"}, "addTags": {"brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway", "shop": "convenience"}, "removeTags": {"brand": "Speedway", "brand:wikidata": "Q7575683", "brand:wikipedia": "en:Speedway LLC", "name": "Speedway", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/The Co-operative Food": {"name": "The Co-operative Food", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q3277439"}, "addTags": {"brand": "The Co-operative Food", "brand:wikidata": "Q3277439", "brand:wikipedia": "en:Co-op Food", "name": "The Co-operative Food", "shop": "convenience"}, "removeTags": {"brand": "The Co-operative Food", "brand:wikidata": "Q3277439", "brand:wikipedia": "en:Co-op Food", "name": "The Co-operative Food", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/Tiger Mart": {"name": "Tiger Mart", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q57643977"}, "addTags": {"brand": "Tiger Mart", "brand:wikidata": "Q57643977", "name": "Tiger Mart", "shop": "convenience"}, "removeTags": {"brand": "Tiger Mart", "brand:wikidata": "Q57643977", "name": "Tiger Mart", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, "shop/convenience/United Dairy Farmers": {"name": "United Dairy Farmers", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "convenience", "brand:wikidata": "Q7887677"}, "addTags": {"brand": "United Dairy Farmers", "brand:wikidata": "Q7887677", "brand:wikipedia": "en:United Dairy Farmers", "name": "United Dairy Farmers", "shop": "convenience"}, "removeTags": {"brand": "United Dairy Farmers", "brand:wikidata": "Q7887677", "brand:wikipedia": "en:United Dairy Farmers", "name": "United Dairy Farmers", "shop": "convenience"}, "matchScore": 2, "suggestion": true}, @@ -2460,6 +2462,7 @@ "shop/houseware/Bed Bath & Beyond": {"name": "Bed Bath & Beyond", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q813782"}, "addTags": {"brand": "Bed Bath & Beyond", "brand:wikidata": "Q813782", "brand:wikipedia": "en:Bed Bath & Beyond", "name": "Bed Bath & Beyond", "shop": "houseware"}, "removeTags": {"brand": "Bed Bath & Beyond", "brand:wikidata": "Q813782", "brand:wikipedia": "en:Bed Bath & Beyond", "name": "Bed Bath & Beyond", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/Blokker": {"name": "Blokker", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q884934"}, "addTags": {"brand": "Blokker", "brand:wikidata": "Q884934", "brand:wikipedia": "en:Blokker Holding", "name": "Blokker", "shop": "houseware"}, "removeTags": {"brand": "Blokker", "brand:wikidata": "Q884934", "brand:wikipedia": "en:Blokker Holding", "name": "Blokker", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/Cervera": {"name": "Cervera", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q10447179"}, "addTags": {"brand": "Cervera", "brand:wikidata": "Q10447179", "brand:wikipedia": "sv:Cervera (företag)", "name": "Cervera", "shop": "houseware"}, "removeTags": {"brand": "Cervera", "brand:wikidata": "Q10447179", "brand:wikipedia": "sv:Cervera (företag)", "name": "Cervera", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, + "shop/houseware/HomeGoods": {"name": "HomeGoods", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q5887941"}, "addTags": {"brand": "HomeGoods", "brand:wikidata": "Q5887941", "brand:wikipedia": "en:HomeGoods", "name": "HomeGoods", "shop": "houseware"}, "removeTags": {"brand": "HomeGoods", "brand:wikidata": "Q5887941", "brand:wikipedia": "en:HomeGoods", "name": "HomeGoods", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/houseware/WMF": {"name": "WMF", "icon": "fas-blender", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "houseware", "brand:wikidata": "Q451423"}, "addTags": {"brand": "WMF", "brand:wikidata": "Q451423", "brand:wikipedia": "en:WMF Group", "name": "WMF", "shop": "houseware"}, "removeTags": {"brand": "WMF", "brand:wikidata": "Q451423", "brand:wikipedia": "en:WMF Group", "name": "WMF", "shop": "houseware"}, "matchScore": 2, "suggestion": true}, "shop/interior_decoration/Depot": {"name": "Depot", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration", "brand:wikidata": "Q1191740"}, "addTags": {"brand": "Depot", "brand:wikidata": "Q1191740", "brand:wikipedia": "de:Gries Deco Holding", "name": "Depot", "shop": "interior_decoration"}, "removeTags": {"brand": "Depot", "brand:wikidata": "Q1191740", "brand:wikipedia": "de:Gries Deco Holding", "name": "Depot", "shop": "interior_decoration"}, "matchScore": 2, "suggestion": true}, "shop/interior_decoration/Hemtex": {"name": "Hemtex", "icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "interior_decoration", "brand:wikidata": "Q10521868"}, "addTags": {"brand": "Hemtex", "name": "Hemtex", "brand:wikidata": "Q10521868", "brand:wikipedia": "sv:Hemtex", "shop": "interior_decoration"}, "removeTags": {"brand": "Hemtex", "name": "Hemtex", "brand:wikidata": "Q10521868", "brand:wikipedia": "sv:Hemtex", "shop": "interior_decoration"}, "matchScore": 2, "suggestion": true}, @@ -2504,7 +2507,6 @@ "shop/mobile_phone/Telekom": {"name": "Telekom", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q9396"}, "addTags": {"brand": "Telekom", "brand:wikidata": "Q9396", "brand:wikipedia": "en:Deutsche Telekom", "name": "Telekom", "shop": "mobile_phone"}, "removeTags": {"brand": "Telekom", "brand:wikidata": "Q9396", "brand:wikipedia": "en:Deutsche Telekom", "name": "Telekom", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Telenor": {"name": "Telenor", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q845632"}, "addTags": {"brand": "Telenor", "brand:wikidata": "Q845632", "brand:wikipedia": "en:Telenor", "name": "Telenor", "shop": "mobile_phone"}, "removeTags": {"brand": "Telenor", "brand:wikidata": "Q845632", "brand:wikipedia": "en:Telenor", "name": "Telenor", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Telus": {"name": "Telus", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q165858"}, "addTags": {"brand": "Telus", "brand:wikidata": "Q165858", "brand:wikipedia": "en:Telus", "name": "Telus", "shop": "mobile_phone"}, "removeTags": {"brand": "Telus", "brand:wikidata": "Q165858", "brand:wikipedia": "en:Telus", "name": "Telus", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, - "shop/mobile_phone/The Phone House": {"name": "The Phone House", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q118046"}, "addTags": {"brand": "The Phone House", "brand:wikidata": "Q118046", "brand:wikipedia": "en:Carphone Warehouse", "name": "The Phone House", "shop": "mobile_phone"}, "removeTags": {"brand": "The Phone House", "brand:wikidata": "Q118046", "brand:wikipedia": "en:Carphone Warehouse", "name": "The Phone House", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Turkcell": {"name": "Turkcell", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q283852"}, "addTags": {"brand": "Turkcell", "brand:wikidata": "Q283852", "brand:wikipedia": "en:Turkcell", "name": "Turkcell", "shop": "mobile_phone"}, "removeTags": {"brand": "Turkcell", "brand:wikidata": "Q283852", "brand:wikipedia": "en:Turkcell", "name": "Turkcell", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Télécentre": {"name": "Télécentre", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q180034"}, "addTags": {"brand": "Télécentre", "brand:wikidata": "Q180034", "brand:wikipedia": "en:Telecentre", "name": "Télécentre", "shop": "mobile_phone"}, "removeTags": {"brand": "Télécentre", "brand:wikidata": "Q180034", "brand:wikipedia": "en:Telecentre", "name": "Télécentre", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, "shop/mobile_phone/Verizon Wireless": {"name": "Verizon Wireless", "icon": "maki-mobile-phone", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "mobile_phone", "brand:wikidata": "Q919641"}, "addTags": {"brand": "Verizon Wireless", "brand:wikidata": "Q919641", "brand:wikipedia": "en:Verizon Wireless", "name": "Verizon Wireless", "shop": "mobile_phone"}, "removeTags": {"brand": "Verizon Wireless", "brand:wikidata": "Q919641", "brand:wikipedia": "en:Verizon Wireless", "name": "Verizon Wireless", "shop": "mobile_phone"}, "matchScore": 2, "suggestion": true}, @@ -2673,6 +2675,7 @@ "shop/supermarket/City Market": {"name": "City Market", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q5123299"}, "addTags": {"brand": "City Market", "brand:wikidata": "Q5123299", "brand:wikipedia": "en:City Market (US grocery store chain)", "name": "City Market", "shop": "supermarket"}, "removeTags": {"brand": "City Market", "brand:wikidata": "Q5123299", "brand:wikipedia": "en:City Market (US grocery store chain)", "name": "City Market", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Co-op": {"name": "Co-op", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q5440676"}, "addTags": {"brand": "Federated Co-operatives", "brand:wikidata": "Q5440676", "brand:wikipedia": "en:Federated Co-operatives", "name": "Co-op", "shop": "supermarket"}, "removeTags": {"brand": "Federated Co-operatives", "brand:wikidata": "Q5440676", "brand:wikipedia": "en:Federated Co-operatives", "name": "Co-op", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Colruyt": {"name": "Colruyt", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q2363991"}, "addTags": {"brand": "Colruyt", "brand:wikidata": "Q2363991", "brand:wikipedia": "en:Colruyt (supermarket)", "name": "Colruyt", "shop": "supermarket"}, "removeTags": {"brand": "Colruyt", "brand:wikidata": "Q2363991", "brand:wikipedia": "en:Colruyt (supermarket)", "name": "Colruyt", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, + "shop/supermarket/Combi": {"name": "Combi", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q1113618"}, "addTags": {"brand": "Combi", "brand:wikidata": "Q1113618", "brand:wikipedia": "de:Combi (Einkaufsmarkt)", "name": "Combi", "shop": "supermarket"}, "removeTags": {"brand": "Combi", "brand:wikidata": "Q1113618", "brand:wikipedia": "de:Combi (Einkaufsmarkt)", "name": "Combi", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Comercial Mexicana": {"name": "Comercial Mexicana", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q2985173"}, "addTags": {"brand": "Comercial Mexicana", "brand:wikidata": "Q2985173", "brand:wikipedia": "en:Comercial Mexicana", "name": "Comercial Mexicana", "shop": "supermarket"}, "removeTags": {"brand": "Comercial Mexicana", "brand:wikidata": "Q2985173", "brand:wikipedia": "en:Comercial Mexicana", "name": "Comercial Mexicana", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Conad": {"name": "Conad", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q639075"}, "addTags": {"brand": "Conad", "brand:wikidata": "Q639075", "brand:wikipedia": "it:Conad", "name": "Conad", "shop": "supermarket"}, "removeTags": {"brand": "Conad", "brand:wikidata": "Q639075", "brand:wikipedia": "it:Conad", "name": "Conad", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Conad City": {"name": "Conad City", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q57543102"}, "addTags": {"brand": "Conad City", "brand:wikidata": "Q57543102", "name": "Conad City", "shop": "supermarket"}, "removeTags": {"brand": "Conad City", "brand:wikidata": "Q57543102", "name": "Conad City", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, @@ -2835,6 +2838,7 @@ "shop/supermarket/Sobeys": {"name": "Sobeys", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q1143340"}, "addTags": {"brand": "Sobeys", "brand:wikidata": "Q1143340", "brand:wikipedia": "en:Sobeys", "name": "Sobeys", "shop": "supermarket"}, "removeTags": {"brand": "Sobeys", "brand:wikidata": "Q1143340", "brand:wikipedia": "en:Sobeys", "name": "Sobeys", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Soriana": {"name": "Soriana", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q735562"}, "addTags": {"brand": "Soriana", "brand:wikidata": "Q735562", "brand:wikipedia": "en:Soriana", "name": "Soriana", "shop": "supermarket"}, "removeTags": {"brand": "Soriana", "brand:wikidata": "Q735562", "brand:wikipedia": "en:Soriana", "name": "Soriana", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Sprouts Farmers Market": {"name": "Sprouts Farmers Market", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q7581369"}, "addTags": {"brand": "Sprouts Farmers Market", "brand:wikidata": "Q7581369", "brand:wikipedia": "en:Sprouts Farmers Market", "name": "Sprouts Farmers Market", "shop": "supermarket"}, "removeTags": {"brand": "Sprouts Farmers Market", "brand:wikidata": "Q7581369", "brand:wikipedia": "en:Sprouts Farmers Market", "name": "Sprouts Farmers Market", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, + "shop/supermarket/Stater Bros.": {"name": "Stater Bros.", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q7604016"}, "addTags": {"brand": "Stater Bros.", "brand:wikidata": "Q7604016", "brand:wikipedia": "en:Stater Bros.", "name": "Stater Bros.", "shop": "supermarket"}, "removeTags": {"brand": "Stater Bros.", "brand:wikidata": "Q7604016", "brand:wikipedia": "en:Stater Bros.", "name": "Stater Bros.", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Stokrotka": {"name": "Stokrotka", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q9345945"}, "addTags": {"brand": "Stokrotka", "brand:wikidata": "Q9345945", "brand:wikipedia": "en:Stokrotka", "name": "Stokrotka", "shop": "supermarket"}, "removeTags": {"brand": "Stokrotka", "brand:wikidata": "Q9345945", "brand:wikipedia": "en:Stokrotka", "name": "Stokrotka", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Stop & Shop": {"name": "Stop & Shop", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q3658429"}, "addTags": {"brand": "Stop & Shop", "brand:wikidata": "Q3658429", "brand:wikipedia": "en:Stop & Shop", "name": "Stop & Shop", "shop": "supermarket"}, "removeTags": {"brand": "Stop & Shop", "brand:wikidata": "Q3658429", "brand:wikipedia": "en:Stop & Shop", "name": "Stop & Shop", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, "shop/supermarket/Suma": {"name": "Suma", "icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "supermarket", "brand:wikidata": "Q58012362"}, "addTags": {"brand": "Suma", "brand:wikidata": "Q58012362", "name": "Suma", "shop": "supermarket"}, "removeTags": {"brand": "Suma", "brand:wikidata": "Q58012362", "name": "Suma", "shop": "supermarket"}, "matchScore": 2, "suggestion": true}, diff --git a/dist/locales/en.json b/dist/locales/en.json index 8260d3a4f..3fa139d53 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -7955,24 +7955,48 @@ } } }, + "osm-india-forum": { + "name": "OpenStreetMap India forum", + "description": "OpenStreetMap India web forum" + }, + "osm-india-github": { + "name": "OpenStreetMap India GitHub", + "description": "Code with us: {url}" + }, "OSM-india-mailinglist": { "name": "OpenStreetMap India Mailinglist", "description": "Talk-in is the official Mailinglist for Indian Community" }, - "OSM-India-Puducherry-Mailing-List": { - "name": "Free Software Hardware Movement - Mailing List", - "description": "FSHM Puducherry mailing list to discuss mapping in Puducherry and other things.", - "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap." + "osm-india-telegram": { + "name": "OpenStreetMap India Telegram", + "description": "Join our family: {url}" }, "OSM-india-twitter": { "name": "OpenStreetMap India Twitter", "description": "We are just a tweet away: {url}" }, + "osm-india-website": { + "name": "OpenStreetMap India", + "description": "Mappers and OpenStreetMap users in India" + }, + "osm-india-wiki": { + "name": "OpenStreetMap Wikiproject India", + "description": "Everything you need to know about mapping in India: {url}" + }, + "osm-india-youtube": { + "name": "OpenStreetMap India Youtube", + "description": "Subscribe to our channel: {url}" + }, "OSM-India-Puducherry-Facebook": { "name": "Free Software Hardware Movement - Facebook", "description": "FSHM Facebook page to know about community events, activities", "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap. Its FB page is the best way to keep in contact with its events." }, + "OSM-Puducherry-Mailing-List": { + "name": "Free Software Hardware Movement - Mailing List", + "description": "FSHM Puducherry mailing list to discuss mapping in Puducherry and other things.", + "extendedDescription": "FSHM organizes events relating to free software/hardware, technology, activism and OpenStreetMap." + }, "OSM-India-Puducherry-Matrix": { "name": "Free Software Hardware Movement - Matrix", "description": "FSHM Riot group to discuss, share and update mapping activities, events in and around Puducherry", @@ -8138,11 +8162,6 @@ "name": "Talk-be Mailing List", "description": "Talk-be is the official mailing list for the Belgian OSM community" }, - "be-maptime": { - "name": "Maptime Belgium", - "description": "Social events organized around mapping - beginners most welcome!", - "extendedDescription": "Maptime is an open learning environment for all levels and degrees of knowledge, offering intentional educational support for the beginner. Maptime is simultaneously flexible and structured, creating space for mapping tutorials, workshops, ongoing projects with a shared goal, and independent/collaborative work time." - }, "be-matrix": { "name": "OpenStreetMap BE Matrix channel", "description": "All mappers are welcome!", diff --git a/test/spec/ui/fields/wikipedia.js b/test/spec/ui/fields/wikipedia.js index 8803e10fe..a1a804bc1 100644 --- a/test/spec/ui/fields/wikipedia.js +++ b/test/spec/ui/fields/wikipedia.js @@ -110,7 +110,7 @@ describe('iD.uiFieldWikipedia', function() { expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); }); - it('does not set delayed wikidata tag if graph has changed', function(done) { + it.skip('does not set delayed wikidata tag if graph has changed', function(done) { var wikipedia = iD.uiFieldWikipedia(field, context).entity(entity); wikipedia.on('change', changeTags); selection.call(wikipedia); From 192b4da9e6cec23f3a3457c81e3e503cfe85e630 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 29 Dec 2018 17:30:35 -0500 Subject: [PATCH 058/114] Fix the strings I broke in 3785ffb15 --- data/core.yaml | 82 ++++++++++++++++----------------- dist/locales/en.json | 82 ++++++++++++++++----------------- modules/services/keepRight.js | 2 +- modules/ui/keepRight_details.js | 31 ++++++++----- 4 files changed, 102 insertions(+), 95 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 057ea195d..c9c0e9b5e 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -670,28 +670,28 @@ en: errorTypes: 20: title: 'Multiple nodes on the same spot' - description: 'There is more than one node in this spot. Node IDs: {0}.' + description: 'There is more than one node in this spot. Node IDs: {var1}.' 30: title: 'Non-closed areas' - description: 'This way is tagged with "{0}={1}" and should be a closed loop.' + description: 'This way is tagged with "{var1}={var2}" and should be a closed loop.' 40: title: 'Impossible oneways' - description: 'The first node ({0}) of this oneway is not connected to any other way.' + description: 'The first node ({var1}) of this oneway is not connected to any other way.' 41: - description: 'The last node ({0}) of this oneway is not connected to any other way.' + description: 'The last node ({var1}) of this oneway is not connected to any other way.' 42: description: 'You cannot reach this node because all ways leading from it are oneway.' 43: description: 'You cannot escape from this node because all ways leading to it are oneway.' 50: title: 'Almost-junctions' - description: 'This node is very close but not connected to way {0}.' + description: 'This node is very close but not connected to way {var1}.' 60: title: 'Deprecated tags' - description: 'This {0} uses deprecated tag "{1}={2}". Please use "{3}" instead.' + description: 'This {var1} uses deprecated tag "{var2}={var3}". Please use "{var4}" instead.' 70: title: 'Missing tags' - description: 'This {0} has an empty tag: "{1}".' + description: 'This {var1} has an empty tag: "{var2}".' 71: description: 'This way has no tags.' 72: @@ -699,18 +699,18 @@ en: 73: description: 'This way has a "tracktype" tag but no "highway" tag.' 74: - description: 'This {0} has an empty tag: "{1}".' + description: 'This {var1} has an empty tag: "{var2}".' 75: - description: 'This {0} has a name "{1}" but no other tags.' + description: 'This {var1} has a name "{var2}" but no other tags.' 90: title: 'Motorway without ref tag' description: 'This way is tagged as motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' 100: title: 'Place of worship without religion' - description: 'This {0} is tagged as place of worship and therefore needs a "religion" tag.' + description: 'This {var1} is tagged as place of worship and therefore needs a "religion" tag.' 110: title: 'Point of interest without name' - description: 'This node is tagged as {0} and therefore needs a name tag.' + description: 'This node is tagged as {var1} and therefore needs a name tag.' 120: title: 'Way without nodes' description: 'This way has just one single node.' @@ -725,35 +725,35 @@ en: description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing.' 170: title: 'FIXME tagged items' - description: '{0}' + description: '{var1}' 180: title: 'Relation without type' description: 'This relation is missing a "type" tag.' 190: title: 'Intersection without junctions' - description: 'This {0} intersects the {1} {2} but there is no junction node.' + description: 'This {var1} intersects the {var2} {var3} but there is no junction node.' 200: title: 'Overlapping ways' - description: 'This {0} overlaps the {1} {2}.' + description: 'This {var1} overlaps the {var2} {var3}.' 210: title: 'Self-intersecting ways' description: 'These errors contain self intersecting ways.' 211: - description: 'This way contains more than one node multiple times. Nodes are {0}. This may or may not be an error.' + description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' 212: description: 'This way has only two different nodes and contains one of them more than once.' 220: title: 'Misspelled tag' - description: 'This {0} is tagged "{1}={2}" where "{3}" looks like "{4}".' + description: 'This {var1} is tagged "{var2}={var3}" where "{var4}" looks like "{var5}".' 221: - description: 'This {0} has a tag with key "{1}".' + description: 'This {var1} has a tag with key "{var2}".' 230: title: 'Layer Conflict' description: 'This node is a junction of ways on different layers.' 231: - description: 'This node is a junction of ways on different layers: {0}.' + description: 'This node is a junction of ways on different layers: {var1}.' 232: - description: 'This {0} is tagged with "layer={1}". This need not be an error but it looks strange.' + description: 'This {var1} is tagged with "layer={var2}". This need not be an error but it looks strange.' 270: title: 'Unusual motorway connection' description: 'This node is a junction of a motorway and a highway other than "motorway", "motorway_link", "trunk", "rest_area", or "construction". Connection to "service" or "unclassified" is only valid if it has "access=no/private", or it leads to a motorway service area, or if it is a "service=parking_aisle".' @@ -765,16 +765,16 @@ en: description: 'This boundary has no name.' 282: title: 'Boundary missing admin level' - description: 'The boundary of {0} has no valid numeric admin_level. Please do not mix admin levels (e.g. "6;7"). Always tag the lowest admin_level of all boundaries.' + description: 'The boundary of {var1} has no valid numeric admin_level. Please do not mix admin levels (e.g. "6;7"). Always tag the lowest admin_level of all boundaries.' 283: title: 'Boundary not a closed loop' - description: 'The boundary of {0} is not a closed loop.' + description: 'The boundary of {var1} is not a closed loop.' 284: title: 'Boundary is split' - description: 'The boundary of {0} splits here.' + description: 'The boundary of {var1} splits here.' 285: title: 'Boundary admin_level too high' - description: 'This boundary way has "admin_level={0}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.' + description: 'This boundary way has "admin_level={var1}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.' 290: title: 'Restriction issue' description: 'There is an unspecified issue with this restriction.' @@ -783,25 +783,25 @@ en: description: 'This turn restriction has no known restriction type.' 292: title: 'Restriction missing "from" way' - description: 'A turn restriction needs exactly one "from" member. This one has {0}.' + description: 'A turn restriction needs exactly one "from" member. This one has {var1}.' 293: title: 'Restriction missing "to" way' - description: 'A turn restriction needs exactly one "to" member. This one has {0}.' + description: 'A turn restriction needs exactly one "to" member. This one has {var1}.' 294: title: 'Restriction "from" or "to" is not a way' - description: '"from" and "to" members of turn restrictions need to be ways. {0}.' + description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' 295: title: 'Restriction "via" is not on the way ends' - description: '"via" (node #{0}) is not the first or the last member of "from" (way #{1}).' + description: '"via" (node #{var1}) is not the first or the last member of "from" (way #{var2}).' 296: title: 'Wrong restriction angle' - description: 'Restriction type is "{0}" but angle is {1} degrees. Maybe the restriction type is not appropriate?' + description: 'Restriction type is "{var1}" but angle is {var2} degrees. Maybe the restriction type is not appropriate?' 297: title: 'Wrong direction of to member' - description: 'Wrong direction of "to" way {0}.' + description: 'Wrong direction of "to" way {var1}.' 298: title: 'Redundant restriction - oneway' - description: 'Entry already prohibited by "oneway" tag on {0}.' + description: 'Entry already prohibited by "oneway" tag on {var1}.' 300: title: 'Missing maxspeed' description: 'Missing maxspeed tag.' @@ -813,25 +813,25 @@ en: description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' 312: title: 'Roundabout wrong direction' - description: 'If this {0} is in a country with {1}-hand traffic then its orientation goes the wrong way around' + description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around' 313: title: 'Roundabout weakly connected' - description: 'This roundabout has only {0} other road(s) connected. Roundabouts typically have 3 or more.' + description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' 320: title: 'Improper link connection' - description: 'This way is tagged as "highway={0}_link" but doesn''t have a connection to any other "{1}" or "{2}_link".' + description: 'This way is tagged as "highway={var1}_link" but doesn''t have a connection to any other "{var2}" or "{var3}_link".' 350: title: 'Improper bridge tags' - description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {0}.' + description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' 360: title: 'Language unknown' - description: 'It would be nice if this {0} had an additional tag "name:XX"="{1}" where XX shows the language of its name "{1}".' + description: 'It would be nice if this {var1} had an additional tag "name:XX"="{var2}" where XX shows the language of its name "{var2}".' 370: title: 'Doubled places' - description: 'This node has tags in common with the surrounding way #{0} {1} and seems to be redundant.' + description: 'This node has tags in common with the surrounding way #{var1} {var2} and seems to be redundant.' 380: title: 'Non-physical use of sport tag' - description: 'This way is tagged "sport={0}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' + description: 'This way is tagged "sport={var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' 390: title: 'Missing tracktype' description: This track doesn't have a "tracktype" tag. @@ -840,7 +840,7 @@ en: description: 'There is an unspecified issue with the geometry here.' 401: title: 'Missing turn restriction' - description: 'Ways {0} and {1} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {2} to {3}.' + description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.' 402: title: 'Impossible angles' description: 'This way bends in a very sharp angle here.' @@ -848,11 +848,11 @@ en: title: 'Website issue' description: 'There is an unspecified issue with a contact website or URL.' 411: - description: 'The URL cannot be opened (HTTP status code {0}).' + description: 'The URL cannot be opened (HTTP status code {var1}).' 412: - description: 'Possible domain squatting: The URL has suspicious text: "{0}".' + description: 'Possible domain squatting: The URL has suspicious text: "{var1}".' 413: - description: 'Possible non-match. Content of the URL did not contain these keywords: ({0}).' + description: 'Possible non-match. Content of the URL did not contain these keywords: ({var1}).' streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/dist/locales/en.json b/dist/locales/en.json index 9d706e4ef..89cb0b5c4 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -811,18 +811,18 @@ "errorTypes": { "20": { "title": "Multiple nodes on the same spot", - "description": "There is more than one node in this spot. Node IDs: {0}." + "description": "There is more than one node in this spot. Node IDs: {var1}." }, "30": { "title": "Non-closed areas", - "description": "This way is tagged with \"{0}={1}\" and should be a closed loop." + "description": "This way is tagged with \"{var1}={var2}\" and should be a closed loop." }, "40": { "title": "Impossible oneways", - "description": "The first node ({0}) of this oneway is not connected to any other way." + "description": "The first node ({var1}) of this oneway is not connected to any other way." }, "41": { - "description": "The last node ({0}) of this oneway is not connected to any other way." + "description": "The last node ({var1}) of this oneway is not connected to any other way." }, "42": { "description": "You cannot reach this node because all ways leading from it are oneway." @@ -832,15 +832,15 @@ }, "50": { "title": "Almost-junctions", - "description": "This node is very close but not connected to way {0}." + "description": "This node is very close but not connected to way {var1}." }, "60": { "title": "Deprecated tags", - "description": "This {0} uses deprecated tag \"{1}={2}\". Please use \"{3}\" instead." + "description": "This {var1} uses deprecated tag \"{var2}={var3}\". Please use \"{var4}\" instead." }, "70": { "title": "Missing tags", - "description": "This {0} has an empty tag: \"{1}\"." + "description": "This {var1} has an empty tag: \"{var2}\"." }, "71": { "description": "This way has no tags." @@ -852,10 +852,10 @@ "description": "This way has a \"tracktype\" tag but no \"highway\" tag." }, "74": { - "description": "This {0} has an empty tag: \"{1}\"." + "description": "This {var1} has an empty tag: \"{var2}\"." }, "75": { - "description": "This {0} has a name \"{1}\" but no other tags." + "description": "This {var1} has a name \"{var2}\" but no other tags." }, "90": { "title": "Motorway without ref tag", @@ -863,11 +863,11 @@ }, "100": { "title": "Place of worship without religion", - "description": "This {0} is tagged as place of worship and therefore needs a \"religion\" tag." + "description": "This {var1} is tagged as place of worship and therefore needs a \"religion\" tag." }, "110": { "title": "Point of interest without name", - "description": "This node is tagged as {0} and therefore needs a name tag." + "description": "This node is tagged as {var1} and therefore needs a name tag." }, "120": { "title": "Way without nodes", @@ -887,7 +887,7 @@ }, "170": { "title": "FIXME tagged items", - "description": "{0}" + "description": "{var1}" }, "180": { "title": "Relation without type", @@ -895,38 +895,38 @@ }, "190": { "title": "Intersection without junctions", - "description": "This {0} intersects the {1} {2} but there is no junction node." + "description": "This {var1} intersects the {var2} {var3} but there is no junction node." }, "200": { "title": "Overlapping ways", - "description": "This {0} overlaps the {1} {2}." + "description": "This {var1} overlaps the {var2} {var3}." }, "210": { "title": "Self-intersecting ways", "description": "These errors contain self intersecting ways." }, "211": { - "description": "This way contains more than one node multiple times. Nodes are {0}. This may or may not be an error." + "description": "This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error." }, "212": { "description": "This way has only two different nodes and contains one of them more than once." }, "220": { "title": "Misspelled tag", - "description": "This {0} is tagged \"{1}={2}\" where \"{3}\" looks like \"{4}\"." + "description": "This {var1} is tagged \"{var2}={var3}\" where \"{var4}\" looks like \"{var5}\"." }, "221": { - "description": "This {0} has a tag with key \"{1}\"." + "description": "This {var1} has a tag with key \"{var2}\"." }, "230": { "title": "Layer Conflict", "description": "This node is a junction of ways on different layers." }, "231": { - "description": "This node is a junction of ways on different layers: {0}." + "description": "This node is a junction of ways on different layers: {var1}." }, "232": { - "description": "This {0} is tagged with \"layer={1}\". This need not be an error but it looks strange." + "description": "This {var1} is tagged with \"layer={var2}\". This need not be an error but it looks strange." }, "270": { "title": "Unusual motorway connection", @@ -942,19 +942,19 @@ }, "282": { "title": "Boundary missing admin level", - "description": "The boundary of {0} has no valid numeric admin_level. Please do not mix admin levels (e.g. \"6;7\"). Always tag the lowest admin_level of all boundaries." + "description": "The boundary of {var1} has no valid numeric admin_level. Please do not mix admin levels (e.g. \"6;7\"). Always tag the lowest admin_level of all boundaries." }, "283": { "title": "Boundary not a closed loop", - "description": "The boundary of {0} is not a closed loop." + "description": "The boundary of {var1} is not a closed loop." }, "284": { "title": "Boundary is split", - "description": "The boundary of {0} splits here." + "description": "The boundary of {var1} splits here." }, "285": { "title": "Boundary admin_level too high", - "description": "This boundary way has \"admin_level={0}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations." + "description": "This boundary way has \"admin_level={var1}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations." }, "290": { "title": "Restriction issue", @@ -966,31 +966,31 @@ }, "292": { "title": "Restriction missing \"from\" way", - "description": "A turn restriction needs exactly one \"from\" member. This one has {0}." + "description": "A turn restriction needs exactly one \"from\" member. This one has {var1}." }, "293": { "title": "Restriction missing \"to\" way", - "description": "A turn restriction needs exactly one \"to\" member. This one has {0}." + "description": "A turn restriction needs exactly one \"to\" member. This one has {var1}." }, "294": { "title": "Restriction \"from\" or \"to\" is not a way", - "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {0}." + "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {var1}." }, "295": { "title": "Restriction \"via\" is not on the way ends", - "description": "\"via\" (node #{0}) is not the first or the last member of \"from\" (way #{1})." + "description": "\"via\" (node #{var1}) is not the first or the last member of \"from\" (way #{var2})." }, "296": { "title": "Wrong restriction angle", - "description": "Restriction type is \"{0}\" but angle is {1} degrees. Maybe the restriction type is not appropriate?" + "description": "Restriction type is \"{var1}\" but angle is {var2} degrees. Maybe the restriction type is not appropriate?" }, "297": { "title": "Wrong direction of to member", - "description": "Wrong direction of \"to\" way {0}." + "description": "Wrong direction of \"to\" way {var1}." }, "298": { "title": "Redundant restriction - oneway", - "description": "Entry already prohibited by \"oneway\" tag on {0}." + "description": "Entry already prohibited by \"oneway\" tag on {var1}." }, "300": { "title": "Missing maxspeed", @@ -1006,31 +1006,31 @@ }, "312": { "title": "Roundabout wrong direction", - "description": "If this {0} is in a country with {1}-hand traffic then its orientation goes the wrong way around" + "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around" }, "313": { "title": "Roundabout weakly connected", - "description": "This roundabout has only {0} other road(s) connected. Roundabouts typically have 3 or more." + "description": "This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more." }, "320": { "title": "Improper link connection", - "description": "This way is tagged as \"highway={0}_link\" but doesn't have a connection to any other \"{1}\" or \"{2}_link\"." + "description": "This way is tagged as \"highway={var1}_link\" but doesn't have a connection to any other \"{var2}\" or \"{var3}_link\"." }, "350": { "title": "Improper bridge tags", - "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {0}." + "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." }, "360": { "title": "Language unknown", - "description": "It would be nice if this {0} had an additional tag \"name:XX\"=\"{1}\" where XX shows the language of its name \"{1}\"." + "description": "It would be nice if this {var1} had an additional tag \"name:XX\"=\"{var2}\" where XX shows the language of its name \"{var2}\"." }, "370": { "title": "Doubled places", - "description": "This node has tags in common with the surrounding way #{0} {1} and seems to be redundant." + "description": "This node has tags in common with the surrounding way #{var1} {var2} and seems to be redundant." }, "380": { "title": "Non-physical use of sport tag", - "description": "This way is tagged \"sport={0}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." + "description": "This way is tagged \"sport={var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." }, "390": { "title": "Missing tracktype", @@ -1042,7 +1042,7 @@ }, "401": { "title": "Missing turn restriction", - "description": "Ways {0} and {1} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {2} to {3}." + "description": "Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}." }, "402": { "title": "Impossible angles", @@ -1053,13 +1053,13 @@ "description": "There is an unspecified issue with a contact website or URL." }, "411": { - "description": "The URL cannot be opened (HTTP status code {0})." + "description": "The URL cannot be opened (HTTP status code {var1})." }, "412": { - "description": "Possible domain squatting: The URL has suspicious text: \"{0}\"." + "description": "Possible domain squatting: The URL has suspicious text: \"{var1}\"." }, "413": { - "description": "Possible non-match. Content of the URL did not contain these keywords: ({0})." + "description": "Possible non-match. Content of the URL did not contain these keywords: ({var1})." } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index b697dc51d..30719bf1d 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -103,7 +103,7 @@ function tokenReplacements(d) { group = '\\' + group + '\\'; } - replacements[i-1] = group; + replacements['var' + i] = group; } return replacements; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index d4475663f..1b9b29981 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,5 +1,6 @@ import { event as d3_event } from 'd3-selection'; +import { dataEn } from '../../data'; import { errorTypes } from '../../data/keepRight.json'; import { t } from '../util/locale'; @@ -47,13 +48,16 @@ export function uiKeepRightDetails(context) { titleEnter .append('div') .text(function() { - var result; - try { - result = t(stringBase + errorType + '.title'); - } catch (e) { - result = t(stringBase + parentErrorType + '.title'); + var et = dataEn.QA.keepRight.errorTypes[errorType]; + var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; + + if (et && et.title) { + return t(stringBase + errorType + '.title'); + } else if (pt && pt.title) { + return t(stringBase + parentErrorType + '.title'); + } else { + return t('inspector.unknown'); } - return result; }); @@ -70,13 +74,16 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details-description-text') .html(function(d) { - var result; - try { - result = t(stringBase + errorType + '.description', d.replacements); - } catch (e) { - result = t(stringBase + parentErrorType + '.description'); + var et = dataEn.QA.keepRight.errorTypes[errorType]; + var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; + + if (et && et.description) { + return t(stringBase + errorType + '.description', d.replacements); + } else if (pt && pt.description) { + return t(stringBase + parentErrorType + '.description', d.replacements); + } else { + return t('inspector.unknown'); } - return result; }); description.selectAll('.kr_error_description-id') From d54d5bfd89e253ced46b727f25d12811fab5f21f Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 29 Dec 2018 17:55:40 -0500 Subject: [PATCH 059/114] Move title into keepright error header --- data/core.yaml | 10 +++--- dist/locales/en.json | 10 +++--- modules/ui/keepRight_details.js | 63 ++++++++++++--------------------- modules/ui/keepRight_header.js | 39 ++++++++++++++++++-- 4 files changed, 68 insertions(+), 54 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index c9c0e9b5e..14d142bd2 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -676,15 +676,15 @@ en: description: 'This way is tagged with "{var1}={var2}" and should be a closed loop.' 40: title: 'Impossible oneways' - description: 'The first node ({var1}) of this oneway is not connected to any other way.' + description: 'The first node {var1} of this oneway is not connected to any other way.' 41: - description: 'The last node ({var1}) of this oneway is not connected to any other way.' + description: 'The last node {var1} of this oneway is not connected to any other way.' 42: description: 'You cannot reach this node because all ways leading from it are oneway.' 43: description: 'You cannot escape from this node because all ways leading to it are oneway.' 50: - title: 'Almost-junctions' + title: 'Almost junctions' description: 'This node is very close but not connected to way {var1}.' 60: title: 'Deprecated tags' @@ -792,7 +792,7 @@ en: description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' 295: title: 'Restriction "via" is not on the way ends' - description: '"via" (node #{var1}) is not the first or the last member of "from" (way #{var2}).' + description: '"via" (node {var1}) is not the first or the last member of "from" (way {var2}).' 296: title: 'Wrong restriction angle' description: 'Restriction type is "{var1}" but angle is {var2} degrees. Maybe the restriction type is not appropriate?' @@ -828,7 +828,7 @@ en: description: 'It would be nice if this {var1} had an additional tag "name:XX"="{var2}" where XX shows the language of its name "{var2}".' 370: title: 'Doubled places' - description: 'This node has tags in common with the surrounding way #{var1} {var2} and seems to be redundant.' + description: 'This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant.' 380: title: 'Non-physical use of sport tag' description: 'This way is tagged "sport={var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' diff --git a/dist/locales/en.json b/dist/locales/en.json index 89cb0b5c4..6b26bf07a 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -819,10 +819,10 @@ }, "40": { "title": "Impossible oneways", - "description": "The first node ({var1}) of this oneway is not connected to any other way." + "description": "The first node {var1} of this oneway is not connected to any other way." }, "41": { - "description": "The last node ({var1}) of this oneway is not connected to any other way." + "description": "The last node {var1} of this oneway is not connected to any other way." }, "42": { "description": "You cannot reach this node because all ways leading from it are oneway." @@ -831,7 +831,7 @@ "description": "You cannot escape from this node because all ways leading to it are oneway." }, "50": { - "title": "Almost-junctions", + "title": "Almost junctions", "description": "This node is very close but not connected to way {var1}." }, "60": { @@ -978,7 +978,7 @@ }, "295": { "title": "Restriction \"via\" is not on the way ends", - "description": "\"via\" (node #{var1}) is not the first or the last member of \"from\" (way #{var2})." + "description": "\"via\" (node {var1}) is not the first or the last member of \"from\" (way {var2})." }, "296": { "title": "Wrong restriction angle", @@ -1026,7 +1026,7 @@ }, "370": { "title": "Doubled places", - "description": "This node has tags in common with the surrounding way #{var1} {var2} and seems to be redundant." + "description": "This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant." }, "380": { "title": "Non-physical use of sport tag", diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 1b9b29981..a807b452f 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -6,24 +6,39 @@ import { t } from '../util/locale'; export function uiKeepRightDetails(context) { - var stringBase = 'QA.keepRight.errorTypes.'; var _error; - function keepRightDetails(selection) { - if (!_error) return; - var errorType = _error.error_type; + function errorDetail(d) { + var unknown = t('inspector.unknown'); + + if (!d) return unknown; + var errorType = d.error_type; + var template = errorTypes[errorType]; - if (!template) return; + if (!template) return unknown; // if there is a parent, save its error type e.g.: // Error 191 = "highway-highway" // Error 190 = "intersections without junctions" (parent) var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); var parentTemplate = errorTypes[parentErrorType]; - if (!parentTemplate) return; + if (!parentTemplate) return unknown; + + var et = dataEn.QA.keepRight.errorTypes[errorType]; + var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; + + if (et && et.description) { + return t('QA.keepRight.errorTypes.' + errorType + '.description', d.replacements); + } else if (pt && pt.description) { + return t('QA.keepRight.errorTypes.' + parentErrorType + '.description', d.replacements); + } else { + return unknown; + } + } + function keepRightDetails(selection) { var details = selection.selectAll('.kr_error-details') .data( (_error ? [_error] : []), @@ -37,29 +52,6 @@ export function uiKeepRightDetails(context) { .append('div') .attr('class', 'kr_error-details kr_error-details-container'); - var titleEnter = detailsEnter - .append('div') - .attr('class', 'kr_error-details-title'); - - titleEnter - .append('h4') - .text(function() { return t('QA.keepRight.detail_title'); }); - - titleEnter - .append('div') - .text(function() { - var et = dataEn.QA.keepRight.errorTypes[errorType]; - var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; - - if (et && et.title) { - return t(stringBase + errorType + '.title'); - } else if (pt && pt.title) { - return t(stringBase + parentErrorType + '.title'); - } else { - return t('inspector.unknown'); - } - }); - // description var description = detailsEnter @@ -73,18 +65,7 @@ export function uiKeepRightDetails(context) { description .append('div') .attr('class', 'kr_error-details-description-text') - .html(function(d) { - var et = dataEn.QA.keepRight.errorTypes[errorType]; - var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; - - if (et && et.description) { - return t(stringBase + errorType + '.description', d.replacements); - } else if (pt && pt.description) { - return t(stringBase + parentErrorType + '.description', d.replacements); - } else { - return t('inspector.unknown'); - } - }); + .html(errorDetail); description.selectAll('.kr_error_description-id') .on('click', function() { clickLink(context, this.text); }); diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 878eb588a..261bc0268 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -1,21 +1,54 @@ -import { t } from '../util/locale'; +import { dataEn } from '../../data'; +import { errorTypes } from '../../data/keepRight.json'; import { svgIcon } from '../svg'; +import { t } from '../util/locale'; export function uiKeepRightHeader() { var _error; + function errorTitle(d) { + var unknown = t('inspector.unknown'); + + if (!d) return unknown; + var errorType = d.error_type; + + var template = errorTypes[errorType]; + if (!template) return unknown; + + // if there is a parent, save its error type e.g.: + // Error 191 = "highway-highway" + // Error 190 = "intersections without junctions" (parent) + var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); + var parentTemplate = errorTypes[parentErrorType]; + if (!parentTemplate) return unknown; + + var et = dataEn.QA.keepRight.errorTypes[errorType]; + var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; + + if (et && et.title) { + return t('QA.keepRight.errorTypes.' + errorType + '.title'); + } else if (pt && pt.title) { + return t('QA.keepRight.errorTypes.' + parentErrorType + '.title'); + } else { + return unknown; + } + } + + function keepRightHeader(selection) { var header = selection.selectAll('.kr_error-header') .data( (_error ? [_error] : []), - function(d) { return d.id; } + function(d) { return d.status + d.id; } ); header.exit() .remove(); + + var headerEnter = header.enter() .append('div') .attr('class', 'kr_error-header'); @@ -35,7 +68,7 @@ export function uiKeepRightHeader() { headerEnter .append('div') .attr('class', 'kr_error-header-label') - .text(function(d) { return t('QA.keepRight.entities.' + d.object_type, { id: d.object_id }); }); + .text(errorTitle); } From 4053e3cbbd7c628ea30f9f5c826e374614e8c1b7 Mon Sep 17 00:00:00 2001 From: Manaswini Das Date: Mon, 31 Dec 2018 18:09:05 +0530 Subject: [PATCH 060/114] Improves CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 11c033760..0451a0279 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -431,7 +431,7 @@ Then change to the master branch and get everything from upstream (the main repo If you want to submit Documentation, Spelling improvements, etc. which do not need testing, you can do this with your browser in GitHub. Please don't use this to change Code and create untested Pull Requests. -You also need a GitHub account and may find this [Article about Editing](https://help.github.com/articles/editing-files-in-another-user-s-repository/) and this [Article about Pull Requests](https://help.github.com/articles/about-pull-requests/) usefull. +You also need a GitHub account and may find this [Article about Editing](https://help.github.com/articles/editing-files-in-another-user-s-repository/) and this [Article about Pull Requests](https://help.github.com/articles/about-pull-requests/) useful. ### Step by Step with Browser From f22d1798b72a21c331a815e6792325b0acce0c92 Mon Sep 17 00:00:00 2001 From: David Gilbertson Date: Tue, 1 Jan 2019 14:30:47 +1100 Subject: [PATCH 061/114] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6a84a4131..5a8821728 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ To run the current development version of iD on your own computer: #### Cloning the repository -The repository is reasonably large, and it's unlikely that you need the full history. If you are happy to wait for it all to download, run: +The repository is reasonably large, and it's unlikely that you need the full history (~200 MB). If you are happy to wait for it all to download, run: ``` git clone https://github.com/openstreetmap/iD.git From 2ec02f369f02626cd94c0a47d9ebb698c8fca431 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 2 Jan 2019 11:46:13 -0500 Subject: [PATCH 062/114] Simplify regex matching - goal is to have fewer, simpler rules --- data/core.yaml | 16 +-- data/keepRight.json | 178 +++++++++++++++++----------------- dist/locales/en.json | 16 +-- modules/services/keepRight.js | 38 ++++++-- 4 files changed, 133 insertions(+), 115 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 14d142bd2..749b48fd1 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -704,13 +704,13 @@ en: description: 'This {var1} has a name "{var2}" but no other tags.' 90: title: 'Motorway without ref tag' - description: 'This way is tagged as motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' + description: 'This way is tagged as a motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' 100: title: 'Place of worship without religion' - description: 'This {var1} is tagged as place of worship and therefore needs a "religion" tag.' + description: 'This {var1} is tagged as a place of worship and therefore needs a religion tag.' 110: title: 'Point of interest without name' - description: 'This node is tagged as {var1} and therefore needs a name tag.' + description: 'This node is tagged as a "{var1}" and therefore needs a name tag.' 120: title: 'Way without nodes' description: 'This way has just one single node.' @@ -731,20 +731,20 @@ en: description: 'This relation is missing a "type" tag.' 190: title: 'Intersection without junctions' - description: 'This {var1} intersects the {var2} {var3} but there is no junction node.' + description: 'This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel.' 200: title: 'Overlapping ways' description: 'This {var1} overlaps the {var2} {var3}.' 210: title: 'Self-intersecting ways' - description: 'These errors contain self intersecting ways.' + description: 'There is an unspecified issue with self intersecting ways.' 211: description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' 212: description: 'This way has only two different nodes and contains one of them more than once.' 220: title: 'Misspelled tag' - description: 'This {var1} is tagged "{var2}={var3}" where "{var4}" looks like "{var5}".' + description: 'This {var1} is tagged "{var2}" where "{var3}" looks like "{var4}".' 221: description: 'This {var1} has a tag with key "{var2}".' 230: @@ -840,7 +840,7 @@ en: description: 'There is an unspecified issue with the geometry here.' 401: title: 'Missing turn restriction' - description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}.' + description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning.' 402: title: 'Impossible angles' description: 'This way bends in a very sharp angle here.' @@ -848,7 +848,7 @@ en: title: 'Website issue' description: 'There is an unspecified issue with a contact website or URL.' 411: - description: 'The URL cannot be opened (HTTP status code {var1}).' + description: 'The URL {var1} cannot be opened (HTTP status code {var2}).' 412: description: 'Possible domain squatting: The URL has suspicious text: "{var1}".' 413: diff --git a/data/keepRight.json b/data/keepRight.json index 6150e39c4..b598ecb74 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -3,29 +3,29 @@ "20": { "title": "multiple nodes on the same spot", "severity": "warning", - "description": "There is more than one node in this spot\\. Offending node IDs: ((?:#\\d+,?)+)", + "description": "There is more than one node in this spot. Offending node IDs: $1", "IDs": ["20"], - "regex": true + "regex": "IDs: ((?:#\\d+,?)+)" }, "30": { "title": "non-closed_areas", "severity": "error", - "description": "This way is tagged with ''([\\w:]+)=(\\w+)''and should be closed-loop", - "regex": true + "description": "This way is tagged with '$1=$2' and should be closed-loop.", + "regex": "'([\\w:]+)=(\\w+)'" }, "40": { "title": "dead-ended one-ways", "severity": "error", - "description": "The first node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "description": "The first node (id $1) of this one-way is not connected to any other way", "IDs": ["n"], - "regex": true + "regex": "\\(id (\\d+)\\)" }, "41": { "title": "", "severity": "error", - "description": "The last node \\(id (\\d+)\\) of this one-way is not connected to any other way", + "description": "The last node (id $1) of this one-way is not connected to any other way", "IDs": ["n"], - "regex": true + "regex": "\\(id (\\d+)\\)" }, "42": { "title": "", @@ -40,15 +40,15 @@ "50": { "title": "almost-junctions", "severity": "error", - "description": "This node is very close but not connected to way #(\\d+)", + "description": "This node is very close but not connected to way #$1", "IDs": ["w"], - "regex": true + "regex": "way #(\\d+)" }, "60": { "title": "depreciated tags", "severity": "warning", - "description": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead!", - "regex": true + "description": "This $1 uses deprecated tag $2 = $3. Please use $4 instead!", + "regex": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead" }, "70": { "title": "missing tags", @@ -73,12 +73,12 @@ "74": { "title": "missing tags", "severity": "error", - "description": "This (node|way|relation) has an empty tag: "([\\w:]+)="", - "regex": true + "description": "This $1 has an empty tag: $2", + "regex": "This (node|way|relation) has an empty tag: "([\\w:]+)="" }, "75": { "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", - "regex": true + "regex": "This (node|way|relation) has a name \\((.+)\\) but no other tag" }, "90": { "title": "motorways without ref", @@ -88,14 +88,14 @@ "100": { "title": "places of worship without religion", "severity": "error", - "description": "This (node|way|relation) is tagged as place of worship and therefore needs a religion tag", - "regex": true + "description": "This $1 is tagged as place of worship and therefore needs a religion tag", + "regex": "This (node|way|relation) is" }, "110": { "title": "point of interest without name", "severity": "error", - "description": "This node is tagged as ([\\w:]+) and therefore needs a name tag", - "regex": true + "description": "This node is tagged as $1 and therefore needs a name tag", + "regex": "as ([\\w:]+) and" }, "120": { "title": "ways without nodes", @@ -121,7 +121,7 @@ "title": "FIXME tagged items", "severity": "error", "description": "(.*)", - "regex": true + "regex": "(.*)" }, "180": { "title": "relations without type", @@ -138,56 +138,56 @@ "severity": "error", "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", "IDs": ["", "", "w"], - "regex": true + "regex": "This (highway) intersects the (highway) #(\\d+) but" }, "192": { "title": "highway-waterway", "severity": "error", "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", "IDs": ["", "", "w"], - "regex": true + "regex": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)" }, "193": { "title": "highway-riverbank", "severity": "error", "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", "IDs": ["", "", "w"], - "regex": true + "regex": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)" }, "194": { "title": "waterway-waterway", "severity": "error", "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", "IDs": ["", "","w"], - "regex": true + "regex": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node" }, "195": { "title": "cycleway-cycleway", "severity": "error", "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", "IDs": ["", "","w"], - "regex": true + "regex": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node" }, "196": { "title": "highway-cycleway", "severity": "error", "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", "IDs": ["", "","w"], - "regex": true + "regex": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node" }, "197": { "title": "cycleway-waterway", "severity": "error", "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)" }, "198": { "title": "cycleway-riverbank", "severity": "error", "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)" }, "200": { "title": "overlapping ways", @@ -199,56 +199,56 @@ "severity": "error", "description": "This (highway) overlaps the (highway) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (highway) overlaps the (highway) #(\\d+)" }, "202": { "title": "highway-waterway", "severity": "error", "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)" }, "203": { "title": "highway-riverbank", "severity": "error", "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)" }, "204": { "title": "waterway-waterway", "severity": "error", "description": "This (waterway) overlaps the (waterway) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (waterway) overlaps the (waterway) #(\\d+)" }, "205": { "title": "cycleway-cycleway", "severity": "error", "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)" }, "206": { "title": "highway-cycleway", "severity": "error", "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)" }, "207": { "title": "cycleway-waterway", "severity": "error", "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)" }, "208": { "title": "cycleway-riverbank", "severity": "error", "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", "IDs": ["", "","w"], - "regex": true + "regex": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)" }, "210": { "title": "loopings", @@ -258,9 +258,9 @@ "211": { "title": "", "severity": "error", - "description": "This way contains more than one node at least twice\\. Nodes are ((?:#\\d+(?:, )?)+)\\. This may or may not be an error", + "description": "This way contains more than one node at least twice. Nodes are $1.", "IDs": ["211"], - "regex": true + "regex": "Nodes are ((?:#\\d+(?:, )?)+)\\." }, "212": { "title": "", @@ -270,14 +270,14 @@ "220": { "title": "misspelled tags", "severity": "error", - "description": "This (node|way|relation) is tagged '([\\w]+)(:([\\w]+))?=(.+)' where "(\\2|\\3|\\4|\\5)" looks like "([\\w\\s]+)"", - "regex": true + "description": "This $1 is tagged '$2' where $3 looks like $4", + "regex": "This (node|way|relation) is tagged '(.+)' where "(.+)" looks like "(.+)"" }, "221": { "title": "", "severity": "error", - "description": "The key of this (node|way|relation)''s tag is ''key'': key=(.+)", - "regex": true + "description": "The key of this $1's tag is 'key': $2", + "regex": "this (node|way|relation)''s tag is ''key'': key=(.+)" }, "230": { "title": "layer conflicts", @@ -287,15 +287,15 @@ "231": { "title": "mixed layers intersection", "severity": "error", - "description": "This node is a junction of ways on different layers: ((?:#\\d+\\(-?\\d+\\),?)+)", + "description": "This node is a junction of ways on different layers: $1", "IDs": ["231"], - "regex": true + "regex": "layers: ((?:#\\d+\\(-?\\d+\\),?)+)" }, "232": { "title": "strange layers", "severity": "error", - "description": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\. This need not be an error but it looks strange", - "regex": true + "description": "This $1 is tagged with layer $2. This need not be an error, but it looks strange", + "regex": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\." }, "270": { "title": "motorways connected directly", @@ -315,26 +315,26 @@ "282": { "title": "missing admin level", "severity": "error", - "description": "The boundary of (.+) has no (?:valid numeric )?admin_level\\..*", - "regex": true + "description": "The boundary of $1 has no (?:valid numeric)?admin_level", + "regex": "of (.+) has" }, "283": { "title": "no closed loop", "severity": "error", - "description": "The boundary of (.+) is not closed-loop", - "regex": true + "description": "The boundary of $1 is not closed-loop", + "regex": "boundary of (.+)" }, "284": { "title": "splitting boundary", "severity": "error", - "description": "The boundary of (.+) splits here", - "regex": true + "description": "The boundary of $1 splits here", + "regex": "boundary of (.+)" }, "285": { "title": "admin_level too high", "severity": "error", - "description": "This boundary-way has admin_level (-?\\d+) but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", - "regex": true + "description": "This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", + "regex": "admin_level (-?\\d+) but" }, "290": { "title": "restrictions", @@ -345,53 +345,53 @@ "title": "missing type", "severity": "error", "description": "This turn-restriction has no (?:known )?restriction type", - "regex": true + "regex": "This turn-restriction has no (?:known )?restriction type" }, "292": { "title": "missing from way", "severity": "error", - "description": "A turn-restriction needs exactly one from member\\. This one has (\\d+)", - "regex": true + "description": "A turn-restriction needs exactly one from member. This one has $1", + "regex": "has (\\d+)" }, "293": { "title": "missing to way", "severity": "error", - "description": "A turn-restriction needs exactly one to member\\. This one has (\\d+)", - "regex": true + "description": "A turn-restriction needs exactly one to member. This one has $1", + "regex": "has (\\d+)" }, "294": { "title": "from or to not a way", "severity": "error", - "description": "From- and To-members of turn restrictions need to be ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)", + "description": "From- and To-members of turn restrictions need to be ways. $1", "IDs": ["294"], - "regex": true + "regex": "ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)" }, "295": { "title": "via is not on the way ends", "severity": "error", - "description": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)", + "description": "via (node #$1) is not the first or the last member of from (way #$2)", "IDs": ["n","w"], - "regex": true + "regex": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)" }, "296": { "title": "wrong restriction angle", "severity": "error", - "description": "restriction type is (\\w+) but angle is (\\d+) degrees. Maybe the restriction type is not appropriate?", - "regex": true + "description": "restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?", + "regex": "is (\\w+), but angle is (-?\\d+) degrees" }, "297": { "title": "wrong direction of to member", "severity": "error", - "description": "wrong direction of to way (\\d+)", + "description": "wrong direction of to way $1", "IDs": ["w"], - "regex": true + "regex": "way (\\d+)" }, "298": { "title": "already restricted by oneway", "severity": "error", - "description": "entry already prohibited by oneway tag on (\\d+)", + "description": "entry already prohibited by oneway tag on $1", "IDs": ["w"], - "regex": true + "regex": "on (\\d+)" }, "300": { "title": "missing maxspeed", @@ -412,45 +412,45 @@ "title": "wrong direction", "severity": "error", "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", - "regex": true + "regex": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around" }, "313": { "title": "faintly connected", "severity": "error", - "description": "This roundabout has only (\\d) other roads connected. Roundabouts typically have three", - "regex": true + "description": "This roundabout has only $1 other roads connected. Roundabouts typically have three", + "regex": "only (\\d) other" }, "320": { "title": "*_link connections", "severity": "error", - "description": "This way is tagged as highway=(\\w+)_link but doesn't have a connection to any other \\1 or \\1_link", - "regex": true + "description": "This way is tagged as highway=$1_link but doesn't have a connection to any other $1 or $1_link", + "regex": "highway=(\\w+)_link" }, "350": { "title": "bridge-tags", "severity": "error", "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...", - "regex": true + "regex": "these tags: (.+)" }, "360": { "title": "language unknown", "severity": "warning", "description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'", - "regex": true + "regex": "this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'" }, "370": { "title": "doubled places", "severity": "error", - "description": "This node has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and seems to be redundand", + "description": "This node has tags in common with the surrounding way #$1 ((?:\\(including the name '.+'\\) )?)and seems to be redundand", "IDs": ["w"], - "regex": true + "regex": "way #(\\d+) ((?:\\(including the name '.+'\\) )?)and" }, "380": { "title": "non-physical use of sport-tag", "severity": "error", - "description": "This way is tagged sport=(\\w+) but has no physical tag like e.g. leisure, building, amenity or highway", - "regex": true + "description": "This way is tagged sport=$1 but has no physical tag like e.g. leisure, building, amenity or highway", + "regex": "sport=(\\w+) but" }, "390": { "title": "missing tracktype", @@ -465,9 +465,9 @@ "401": { "title": "missing turn restriction", "severity": "error", - "description": "ways (\\d+) and (\\d+) join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?", - "IDs": ["w", "w", "w", "w"], - "regex": true + "description": "ways $1 and $2 join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning( from way (\\1|\\2) to (\\1|\\2))?", + "IDs": ["w", "w"], + "regex": "ways (\\d+) and (\\d+) join" }, "402": { "title": "impossible angles", @@ -482,20 +482,20 @@ "411": { "title": "http error", "severity": "error", - "description": "The URL \\(\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)", - "regex": true + "description": "The URL ($1) cannot be opened (HTTP status code $2)", + "regex": "href=(.+)>\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)" }, "412": { "title": "domain hijacking", "severity": "error", - "description": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''", - "regex": true + "description": "Possible domain squatting: $1. Suspicious text is: \"$2\"", + "regex": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''" }, "413": { "title": "non-match", "severity": "error", - "description": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)", - "regex": true + "description": "Content of the URL ($1) did not contain these keywords: ($2)", + "regex": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)" } } } diff --git a/dist/locales/en.json b/dist/locales/en.json index 6b26bf07a..546019cf7 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -859,15 +859,15 @@ }, "90": { "title": "Motorway without ref tag", - "description": "This way is tagged as motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag." + "description": "This way is tagged as a motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag." }, "100": { "title": "Place of worship without religion", - "description": "This {var1} is tagged as place of worship and therefore needs a \"religion\" tag." + "description": "This {var1} is tagged as a place of worship and therefore needs a religion tag." }, "110": { "title": "Point of interest without name", - "description": "This node is tagged as {var1} and therefore needs a name tag." + "description": "This node is tagged as a \"{var1}\" and therefore needs a name tag." }, "120": { "title": "Way without nodes", @@ -895,7 +895,7 @@ }, "190": { "title": "Intersection without junctions", - "description": "This {var1} intersects the {var2} {var3} but there is no junction node." + "description": "This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel." }, "200": { "title": "Overlapping ways", @@ -903,7 +903,7 @@ }, "210": { "title": "Self-intersecting ways", - "description": "These errors contain self intersecting ways." + "description": "There is an unspecified issue with self intersecting ways." }, "211": { "description": "This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error." @@ -913,7 +913,7 @@ }, "220": { "title": "Misspelled tag", - "description": "This {var1} is tagged \"{var2}={var3}\" where \"{var4}\" looks like \"{var5}\"." + "description": "This {var1} is tagged \"{var2}\" where \"{var3}\" looks like \"{var4}\"." }, "221": { "description": "This {var1} has a tag with key \"{var2}\"." @@ -1042,7 +1042,7 @@ }, "401": { "title": "Missing turn restriction", - "description": "Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning from way {var3} to {var4}." + "description": "Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning." }, "402": { "title": "Impossible angles", @@ -1053,7 +1053,7 @@ "description": "There is an unspecified issue with a contact website or URL." }, "411": { - "description": "The URL cannot be opened (HTTP status code {var1})." + "description": "The URL {var1} cannot be opened (HTTP status code {var2})." }, "412": { "description": "Possible domain squatting: The URL has suspicious text: \"{var1}\"." diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 30719bf1d..92e0fc3a7 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -74,19 +74,27 @@ function tokenReplacements(d) { var replacements = {}; var html_re = new RegExp(/<\/[a-z][\s\S]*>/); - var errorTemplate = errorTypes[d.error_type]; - if (!errorTemplate) return; + var errorTemplate = errorTypes[d.which_type]; + if (!errorTemplate) { + /* eslint-disable no-console */ + console.log('No Template: ', d.error_type); + console.log(' ', d.description); + /* eslint-enable no-console */ + return; + } // some descriptions are just fixed text if (!errorTemplate.regex) return; // regex pattern should match description with variable details captured as groups - var errorDescription = d.description; - var errorRegex = new RegExp(errorTemplate.description); - var errorMatch = errorRegex.exec(errorDescription); + var errorRegex = new RegExp(errorTemplate.regex, 'i'); + var errorMatch = errorRegex.exec(d.description); if (!errorMatch) { - // TODO: Remove, for regex dev testing - console.log('Unmatched:', d.error_type, d.description, errorRegex); + /* eslint-disable no-console */ + console.log('Unmatched: ', d.error_type); + console.log(' ', d.description); + console.log(' ', errorRegex); + /* eslint-enable no-console */ return; } @@ -186,8 +194,6 @@ function parseError(group, idType) { return newList.join(', '); } - // TODO: Handle error 401 template addition - // arbitrary node list of form: #ID,#ID,#ID... function parseWarning20(list) { var newList = []; @@ -278,6 +284,16 @@ export default { var loc = feature.geometry.coordinates; var props = feature.properties; + // if there is a parent, save its error type e.g.: + // Error 191 = "highway-highway" + // Error 190 = "intersections without junctions" (parent) + var errorType = props.error_type; + var errorTemplate = errorTypes[errorType]; + var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); + + // try to handle error type directly, fallback to parent error type. + var whichType = errorTemplate ? errorType : parentErrorType; + // - move markers slightly so it doesn't obscure the geometry, // - then move markers away from other coincident markers var coincident = false; @@ -295,7 +311,9 @@ export default { comment: props.comment || null, description: props.description || '', error_id: props.error_id, - error_type: props.error_type, + which_type: whichType, + error_type: errorType, + parent_error_type: parentErrorType, object_id: props.object_id, object_type: props.object_type, schema: props.schema, From 9557c0204a2c024c1561450e799a7c1f0264fd99 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 2 Jan 2019 13:39:21 -0500 Subject: [PATCH 063/114] Relax some more regex for tag parsing, (to support 'key=*') Also implement parsing error 231 - layer conflict --- data/core.yaml | 7 +- data/keepRight.json | 138 ++++---------------------------- dist/locales/en.json | 9 ++- modules/services/keepRight.js | 34 +++----- modules/ui/keepRight_details.js | 11 +-- modules/ui/keepRight_header.js | 11 +-- 6 files changed, 39 insertions(+), 171 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 749b48fd1..699e272d2 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -673,7 +673,7 @@ en: description: 'There is more than one node in this spot. Node IDs: {var1}.' 30: title: 'Non-closed areas' - description: 'This way is tagged with "{var1}={var2}" and should be a closed loop.' + description: 'This way is tagged with "{var1}" and should be a closed loop.' 40: title: 'Impossible oneways' description: 'The first node {var1} of this oneway is not connected to any other way.' @@ -688,7 +688,7 @@ en: description: 'This node is very close but not connected to way {var1}.' 60: title: 'Deprecated tags' - description: 'This {var1} uses deprecated tag "{var2}={var3}". Please use "{var4}" instead.' + description: 'This {var1} uses deprecated tag "{var2}". Please use "{var3}" instead.' 70: title: 'Missing tags' description: 'This {var1} has an empty tag: "{var2}".' @@ -752,6 +752,7 @@ en: description: 'This node is a junction of ways on different layers.' 231: description: 'This node is a junction of ways on different layers: {var1}.' + layer: '(layer: {layer})' 232: description: 'This {var1} is tagged with "layer={var2}". This need not be an error but it looks strange.' 270: @@ -792,7 +793,7 @@ en: description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' 295: title: 'Restriction "via" is not on the way ends' - description: '"via" (node {var1}) is not the first or the last member of "from" (way {var2}).' + description: '"via" (node {var1}) is not the first or the last member of "{var2}" (way {var3}).' 296: title: 'Wrong restriction angle' description: 'Restriction type is "{var1}" but angle is {var2} degrees. Maybe the restriction type is not appropriate?' diff --git a/data/keepRight.json b/data/keepRight.json index b598ecb74..cf9b3fcb7 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -10,8 +10,8 @@ "30": { "title": "non-closed_areas", "severity": "error", - "description": "This way is tagged with '$1=$2' and should be closed-loop.", - "regex": "'([\\w:]+)=(\\w+)'" + "description": "This way is tagged with '$1' and should be closed-loop.", + "regex": "'(.+)'" }, "40": { "title": "dead-ended one-ways", @@ -47,8 +47,8 @@ "60": { "title": "depreciated tags", "severity": "warning", - "description": "This $1 uses deprecated tag $2 = $3. Please use $4 instead!", - "regex": "This (node|way|relation) uses deprecated tag '([\\w:]+)=(.+)'\\. Please use "(.+)" instead" + "description": "This $1 uses deprecated tag $2. Please use $3 instead!", + "regex": "This (node|way|relation) uses deprecated tag '(.+)'\\. Please use "(.+)"" }, "70": { "title": "missing tags", @@ -74,11 +74,11 @@ "title": "missing tags", "severity": "error", "description": "This $1 has an empty tag: $2", - "regex": "This (node|way|relation) has an empty tag: "([\\w:]+)="" + "regex": "This (node|way|relation) has an empty tag: "(.+)="" }, "75": { "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", - "regex": "This (node|way|relation) has a name \\((.+)\\) but no other tag" + "regex": "This (node|way|relation) has a name \\((.+)\\)" }, "90": { "title": "motorways without ref", @@ -95,7 +95,7 @@ "title": "point of interest without name", "severity": "error", "description": "This node is tagged as $1 and therefore needs a name tag", - "regex": "as ([\\w:]+) and" + "regex": "as (.+) and" }, "120": { "title": "ways without nodes", @@ -131,124 +131,16 @@ "190": { "title": "intersections without junctions", "severity": "error", - "description": "Finds way crossings on same layer without common node as a junction" - }, - "191": { - "title": "highway-highway", - "severity": "error", - "description": "This (highway) intersects the (highway) #(\\d+) but there is no junction node", + "description": "This $1 intersects the $2 #$3 but there is no junction node", "IDs": ["", "", "w"], - "regex": "This (highway) intersects the (highway) #(\\d+) but" - }, - "192": { - "title": "highway-waterway", - "severity": "error", - "description": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)", - "IDs": ["", "", "w"], - "regex": "This (highway|waterway) intersects the (highway|waterway) #(\\d+)" - }, - "193": { - "title": "highway-riverbank", - "severity": "error", - "description": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)", - "IDs": ["", "", "w"], - "regex": "This (highway|riverbank) intersects the (highway|riverbank) #(\\d+)" - }, - "194": { - "title": "waterway-waterway", - "severity": "error", - "description": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node", - "IDs": ["", "","w"], - "regex": "This (waterway) intersects the (waterway) #(\\d+) but there is no junction node" - }, - "195": { - "title": "cycleway-cycleway", - "severity": "error", - "description": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "","w"], - "regex": "This (cycleway/footpath) intersects the (cycleway/footpath) #(\\d+) but there is no junction node" - }, - "196": { - "title": "highway-cycleway", - "severity": "error", - "description": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node", - "IDs": ["", "","w"], - "regex": "This (highway|cycleway/footpath) intersects the (highway|cycleway/footpath) #(\\d+) but there is no junction node" - }, - "197": { - "title": "cycleway-waterway", - "severity": "error", - "description": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (waterway|cycleway/footpath) intersects the (waterway|cycleway/footpath) #(\\d+)" - }, - "198": { - "title": "cycleway-riverbank", - "severity": "error", - "description": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (riverbank|cycleway/footpath) intersects the (riverbank|cycleway/footpath) #(\\d+)" + "regex": "This (.+) intersects the (.+) #(\\d+)" }, "200": { "title": "overlapping ways", "severity": "error", - "description": "Finds overlapping ways on same layer" - }, - "201": { - "title": "highway-highway", - "severity": "error", - "description": "This (highway) overlaps the (highway) #(\\d+)", "IDs": ["", "","w"], - "regex": "This (highway) overlaps the (highway) #(\\d+)" - }, - "202": { - "title": "highway-waterway", - "severity": "error", - "description": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (highway|waterway) overlaps the (highway|waterway) #(\\d+)" - }, - "203": { - "title": "highway-riverbank", - "severity": "error", - "description": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (highway|riverbank) overlaps the (highway|riverbank) #(\\d+)" - }, - "204": { - "title": "waterway-waterway", - "severity": "error", - "description": "This (waterway) overlaps the (waterway) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (waterway) overlaps the (waterway) #(\\d+)" - }, - "205": { - "title": "cycleway-cycleway", - "severity": "error", - "description": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (cycleway/footpath) overlaps the (cycleway/footpath) #(\\d+)" - }, - "206": { - "title": "highway-cycleway", - "severity": "error", - "description": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (highway|cycleway/footpath) overlaps the (highway|cycleway/footpath) #(\\d+)" - }, - "207": { - "title": "cycleway-waterway", - "severity": "error", - "description": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (waterway|cycleway/footpath) overlaps the (waterway|cycleway/footpath) #(\\d+)" - }, - "208": { - "title": "cycleway-riverbank", - "severity": "error", - "description": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)", - "IDs": ["", "","w"], - "regex": "This (riverbank|cycleway/footpath) overlaps the (riverbank|cycleway/footpath) #(\\d+)" + "description": "This $1 overlaps the $2 #$3", + "regex": "This (.+) overlaps the (.+) #(\\d+)" }, "210": { "title": "loopings", @@ -289,7 +181,7 @@ "severity": "error", "description": "This node is a junction of ways on different layers: $1", "IDs": ["231"], - "regex": "layers: ((?:#\\d+\\(-?\\d+\\),?)+)" + "regex": "layers: (.+)" }, "232": { "title": "strange layers", @@ -369,9 +261,9 @@ "295": { "title": "via is not on the way ends", "severity": "error", - "description": "via (node #$1) is not the first or the last member of from (way #$2)", + "description": "via (node #$1) is not the first or the last member of (from|to) (way #$3)", "IDs": ["n","w"], - "regex": "via \\(node #(\\d+)\\) is not the first or the last member of from \\(way #(\\d+)\\)" + "regex": "via \\(node #(\\d+)\\) is not the first or the last member of (from|to) \\(way #(\\d+)\\)" }, "296": { "title": "wrong restriction angle", @@ -412,7 +304,7 @@ "title": "wrong direction", "severity": "error", "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", - "regex": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around" + "regex": "this ((?:mini_)?roundabout) is in a country with (left|right)-hand" }, "313": { "title": "faintly connected", diff --git a/dist/locales/en.json b/dist/locales/en.json index 546019cf7..d06031da9 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -815,7 +815,7 @@ }, "30": { "title": "Non-closed areas", - "description": "This way is tagged with \"{var1}={var2}\" and should be a closed loop." + "description": "This way is tagged with \"{var1}\" and should be a closed loop." }, "40": { "title": "Impossible oneways", @@ -836,7 +836,7 @@ }, "60": { "title": "Deprecated tags", - "description": "This {var1} uses deprecated tag \"{var2}={var3}\". Please use \"{var4}\" instead." + "description": "This {var1} uses deprecated tag \"{var2}\". Please use \"{var3}\" instead." }, "70": { "title": "Missing tags", @@ -923,7 +923,8 @@ "description": "This node is a junction of ways on different layers." }, "231": { - "description": "This node is a junction of ways on different layers: {var1}." + "description": "This node is a junction of ways on different layers: {var1}.", + "layer": "(layer: {layer})" }, "232": { "description": "This {var1} is tagged with \"layer={var2}\". This need not be an error but it looks strange." @@ -978,7 +979,7 @@ }, "295": { "title": "Restriction \"via\" is not on the way ends", - "description": "\"via\" (node {var1}) is not the first or the last member of \"from\" (way {var2})." + "description": "\"via\" (node {var1}) is not the first or the last member of \"{var2}\" (way {var3})." }, "296": { "title": "Wrong restriction angle", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 92e0fc3a7..0730695ca 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -120,7 +120,7 @@ function tokenReplacements(d) { function parseError(group, idType) { - function fillPlaceholder(d) { + function linkEntity(d) { return '' + d + ''; } @@ -131,7 +131,7 @@ function parseError(group, idType) { items.forEach(function(item) { // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); + var id = linkEntity('n' + item.slice(1)); newList.push(id); }); @@ -141,24 +141,16 @@ function parseError(group, idType) { // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... function parseError231(list) { var newList = []; - var items = list.split(','); + // unfortunately 'layer' can itself contain commas, so we split on '),' + var items = list.split('),'); items.forEach(function(item) { - var id; - var layer; - - // item of form "#ID(layer)" - item = item.split('('); - - // ID has # at the front - id = item[0].slice(1); - id = fillPlaceholder('w' + id); - - // layer has trailing ) - layer = item[1].slice(0,-1); - - // TODO: translation - newList.push(id + ' (layer: ' + layer + ')'); + var match = item.match(/\#(\d+)\((.+)\)?/); + if (match !== null && match.length > 2) { + newList.push(linkEntity('w' + match[1]) + + t('QA.keepRight.errorTypes.231.layer', { layer: match[2] }) + ); + } }); return newList.join(', '); @@ -185,7 +177,7 @@ function parseError(group, idType) { // ID has # at the front id = item[2].slice(1); - id = fillPlaceholder(idType + id); + id = linkEntity(idType + id); item = [role, item[1], id].join(' '); newList.push(item); @@ -201,7 +193,7 @@ function parseError(group, idType) { items.forEach(function(item) { // ID has # at the front - var id = fillPlaceholder('n' + item.slice(1)); + var id = linkEntity('n' + item.slice(1)); newList.push(id); }); @@ -213,7 +205,7 @@ function parseError(group, idType) { case 'n': case 'w': case 'r': - group = fillPlaceholder(idType + group); + group = linkEntity(idType + group); break; // some errors have more complex ID lists/variance case '211': diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index a807b452f..a093ab3f7 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -14,16 +14,7 @@ export function uiKeepRightDetails(context) { if (!d) return unknown; var errorType = d.error_type; - - var template = errorTypes[errorType]; - if (!template) return unknown; - - // if there is a parent, save its error type e.g.: - // Error 191 = "highway-highway" - // Error 190 = "intersections without junctions" (parent) - var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); - var parentTemplate = errorTypes[parentErrorType]; - if (!parentTemplate) return unknown; + var parentErrorType = d.parent_error_type var et = dataEn.QA.keepRight.errorTypes[errorType]; var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 261bc0268..b432c9fcc 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -13,16 +13,7 @@ export function uiKeepRightHeader() { if (!d) return unknown; var errorType = d.error_type; - - var template = errorTypes[errorType]; - if (!template) return unknown; - - // if there is a parent, save its error type e.g.: - // Error 191 = "highway-highway" - // Error 190 = "intersections without junctions" (parent) - var parentErrorType = (Math.floor(errorType / 10) * 10).toString(); - var parentTemplate = errorTypes[parentErrorType]; - if (!parentTemplate) return unknown; + var parentErrorType = d.parent_error_type var et = dataEn.QA.keepRight.errorTypes[errorType]; var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; From 4494c3ab9c4075b21eaba03f426276f5258c8e8b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 2 Jan 2019 15:46:25 -0500 Subject: [PATCH 064/114] Fix comment box style, remove unused vars for eslint --- css/80_app.css | 68 +++++++++++++++------------------ modules/services/keepRight.js | 4 +- modules/svg/keepRight.js | 34 ++--------------- modules/svg/notes.js | 6 +-- modules/ui/keepRight_details.js | 3 +- modules/ui/keepRight_header.js | 3 +- 6 files changed, 39 insertions(+), 79 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 206e48218..5540552eb 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2544,13 +2544,15 @@ input.key-trap { padding: 10px; } -.note-save #new-comment-input { +.keepRight-save .new-comment-input, +.note-save .new-comment-input { width: 100%; height: 100px; max-height: 300px; min-height: 100px; } +.keepRight-save .detail-section, .note-save .detail-section { margin: 10px 0; } @@ -2568,56 +2570,56 @@ input.key-trap { stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */ } -.kr_error_type_40, +.kr_error_type_40, /* impossible oneways */ .kr_error_type_41, .kr_error_type_42, .kr_error_type_43 { color: #fd007f; } -.kr_error_type_50 { +.kr_error_type_50 { /* almost junctions */ color: #c827fe; } -.kr_error_type_70, +.kr_error_type_70, /* missing tags */ .kr_error_type_71, .kr_error_type_72 { color: #74aeaf; } -.kr_error_type_90 { +.kr_error_type_90 { /* motorway without ref */ color: #3124af; } -.kr_error_type_100 { +.kr_error_type_100 { /* place of worship without religion */ color: #a80000; } -.kr_error_type_110 { +.kr_error_type_110 { /* poi without name */ color: #44650b; } -.kr_error_type_120 { +.kr_error_type_120 { /* way without nodes */ color: #99274d; } -.kr_error_type_130 { +.kr_error_type_130 { /* disconnected way */ color: #eb7310; } -.kr_error_type_150 { +.kr_error_type_150 { /* railway crossing without tag */ color: #7218c1; } -.kr_error_type_160 { +.kr_error_type_160 { /* wrong railway tag */ color: #c903ae; } -.kr_error_type_170 { +.kr_error_type_170 { /* FIXME tag */ color: #07d40b; } -.kr_error_type_180 { +.kr_error_type_180 { /* relation without type */ color: #01ff0a; } @@ -2629,7 +2631,7 @@ input.key-trap { .kr_error_type_195, .kr_error_type_196, .kr_error_type_197, -.kr_error_type_198 { +.kr_error_type_198 { /* intersection without junction */ color: #e6fcb0; } @@ -2641,32 +2643,32 @@ input.key-trap { .kr_error_type_205, .kr_error_type_206, .kr_error_type_207, -.kr_error_type_208 { +.kr_error_type_208 { /* overlapping ways */ color: #fdbf6f; } .kr_error_type_210, .kr_error_type_211, -.kr_error_type_212 { +.kr_error_type_212 { /* self intersecting ways */ color: #4a7601; } .kr_error_type_220, -.kr_error_type_221 { +.kr_error_type_221 { /* misspelled tag */ color: #ef7cf2; } .kr_error_type_230, .kr_error_type_231, -.kr_error_type_232 { +.kr_error_type_232 { /* layer conflict */ color: #b15928; } -.kr_error_type_270 { +.kr_error_type_270 { /* unusual motorway connection */ color: #2aaf92; } -.kr_error_type_280, +.kr_error_type_280, /* boundary issue */ .kr_error_type_281, .kr_error_type_282, .kr_error_type_283, @@ -2675,7 +2677,7 @@ input.key-trap { color: #5f47a0; } -.kr_error_type_290, +.kr_error_type_290, /* restriction issue */ .kr_error_type_291, .kr_error_type_292, .kr_error_type_293, @@ -2687,52 +2689,42 @@ input.key-trap { color: #a6cee3; } -.kr_error_type_310, +.kr_error_type_310, /* roundabout issue */ .kr_error_type_311, .kr_error_type_312, .kr_error_type_313 { color: #0550e8; } -.kr_error_type_320 { +.kr_error_type_320 { /* improper _link */ color: #28d9bb; } -.kr_error_type_350 { +.kr_error_type_350 { /* improper bridge tag */ color: #ffff99; } -.kr_error_type_370 { +.kr_error_type_370 { /* doubled places */ color: #ff8fdf; } -.kr_error_type_380 { +.kr_error_type_380 { /* non-physical sport tag */ color: #b3b465; } -.kr_error_type_400, +.kr_error_type_400, /* geometry / turn angles */ .kr_error_type_401, .kr_error_type_402 { color: #b64f69; } -.kr_error_type_410, +.kr_error_type_410, /* website issue */ .kr_error_type_411, .kr_error_type_412, .kr_error_type_413 { color: #b07f7e; } -.kr_error-details-title { - text-align: left; - margin-bottom: 20px; -} - -.kr_error-details-description { - text-align: left; - margin-bottom: 10px; -} - /* Custom Data Editor ------------------------------------------------------- */ diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 0730695ca..35318a370 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -147,8 +147,8 @@ function parseError(group, idType) { items.forEach(function(item) { var match = item.match(/\#(\d+)\((.+)\)?/); if (match !== null && match.length > 2) { - newList.push(linkEntity('w' + match[1]) - + t('QA.keepRight.errorTypes.231.layer', { layer: match[2] }) + newList.push(linkEntity('w' + match[1]) + ' ' + + t('QA.keepRight.errorTypes.231.layer', { layer: match[2] }) ); } }); diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 60fd0bb7c..17b8b2395 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -82,39 +82,13 @@ export function svgKeepRight(projection, context, dispatch) { } - function click(d) { - var service = getService(); - if (!service) return; - - context.map().centerEase(d.loc); - - var selected = service.getSelectedImage(); - var selectedImageKey = selected && selected.key; - var imageKey; - - // Pick one of the images the sign was detected in, - // preference given to an image already selected. - d.detections.forEach(function(detection) { - if (!imageKey || selectedImageKey === detection.image_key) { - imageKey = detection.image_key; - } - }); - - service - .selectImage(null, imageKey) - .updateViewer(imageKey, context) - .showViewer(); - } - - function update() { var service = getService(); - var selectedID = context.selectedNoteID(); // TODO: update with selectedErrorID + var selectedID = context.selectedErrorID(); var data = (service ? service.getErrors(projection) : []); - var visibleData = data; // getVisible(data); // TODO: only show sub-layers that are toggled on var transform = svgPointTransform(projection); var kr_errors = layer.selectAll('.kr_error') - .data(visibleData, function(d) { return d.id; }); + .data(data, function(d) { return d.id; }); // exit kr_errors.exit() @@ -124,8 +98,8 @@ export function svgKeepRight(projection, context, dispatch) { var kr_errorsEnter = kr_errors.enter() .append('g') .attr('class', function(d) { - return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; }) - .classed('new', function(d) { return d.id < 0; }); + return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; } + ); kr_errorsEnter .append('ellipse') diff --git a/modules/svg/notes.js b/modules/svg/notes.js index bc97adb55..9e1d2c1aa 100644 --- a/modules/svg/notes.js +++ b/modules/svg/notes.js @@ -165,14 +165,10 @@ export function svgNotes(projection, context, dispatch) { .style('display', enabled ? 'block' : 'none') .merge(layer); - function dimensions() { - return [window.innerWidth, window.innerHeight]; - } - if (enabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); - service.loadNotes(projection, dimensions()); + service.loadNotes(projection); update(); } else { editOff(); diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index a093ab3f7..1c0602b51 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,7 +1,6 @@ import { event as d3_event } from 'd3-selection'; import { dataEn } from '../../data'; -import { errorTypes } from '../../data/keepRight.json'; import { t } from '../util/locale'; @@ -14,7 +13,7 @@ export function uiKeepRightDetails(context) { if (!d) return unknown; var errorType = d.error_type; - var parentErrorType = d.parent_error_type + var parentErrorType = d.parent_error_type; var et = dataEn.QA.keepRight.errorTypes[errorType]; var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index b432c9fcc..2e14992ef 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -1,5 +1,4 @@ import { dataEn } from '../../data'; -import { errorTypes } from '../../data/keepRight.json'; import { svgIcon } from '../svg'; import { t } from '../util/locale'; @@ -13,7 +12,7 @@ export function uiKeepRightHeader() { if (!d) return unknown; var errorType = d.error_type; - var parentErrorType = d.parent_error_type + var parentErrorType = d.parent_error_type; var et = dataEn.QA.keepRight.errorTypes[errorType]; var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; From ee92758801ce71fa93933e61fab691e54b346b41 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 2 Jan 2019 16:15:21 -0500 Subject: [PATCH 065/114] KeepRight only allows one anonymous comment per issue - simplify --- data/core.yaml | 7 ++--- dist/locales/en.json | 7 ++--- modules/ui/index.js | 1 - modules/ui/keepRight_comment.js | 33 -------------------- modules/ui/keepRight_editor.js | 54 ++++++++++++--------------------- 5 files changed, 23 insertions(+), 79 deletions(-) delete mode 100644 modules/ui/keepRight_comment.js diff --git a/data/core.yaml b/data/core.yaml index 699e272d2..165bded6d 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -652,11 +652,8 @@ en: title: KeepRight Error detail_title: Error detail_description: Description - comment_header: Comment - newInputPlaceholder: Enter a comment to share with other users. - updateInputPlaceholder: Update the comment above to share with other users. - newComment: New Comment - updateComment: Update Comment + comment: Comment + comment_placeholder: Enter a comment to share with other users. upload_explanation: Your comments will be publicly visible on KeepRight. upload_explanation_with_user: "Your comments as {user} will be publicly visible on KeepRight." resolve_comment: Comment and Resolve diff --git a/dist/locales/en.json b/dist/locales/en.json index d06031da9..302354360 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -792,11 +792,8 @@ "title": "KeepRight Error", "detail_title": "Error", "detail_description": "Description", - "comment_header": "Comment", - "newInputPlaceholder": "Enter a comment to share with other users.", - "updateInputPlaceholder": "Update the comment above to share with other users.", - "newComment": "New Comment", - "updateComment": "Update Comment", + "comment": "Comment", + "comment_placeholder": "Enter a comment to share with other users.", "upload_explanation": "Your comments will be publicly visible on KeepRight.", "upload_explanation_with_user": "Your comments as {user} will be publicly visible on KeepRight.", "resolve_comment": "Comment and Resolve", diff --git a/modules/ui/index.js b/modules/ui/index.js index d80ba9117..d92bd0a7d 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -30,7 +30,6 @@ export { uiGeolocate } from './geolocate'; export { uiHelp } from './help'; export { uiInfo } from './info'; export { uiInspector } from './inspector'; -export { uiKeepRightComment } from './keepRight_comment'; export { uiKeepRightDetails } from './keepRight_details'; export { uiKeepRightEditor } from './keepRight_editor'; export { uiKeepRightHeader } from './keepRight_header'; diff --git a/modules/ui/keepRight_comment.js b/modules/ui/keepRight_comment.js deleted file mode 100644 index 516847600..000000000 --- a/modules/ui/keepRight_comment.js +++ /dev/null @@ -1,33 +0,0 @@ -import { t } from '../util/locale'; - - -export function uiKeepRightComment() { - var _error; - - function keepRightComment(selection) { - if (!_error.comment) return; - var comment = selection.selectAll('.comments-container') - .data([0]); - - var comment_details = comment.enter() - .append('div') - .attr('class', 'kr_error-comment-container'); - - comment_details - .append('h4') - .text(t('QA.keepRight.comment_header')); - - comment_details - .append('div') - .text(_error.comment); - } - - keepRightComment.error = function(val) { - if (!arguments.length) return _error; - _error = val; - return keepRightComment; - }; - - - return keepRightComment; -} diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 6194f4b5e..f8b9d7687 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -8,23 +8,12 @@ import { t } from '../util/locale'; import { services } from '../services'; import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; - -import { - uiKeepRightComment, - uiKeepRightDetails, - uiKeepRightHeader, - uiViewOnKeepRight, -} from './index'; - -import { - utilNoAuto, - utilRebind -} from '../util'; +import { uiKeepRightDetails, uiKeepRightHeader, uiViewOnKeepRight } from './index'; +import { utilNoAuto, utilRebind } from '../util'; export function uiKeepRightEditor(context) { var dispatch = d3_dispatch('change'); - var keepRightComment = uiKeepRightComment(); var keepRightDetails = uiKeepRightDetails(context); var keepRightHeader = uiKeepRightHeader(context); @@ -69,8 +58,7 @@ export function uiKeepRightEditor(context) { .merge(editor) .call(keepRightHeader.error(_error)) .call(keepRightDetails.error(_error)) - .call(keepRightComment.error(_error)) - .call(errorSaveSection); + .call(keepRightSaveSection); var footer = selection.selectAll('.footer') @@ -84,44 +72,40 @@ export function uiKeepRightEditor(context) { } - function errorSaveSection(selection) { + function keepRightSaveSection(selection) { var isSelected = (_error && _error.id === context.selectedErrorID()); - var errorSave = selection.selectAll('.error-save') + var saveSection = selection.selectAll('.error-save') .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); // exit - errorSave.exit() + saveSection.exit() .remove(); // enter - var errorSaveEnter = errorSave.enter() + var saveSectionEnter = saveSection.enter() .append('div') .attr('class', 'keepRight-save save-section cf'); - errorSaveEnter + saveSectionEnter .append('h4') .attr('class', '.error-save-header') - .text(function(d) { - return d.comment ? t('QA.keepRight.updateComment') : t('QA.keepRight.newComment'); - }); + .text(t('QA.keepRight.comment')); - errorSaveEnter + saveSectionEnter .append('textarea') .attr('class', 'new-comment-input') - .attr('placeholder', function(d) { - return d.comment ? t('QA.keepRight.updateInputPlaceholder') : t('QA.keepRight.newInputPlaceholder'); - }) + .attr('placeholder', t('QA.keepRight.comment_placeholder')) .attr('maxlength', 1000) - .property('value', function(d) { return d.newComment; }) + .property('value', function(d) { return d.comment; }) .call(utilNoAuto) .on('input', changeInput) .on('blur', changeInput); // update - errorSave = errorSaveEnter - .merge(errorSave) + saveSection = saveSectionEnter + .merge(saveSection) .call(userDetails) - .call(errorSaveButtons); + .call(keepRightSaveButtons); function changeInput() { @@ -129,15 +113,15 @@ export function uiKeepRightEditor(context) { var val = input.property('value').trim() || undefined; // store the unsaved comment with the error itself - _error = _error.update({ newComment: val }); + _error = _error.update({ newComment: val, comment: val }); var keepRight = services.keepRight; if (keepRight) { keepRight.replaceError(_error); // update keepright cache } - errorSave - .call(errorSaveButtons); + saveSection + .call(keepRightSaveButtons); } } @@ -232,7 +216,7 @@ export function uiKeepRightEditor(context) { } - function errorSaveButtons(selection) { + function keepRightSaveButtons(selection) { var osm = services.osm; var hasAuth = osm && osm.authenticated(); From 69ce5ee9643204d15c8f870e7412156b4ed0678d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 00:07:55 -0500 Subject: [PATCH 066/114] Remove user details and auth and adjust button texts for commenting The user details and auth are not needed because KeepRight accepts only anonymous updating --- data/core.yaml | 11 +-- dist/locales/en.json | 11 +-- modules/services/keepRight.js | 12 +-- modules/ui/keepRight_editor.js | 162 ++++++--------------------------- 4 files changed, 43 insertions(+), 153 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 165bded6d..eaf6a52dd 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -654,12 +654,11 @@ en: detail_description: Description comment: Comment comment_placeholder: Enter a comment to share with other users. - upload_explanation: Your comments will be publicly visible on KeepRight. - upload_explanation_with_user: "Your comments as {user} will be publicly visible on KeepRight." - resolve_comment: Comment and Resolve - ignore_comment: Comment and Ignore - resolve: Resolve - ignore: Ignore + close: Close (Error Fixed) + ignore: Ignore (Not an Error) + save_comment: Save Comment + close_comment: Close and Comment + ignore_comment: Ignore and Comment entities: node: "Node {id}" way: "Way {id}" diff --git a/dist/locales/en.json b/dist/locales/en.json index 302354360..65609b06d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -794,12 +794,11 @@ "detail_description": "Description", "comment": "Comment", "comment_placeholder": "Enter a comment to share with other users.", - "upload_explanation": "Your comments will be publicly visible on KeepRight.", - "upload_explanation_with_user": "Your comments as {user} will be publicly visible on KeepRight.", - "resolve_comment": "Comment and Resolve", - "ignore_comment": "Comment and Ignore", - "resolve": "Resolve", - "ignore": "Ignore", + "close": "Close (Error Fixed)", + "ignore": "Ignore (Not an Error)", + "save_comment": "Save Comment", + "close_comment": "Close and Comment", + "ignore_comment": "Ignore and Comment", "entities": { "node": "Node {id}", "way": "Way {id}", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 35318a370..d83a66bb7 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -10,7 +10,6 @@ import { request as d3_request } from 'd3-request'; import { geoExtent, geoVecAdd } from '../geo'; import { krError } from '../osm'; -import { services } from './index'; import { t } from '../util/locale'; import { utilRebind, utilTiler, utilQsString } from '../util'; @@ -326,19 +325,15 @@ export default { postKeepRightUpdate: function(update, callback) { - if (!services.osm.authenticated()) { - return callback({ message: 'Not Authenticated', status: -3 }, update); - } if (_krCache.inflight[update.id]) { - return callback( - { message: 'Error update already inflight', status: -2 }, update); + return callback({ message: 'Error update already inflight', status: -2 }, update); } var path = apibase + 'comment.php?'; if (update.state) { path += '&st=' + update.state; } - if (update.newComment) { + if (update.newComment !== undefined) { path += '&' + utilQsString({ co: update.newComment }); } @@ -372,11 +367,13 @@ export default { }); }, + // get a single error from the cache getError: function(id) { return _krCache.keepRight[id]; }, + // replace a single error in the cache replaceError: function(error) { if (!(error instanceof krError) || !error.id) return; @@ -386,6 +383,7 @@ export default { return error; }, + // remove a single error from the cache removeError: function(error) { if (!(error instanceof krError) || !error.id) return; diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index f8b9d7687..5d73e0ddf 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -1,8 +1,5 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; -import { - event as d3_event, - select as d3_select -} from 'd3-selection'; +import { select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; import { services } from '../services'; @@ -96,7 +93,7 @@ export function uiKeepRightEditor(context) { .attr('class', 'new-comment-input') .attr('placeholder', t('QA.keepRight.comment_placeholder')) .attr('maxlength', 1000) - .property('value', function(d) { return d.comment; }) + .property('value', function(d) { return d.newComment || d.comment; }) .call(utilNoAuto) .on('input', changeInput) .on('blur', changeInput); @@ -104,16 +101,19 @@ export function uiKeepRightEditor(context) { // update saveSection = saveSectionEnter .merge(saveSection) - .call(userDetails) .call(keepRightSaveButtons); function changeInput() { var input = d3_select(this); - var val = input.property('value').trim() || undefined; + var val = input.property('value').trim(); + + if (val === _error.comment) { + val = undefined; + } // store the unsaved comment with the error itself - _error = _error.update({ newComment: val, comment: val }); + _error = _error.update({ newComment: val }); var keepRight = services.keepRight; if (keepRight) { @@ -126,100 +126,7 @@ export function uiKeepRightEditor(context) { } - function userDetails(selection) { - var detailSection = selection.selectAll('.detail-section') - .data([0]); - - detailSection = detailSection.enter() - .append('div') - .attr('class', 'detail-section') - .merge(detailSection); - - var osm = services.osm; - if (!osm) return; - - // Add warning if user is not logged in - var hasAuth = osm.authenticated(); - var authWarning = detailSection.selectAll('.auth-warning') - .data(hasAuth ? [] : [0]); - - authWarning.exit() - .transition() - .duration(200) - .style('opacity', 0) - .remove(); - - var authEnter = authWarning.enter() - .insert('div', '.tag-reference-body') - .attr('class', 'field-warning auth-warning') - .style('opacity', 0); - - authEnter - .call(svgIcon('#iD-icon-alert', 'inline')); - - authEnter - .append('span') - .text(t('note.login')); - - authEnter - .append('a') - .attr('target', '_blank') - .call(svgIcon('#iD-icon-out-link', 'inline')) - .append('span') - .text(t('login')) - .on('click.error-login', function() { - d3_event.preventDefault(); - osm.authenticate(); - }); - - authEnter - .transition() - .duration(200) - .style('opacity', 1); - - - var prose = detailSection.selectAll('.error-save-prose') - .data(hasAuth ? [0] : []); - - prose.exit() - .remove(); - - prose = prose.enter() - .append('p') - .attr('class', 'error-save-prose') - .text(t('QA.keepRight.upload_explanation')) - .merge(prose); - - osm.userDetails(function(err, user) { - if (err) return; - - var userLink = d3_select(document.createElement('div')); - - if (user.image_url) { - userLink - .append('img') - .attr('src', user.image_url) - .attr('class', 'icon pre-text user-icon'); - } - - userLink - .append('a') - .attr('class', 'user-info') - .text(user.display_name) - .attr('href', osm.userURL(user.display_name)) - .attr('tabindex', -1) - .attr('target', '_blank'); - - prose - .html(t('QA.keepRight.upload_explanation_with_user', { user: userLink.html() })); - }); - } - - function keepRightSaveButtons(selection) { - var osm = services.osm; - var hasAuth = osm && osm.authenticated(); - var isSelected = (_error && _error.id === context.selectedErrorID()); var buttonSection = selection.selectAll('.buttons') .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); @@ -235,41 +142,43 @@ export function uiKeepRightEditor(context) { buttonEnter .append('button') - .attr('class', 'button resolve-button action'); + .attr('class', 'button comment-button action') + .text(t('QA.keepRight.save_comment')); + + buttonEnter + .append('button') + .attr('class', 'button close-button action'); buttonEnter .append('button') .attr('class', 'button ignore-button action'); - buttonEnter - .append('button') - .attr('class', 'button comment-button action') - .text(t('note.comment')); - // update buttonSection = buttonSection .merge(buttonEnter); - buttonSection.select('.cancel-button') // select and propagate data - .on('click.cancel', function(d) { + buttonSection.select('.comment-button') // select and propagate data + .attr('disabled', function(d) { + return d.newComment === undefined ? true : null; + }) + .on('click.comment', function(d) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - keepRight.removeError(d); + keepRight.postKeepRightUpdate(d, function(err, error) { + dispatch.call('change', error); + }); } - context.enter(modeBrowse(context)); - dispatch.call('change'); }); - buttonSection.select('.resolve-button') // select and propagate data - .attr('disabled', (hasAuth ? null : true)) + buttonSection.select('.close-button') // select and propagate data .text(function(d) { // NOTE: no state is available because keepRight export only exports open errors - var andComment = (d.newComment ? '_comment' : ''); - return t('QA.keepRight.resolve' + andComment); + var andComment = (d.newComment !== undefined ? '_comment' : ''); + return t('QA.keepRight.close' + andComment); }) - .on('click.state', function(d) { + .on('click.close', function(d) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { @@ -282,12 +191,11 @@ export function uiKeepRightEditor(context) { }); buttonSection.select('.ignore-button') // select and propagate data - .attr('disabled', (hasAuth ? null : true)) .text(function(d) { - var andComment = (d.newComment ? '_comment' : ''); + var andComment = (d.newComment !== undefined ? '_comment' : ''); return t('QA.keepRight.ignore' + andComment); }) - .on('click.state', function(d) { + .on('click.ignore', function(d) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { @@ -297,20 +205,6 @@ export function uiKeepRightEditor(context) { }); } }); - - buttonSection.select('.comment-button') // select and propagate data - .attr('disabled', function(d) { - return (hasAuth && d.newComment) ? null : true; - }) - .on('click.comment', function(d) { - this.blur(); // avoid keeping focus on the button - #4641 - var keepRight = services.keepRight; - if (keepRight) { - keepRight.postKeepRightUpdate(d, function(err, error) { - dispatch.call('change', error); - }); - } - }); } From 9e6497b5495ac85874f46d983ebf355394486629 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 01:39:40 -0500 Subject: [PATCH 067/114] Update or remove the error after POSTing changes to server --- modules/services/keepRight.js | 46 +++++++++++++++++++--------------- modules/ui/keepRight_editor.js | 6 ++--- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index d83a66bb7..15e1efb16 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -324,34 +324,40 @@ export default { }, - postKeepRightUpdate: function(update, callback) { - if (_krCache.inflight[update.id]) { - return callback({ message: 'Error update already inflight', status: -2 }, update); + postKeepRightUpdate: function(d, callback) { + if (_krCache.inflight[d.id]) { + return callback({ message: 'Error update already inflight', status: -2 }, d); } - var path = apibase + 'comment.php?'; - if (update.state) { - path += '&st=' + update.state; + var that = this; + var params = { schema: d.schema, id: d.error_id }; + + if (d.state) { + params.st = d.state; } - if (update.newComment !== undefined) { - path += '&' + utilQsString({ co: update.newComment }); + if (d.newComment !== undefined) { + params.co = d.newComment; } - path += '&schema=' + update.schema + '&id=' + update.error_id; + // NOTE: This throws a CORS err, but it seems successful. + // We don't care too much about the response, so this is fine. + var url = apibase + 'comment.php?' + utilQsString(params); + _krCache.inflight[d.id] = d3_request(url) + .post(function(err) { + delete _krCache.inflight[d.id]; + if (d.state === 'ignore' || d.state === 'ignore_t') { + that.removeError(d); + } else { + d = that.replaceError(d.update({ + comment: d.newComment, + newComment: undefined, + state: undefined + })); + } - _krCache.inflight[update.id] = d3_request(path) - .mimeType('application/json') - .response(function(xhr) { - return JSON.parse(xhr.responseText); - }) - .post(function(err, data) { - delete _krCache.inflight[update.id]; - if (err) { return callback(err); } - - console.log('data ', data); + return callback(err, d); }); - // NOTE: This throws a CORS error, but it seems successful? }, diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 5d73e0ddf..01f120226 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -174,7 +174,6 @@ export function uiKeepRightEditor(context) { buttonSection.select('.close-button') // select and propagate data .text(function(d) { - // NOTE: no state is available because keepRight export only exports open errors var andComment = (d.newComment !== undefined ? '_comment' : ''); return t('QA.keepRight.close' + andComment); }) @@ -182,8 +181,7 @@ export function uiKeepRightEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - - d.state = d.state === 'ignore_t' ? '' : 'ignore_t'; + d.state = 'ignore_t'; // ignore temporarily (error fixed) keepRight.postKeepRightUpdate(d, function(err, error) { dispatch.call('change', error); }); @@ -199,7 +197,7 @@ export function uiKeepRightEditor(context) { this.blur(); // avoid keeping focus on the button - #4641 var keepRight = services.keepRight; if (keepRight) { - d.state = d.state === 'ignore' ? '' : 'ignore'; + d.state = 'ignore'; // ignore permanently (false positive) keepRight.postKeepRightUpdate(d, function(err, error) { dispatch.call('change', error); }); From 0256ad7f0d51bbc2569a09cf2bc31bba9f53f67b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 10:04:53 -0500 Subject: [PATCH 068/114] Remove redundant CSS --- css/65_data.css | 404 +++++++++++++++++++----------------------------- css/80_app.css | 188 +++------------------- 2 files changed, 181 insertions(+), 411 deletions(-) diff --git a/css/65_data.css b/css/65_data.css index 023f61fc1..b4159ddd6 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -1,5 +1,5 @@ -/* OSM Notes Layer */ +/* OSM Notes and KeepRight Layers */ .layer-keepRight, .layer-notes { pointer-events: none; @@ -27,6 +27,7 @@ .kr_error-header-icon .kr_error-fill, .layer-keepRight .kr_error .kr_error-fill { stroke: #333; + stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */ } .note-header-icon .note-fill, @@ -64,6 +65,164 @@ } +/* Keep Right Errors +------------------------------------------------------- */ +.kr_error_type_40, /* impossible oneways */ +.kr_error_type_41, +.kr_error_type_42, +.kr_error_type_43 { + color: #fd007f; +} + +.kr_error_type_50 { /* almost junctions */ + color: #c827fe; +} + +.kr_error_type_70, /* missing tags */ +.kr_error_type_71, +.kr_error_type_72 { + color: #74aeaf; +} + +.kr_error_type_90 { /* motorway without ref */ + color: #3124af; +} + +.kr_error_type_100 { /* place of worship without religion */ + color: #a80000; +} + +.kr_error_type_110 { /* poi without name */ + color: #44650b; +} + +.kr_error_type_120 { /* way without nodes */ + color: #99274d; +} + +.kr_error_type_130 { /* disconnected way */ + color: #eb7310; +} + +.kr_error_type_150 { /* railway crossing without tag */ + color: #7218c1; +} + +.kr_error_type_160 { /* wrong railway tag */ + color: #c903ae; +} + +.kr_error_type_170 { /* FIXME tag */ + color: #07d40b; +} + +.kr_error_type_180 { /* relation without type */ + color: #01ff0a; +} + +.kr_error_type_190, +.kr_error_type_191, +.kr_error_type_192, +.kr_error_type_193, +.kr_error_type_194, +.kr_error_type_195, +.kr_error_type_196, +.kr_error_type_197, +.kr_error_type_198 { /* intersection without junction */ + color: #e6fcb0; +} + +.kr_error_type_200, +.kr_error_type_201, +.kr_error_type_202, +.kr_error_type_203, +.kr_error_type_204, +.kr_error_type_205, +.kr_error_type_206, +.kr_error_type_207, +.kr_error_type_208 { /* overlapping ways */ + color: #fdbf6f; +} + +.kr_error_type_210, +.kr_error_type_211, +.kr_error_type_212 { /* self intersecting ways */ + color: #4a7601; +} + +.kr_error_type_220, +.kr_error_type_221 { /* misspelled tag */ + color: #ef7cf2; +} + +.kr_error_type_230, +.kr_error_type_231, +.kr_error_type_232 { /* layer conflict */ + color: #b15928; +} + +.kr_error_type_270 { /* unusual motorway connection */ + color: #2aaf92; +} + +.kr_error_type_280, /* boundary issue */ +.kr_error_type_281, +.kr_error_type_282, +.kr_error_type_283, +.kr_error_type_284, +.kr_error_type_285 { + color: #5f47a0; +} + +.kr_error_type_290, /* restriction issue */ +.kr_error_type_291, +.kr_error_type_292, +.kr_error_type_293, +.kr_error_type_294, +.kr_error_type_295, +.kr_error_type_296, +.kr_error_type_297, +.kr_error_type_298 { + color: #a6cee3; +} + +.kr_error_type_310, /* roundabout issue */ +.kr_error_type_311, +.kr_error_type_312, +.kr_error_type_313 { + color: #0550e8; +} + +.kr_error_type_320 { /* improper _link */ + color: #28d9bb; +} + +.kr_error_type_350 { /* improper bridge tag */ + color: #ffff99; +} + +.kr_error_type_370 { /* doubled places */ + color: #ff8fdf; +} + +.kr_error_type_380 { /* non-physical sport tag */ + color: #b3b465; +} + +.kr_error_type_400, /* geometry / turn angles */ +.kr_error_type_401, +.kr_error_type_402 { + color: #b64f69; +} + +.kr_error_type_410, /* website issue */ +.kr_error_type_411, +.kr_error_type_412, +.kr_error_type_413 { + color: #b07f7e; +} + + /* Custom Map Data (geojson, gpx, kml, vector tile) */ .layer-mapdata { pointer-events: none; @@ -124,246 +283,3 @@ stroke-miterlimit: 1; } - -/* OSM Note UI */ -.note-header, -.kr_error-header { - background-color: #f6f6f6; - border-radius: 5px; - border: 1px solid #ccc; - display: flex; - flex-flow: row nowrap; - align-items: center; -} - -.note-header-icon, -.kr_error-header-icon { - background-color: #fff; - padding: 10px; - flex: 0 0 62px; - position: relative; - width: 60px; - height: 60px; - border-right: 1px solid #ccc; - border-radius: 5px 0 0 5px; -} -[dir='rtl'] .note-header-icon, -[dir='rtl'] .kr_error-header-icon { - border-right: unset; - border-left: 1px solid #ccc; - border-radius: 0 5px 5px 0; -} - -.note-header-icon .icon-wrap, -.kr_error-header-icon .icon-wrap { - position: absolute; - top: 0px; -} - -.note-header-label, -.kr_error-header-label { - background-color: #f6f6f6; - padding: 0 15px; - flex: 1 1 100%; - font-size: 14px; - font-weight: bold; - border-radius: 0 5px 5px 0; -} -[dir='rtl'] .note-header-label, -[dir='rtl'] .kr_error-header-label { - border-radius: 5px 0 0 5px; -} - -/* KeepRight */ -.kr_error-comment-container, -.kr_error-details-container { - background: #ececec; - padding: 10px 10px; - border-radius: 8px; - margin-top: 20px; -} - -.kr_error_type_30 { - color: #ddb87d; -} - -.kr_error_type_40, -.kr_error_type_41, -.kr_error_type_42, -.kr_error_type_43 { - color: #894668; -} - -.kr_error_type_50 { - color: #c827fe; -} - -.kr_error_type_70, -.kr_error_type_71, -.kr_error_type_72 { - color: #74aeaf; -} - -.kr_error_type_90 { - color: #3124af; -} - -.kr_error_type_100 { - color: #9e8e91; -} - -.kr_error_type_110 { - color: #44650b; -} - -.kr_error_type_120 { - color: #99274d; -} - -.kr_error_type_130 { - color: #eb7310; -} - -.kr_error_type_150 { - color: #7218c1; -} - -.kr_error_type_160 { - color: #c903ae; -} - -.kr_error_type_170 { - color: #07d40b; -} - -.kr_error_type_180 { - color: #09ef12; -} - -.kr_error_type_190, -.kr_error_type_191, -.kr_error_type_192, -.kr_error_type_193, -.kr_error_type_194, -.kr_error_type_195, -.kr_error_type_196, -.kr_error_type_197, -.kr_error_type_198 { - color: #e6fcb0; -} - -.kr_error_type_200, -.kr_error_type_201, -.kr_error_type_202, -.kr_error_type_203, -.kr_error_type_204, -.kr_error_type_205, -.kr_error_type_206, -.kr_error_type_207, -.kr_error_type_208 { - color: #71f264; -} - -.kr_error_type_210, -.kr_error_type_211, -.kr_error_type_212 { - color: #4a7601; -} - -.kr_error_type_220, -.kr_error_type_221 { - color: #ef7cf2; -} - -.kr_error_type_230, -.kr_error_type_231, -.kr_error_type_232 { - color: #5f775c; -} - -.kr_error_type_270 { - color: #2aaf92; -} - -.kr_error_type_280, -.kr_error_type_281, -.kr_error_type_282, -.kr_error_type_283, -.kr_error_type_284, -.kr_error_type_285 { - color: #5f47a0; -} - -.kr_error_type_290, -.kr_error_type_291, -.kr_error_type_292, -.kr_error_type_293, -.kr_error_type_294, -.kr_error_type_295, -.kr_error_type_296, -.kr_error_type_297, -.kr_error_type_298 { - color: #9bb2cd; -} - -.kr_error_type_310, -.kr_error_type_311, -.kr_error_type_312, -.kr_error_type_313 { - color: #0550e8; -} - -.kr_error_type_320 { - color: #28d9bb; -} - -.kr_error_type_350 { - color: #4d719c; -} - -.kr_error_type_370 { - color: #ff8fdf; -} - -.kr_error_type_380 { - color: #b3b465; -} - -.kr_error_type_400, -.kr_error_type_401, -.kr_error_type_402 { - color: #b20e36; -} - -.kr_error_type_410, -.kr_error_type_411, -.kr_error_type_412, -.kr_error_type_413 { - color: #b07f7e; -} - -.kr_error-details-title { - text-align: left; - margin-bottom: 20px; -} - -.kr_error-details-description { - text-align: left; - margin-bottom: 10px; -} - -.QA-buttons { - display: flex; - flex-flow: row nowrap; - justify-content: space-around; - align-items: center; - padding: 0 5px; -} - -.QA-toggle-off, -.QA-toggle-on { - width: 35%; - margin: 10px 0px; - text-align: center; - vertical-align: middle; -} diff --git a/css/80_app.css b/css/80_app.css index 5540552eb..a249a85a4 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2431,9 +2431,10 @@ input.key-trap { } -/* OSM Note Editor +/* OSM Note / KeepRight Editors ------------------------------------------------------- */ -.note-header { +.note-header, +.kr_error-header { background-color: #f6f6f6; border-radius: 5px; border: 1px solid #ccc; @@ -2442,7 +2443,8 @@ input.key-trap { align-items: center; } -.note-header-icon { +.note-header-icon, +.kr_error-header-icon { background-color: #fff; padding: 10px; flex: 0 0 62px; @@ -2452,18 +2454,21 @@ input.key-trap { border-right: 1px solid #ccc; border-radius: 5px 0 0 5px; } -[dir='rtl'] .note-header-icon { +[dir='rtl'] .note-header-icon, +[dir='rtl'] .kr_error-header-icon { border-right: unset; border-left: 1px solid #ccc; border-radius: 0 5px 5px 0; } -.note-header-icon .icon-wrap { +.note-header-icon .icon-wrap, +.kr_error-header-icon .icon-wrap { position: absolute; top: 0px; } -.note-header-label { +.note-header-label, +.kr_error-header-label { background-color: #f6f6f6; padding: 0 15px; flex: 1 1 100%; @@ -2471,7 +2476,8 @@ input.key-trap { font-weight: bold; border-radius: 0 5px 5px 0; } -[dir='rtl'] .note-header-label { +[dir='rtl'] .note-header-label, +[dir='rtl'] .kr_error-header-label { border-radius: 5px 0 0 5px; } @@ -2561,168 +2567,16 @@ input.key-trap { float: right; } - -/* Keep Right Errors -------------------------------------------------------- */ -.kr_error-header-icon .kr_error .kr_error-fill, -.layer-keepRight .kr_error .kr_error-fill { - stroke: #333; - stroke-width: 1.3px; /* NOTE: likely a better way to scale the icon stroke */ +.kr_error-details-container { + background: #ececec; + padding: 10px; + margin-top: 20px; + border-radius: 4px; + border: 1px solid #ccc; } -.kr_error_type_40, /* impossible oneways */ -.kr_error_type_41, -.kr_error_type_42, -.kr_error_type_43 { - color: #fd007f; -} - -.kr_error_type_50 { /* almost junctions */ - color: #c827fe; -} - -.kr_error_type_70, /* missing tags */ -.kr_error_type_71, -.kr_error_type_72 { - color: #74aeaf; -} - -.kr_error_type_90 { /* motorway without ref */ - color: #3124af; -} - -.kr_error_type_100 { /* place of worship without religion */ - color: #a80000; -} - -.kr_error_type_110 { /* poi without name */ - color: #44650b; -} - -.kr_error_type_120 { /* way without nodes */ - color: #99274d; -} - -.kr_error_type_130 { /* disconnected way */ - color: #eb7310; -} - -.kr_error_type_150 { /* railway crossing without tag */ - color: #7218c1; -} - -.kr_error_type_160 { /* wrong railway tag */ - color: #c903ae; -} - -.kr_error_type_170 { /* FIXME tag */ - color: #07d40b; -} - -.kr_error_type_180 { /* relation without type */ - color: #01ff0a; -} - -.kr_error_type_190, -.kr_error_type_191, -.kr_error_type_192, -.kr_error_type_193, -.kr_error_type_194, -.kr_error_type_195, -.kr_error_type_196, -.kr_error_type_197, -.kr_error_type_198 { /* intersection without junction */ - color: #e6fcb0; -} - -.kr_error_type_200, -.kr_error_type_201, -.kr_error_type_202, -.kr_error_type_203, -.kr_error_type_204, -.kr_error_type_205, -.kr_error_type_206, -.kr_error_type_207, -.kr_error_type_208 { /* overlapping ways */ - color: #fdbf6f; -} - -.kr_error_type_210, -.kr_error_type_211, -.kr_error_type_212 { /* self intersecting ways */ - color: #4a7601; -} - -.kr_error_type_220, -.kr_error_type_221 { /* misspelled tag */ - color: #ef7cf2; -} - -.kr_error_type_230, -.kr_error_type_231, -.kr_error_type_232 { /* layer conflict */ - color: #b15928; -} - -.kr_error_type_270 { /* unusual motorway connection */ - color: #2aaf92; -} - -.kr_error_type_280, /* boundary issue */ -.kr_error_type_281, -.kr_error_type_282, -.kr_error_type_283, -.kr_error_type_284, -.kr_error_type_285 { - color: #5f47a0; -} - -.kr_error_type_290, /* restriction issue */ -.kr_error_type_291, -.kr_error_type_292, -.kr_error_type_293, -.kr_error_type_294, -.kr_error_type_295, -.kr_error_type_296, -.kr_error_type_297, -.kr_error_type_298 { - color: #a6cee3; -} - -.kr_error_type_310, /* roundabout issue */ -.kr_error_type_311, -.kr_error_type_312, -.kr_error_type_313 { - color: #0550e8; -} - -.kr_error_type_320 { /* improper _link */ - color: #28d9bb; -} - -.kr_error_type_350 { /* improper bridge tag */ - color: #ffff99; -} - -.kr_error_type_370 { /* doubled places */ - color: #ff8fdf; -} - -.kr_error_type_380 { /* non-physical sport tag */ - color: #b3b465; -} - -.kr_error_type_400, /* geometry / turn angles */ -.kr_error_type_401, -.kr_error_type_402 { - color: #b64f69; -} - -.kr_error_type_410, /* website issue */ -.kr_error_type_411, -.kr_error_type_412, -.kr_error_type_413 { - color: #b07f7e; +.kr_error-details-description { + margin-bottom: 10px; } From 46ebce2d6f91fb3159fd219343f1e3aa75ae1094 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 11:37:14 -0500 Subject: [PATCH 069/114] Fix error reporting urls and make sure sidebar not showing stale data --- modules/services/keepRight.js | 6 ++++++ modules/services/osm.js | 5 +++-- modules/svg/keepRight.js | 15 --------------- modules/ui/inspector.js | 12 ++++++------ modules/ui/keepRight_details.js | 6 +++--- modules/ui/keepRight_editor.js | 10 +++++++--- modules/ui/keepRight_header.js | 8 +++----- modules/ui/note_comments.js | 4 ++-- modules/ui/note_editor.js | 4 ++-- modules/ui/note_header.js | 4 ++-- modules/ui/note_report.js | 23 ++++++++++------------- modules/ui/sidebar.js | 29 +++++++++++++++++++---------- modules/ui/view_on_keepRight.js | 16 ++++++++-------- modules/ui/view_on_osm.js | 5 +---- 14 files changed, 72 insertions(+), 75 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 15e1efb16..9bae8840e 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -396,5 +396,11 @@ export default { delete _krCache.keepRight[error.id]; updateRtree(encodeErrorRtree(error), false); // false = remove + }, + + + errorURL: function(error) { + return apibase + 'report_map.php?schema=' + error.schema + '&error=' + error.id; } + }; diff --git a/modules/services/osm.js b/modules/services/osm.js index de3a91f4d..d3e52cda5 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -431,8 +431,9 @@ export default { return urlroot + '/note/' + note.id; }, - keepRightURL: function(error) { - return 'https://www.keepright.at/report_map.php?schema=' + error.schema + '&error=' + error.id; + + noteReportURL: function(note) { + return urlroot + '/reports/new?reportable_type=Note&reportable_id=' + note.id; }, diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 17b8b2395..0ef3c6146 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -24,7 +24,6 @@ export function svgKeepRight(projection, context, dispatch) { if (svgKeepRight.initialized) return; // run once svgKeepRight.enabled = false; svgKeepRight.initialized = true; - svgKeepRight.visibleErrors = [30]; } @@ -184,20 +183,6 @@ export function svgKeepRight(projection, context, dispatch) { }; - drawKeepRight.visibleErrors = function(_) { - if (!arguments.length) return svgKeepRight.visibleErrors; - svgKeepRight.visibleErrors.push(_); - if (svgKeepRight.visibleErrors) { - showLayer(); - } else { - hideLayer(); - } - dispatch.call('change'); - return this; - }; - - - init(); return drawKeepRight; } diff --git a/modules/ui/inspector.js b/modules/ui/inspector.js index f4785ddc3..7e634b602 100644 --- a/modules/ui/inspector.js +++ b/modules/ui/inspector.js @@ -92,9 +92,9 @@ export function uiInspector(context) { } - inspector.state = function(_) { + inspector.state = function(val) { if (!arguments.length) return _state; - _state = _; + _state = val; entityEditor.state(_state); // remove any old field help overlay that might have gotten attached to the inspector @@ -104,16 +104,16 @@ export function uiInspector(context) { }; - inspector.entityID = function(_) { + inspector.entityID = function(val) { if (!arguments.length) return _entityID; - _entityID = _; + _entityID = val; return inspector; }; - inspector.newFeature = function(_) { + inspector.newFeature = function(val) { if (!arguments.length) return _newFeature; - _newFeature = _; + _newFeature = val; return inspector; }; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 1c0602b51..01702365d 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -32,7 +32,7 @@ export function uiKeepRightDetails(context) { var details = selection.selectAll('.kr_error-details') .data( (_error ? [_error] : []), - function(d) { return d.status + d.id; } + function(d) { return d.id + '-' + (d.status || 0); } ); details.exit() @@ -69,9 +69,9 @@ export function uiKeepRightDetails(context) { } - keepRightDetails.error = function(_) { + keepRightDetails.error = function(val) { if (!arguments.length) return _error; - _error = _; + _error = val; return keepRightDetails; }; diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 01f120226..532c8c3e2 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -71,8 +71,12 @@ export function uiKeepRightEditor(context) { function keepRightSaveSection(selection) { var isSelected = (_error && _error.id === context.selectedErrorID()); + var isShown = (_error && (isSelected || _error.newComment || _error.comment)); var saveSection = selection.selectAll('.error-save') - .data((isSelected ? [_error] : []), function(d) { return d.status + d.id; }); + .data( + (isShown ? [_error] : []), + function(d) { return d.id + '-' + (d.status || 0); } + ); // exit saveSection.exit() @@ -206,9 +210,9 @@ export function uiKeepRightEditor(context) { } - keepRightEditor.error = function(_) { + keepRightEditor.error = function(val) { if (!arguments.length) return _error; - _error = _; + _error = val; return keepRightEditor; }; diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index 2e14992ef..e2a63e2f4 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -31,14 +31,12 @@ export function uiKeepRightHeader() { var header = selection.selectAll('.kr_error-header') .data( (_error ? [_error] : []), - function(d) { return d.status + d.id; } + function(d) { return d.id + '-' + (d.status || 0); } ); header.exit() .remove(); - - var headerEnter = header.enter() .append('div') .attr('class', 'kr_error-header'); @@ -62,9 +60,9 @@ export function uiKeepRightHeader() { } - keepRightHeader.error = function(_) { + keepRightHeader.error = function(val) { if (!arguments.length) return _error; - _error = _; + _error = val; return keepRightHeader; }; diff --git a/modules/ui/note_comments.js b/modules/ui/note_comments.js index 4534ea10b..cbb004095 100644 --- a/modules/ui/note_comments.js +++ b/modules/ui/note_comments.js @@ -110,9 +110,9 @@ export function uiNoteComments() { } - noteComments.note = function(_) { + noteComments.note = function(val) { if (!arguments.length) return _note; - _note = _; + _note = val; return noteComments; }; diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index 9df0a8f69..5de11ff5d 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -425,9 +425,9 @@ export function uiNoteEditor(context) { } - noteEditor.note = function(_) { + noteEditor.note = function(val) { if (!arguments.length) return _note; - _note = _; + _note = val; return noteEditor; }; diff --git a/modules/ui/note_header.js b/modules/ui/note_header.js index 8fc2a0e95..c19338c49 100644 --- a/modules/ui/note_header.js +++ b/modules/ui/note_header.js @@ -49,9 +49,9 @@ export function uiNoteHeader() { } - noteHeader.note = function(_) { + noteHeader.note = function(val) { if (!arguments.length) return _note; - _note = _; + _note = val; return noteHeader; }; diff --git a/modules/ui/note_report.js b/modules/ui/note_report.js index 759f9ba13..09b78a4e4 100644 --- a/modules/ui/note_report.js +++ b/modules/ui/note_report.js @@ -1,23 +1,20 @@ import { t } from '../util/locale'; +import { osmNote } from '../osm'; +import { services } from '../services'; import { svgIcon } from '../svg'; -import { - osmNote -} from '../osm'; export function uiNoteReport() { var _note; - var url = 'https://www.openstreetmap.org/reports/new?reportable_id='; function noteReport(selection) { + var url; + if (services.osm && (_note instanceof osmNote) && (!_note.isNew())) { + url = services.osm.noteReportURL(_note); + } - if (!(_note instanceof osmNote)) return; - - url += _note.id + '&reportable_type=Note'; - - var data = ((!_note || _note.isNew()) ? [] : [_note]); var link = selection.selectAll('.note-report') - .data(data, function(d) { return d.id; }); + .data(url ? [url] : []); // exit link.exit() @@ -28,7 +25,7 @@ export function uiNoteReport() { .append('a') .attr('class', 'note-report') .attr('target', '_blank') - .attr('href', url) + .attr('href', function(d) { return d; }) .call(svgIcon('#iD-icon-out-link', 'inline')); linkEnter @@ -37,9 +34,9 @@ export function uiNoteReport() { } - noteReport.note = function(_) { + noteReport.note = function(val) { if (!arguments.length) return _note; - _note = _; + _note = val; return noteReport; }; diff --git a/modules/ui/sidebar.js b/modules/ui/sidebar.js index 576ea072c..f9a669f5e 100644 --- a/modules/ui/sidebar.js +++ b/modules/ui/sidebar.js @@ -10,6 +10,7 @@ import { } from 'd3-selection'; import { osmEntity, osmNote, krError } from '../osm'; +import { services } from '../services'; import { uiDataEditor, uiFeatureList, uiInspector, uiNoteEditor, uiKeepRightEditor } from './index'; import { textDirection } from '../util/locale'; @@ -22,7 +23,7 @@ export function uiSidebar(context) { var _current; var _wasData = false; var _wasNote = false; - var _was_krError = false; + var _wasKRError = false; function sidebar(selection) { @@ -119,6 +120,11 @@ export function uiSidebar(context) { if (context.mode().id === 'drag-note') return; _wasNote = true; + var osm = services.osm; + if (osm) { + datum = osm.getNote(datum.id); // marker may contain stale data - get latest + } + sidebar .show(noteEditor.note(datum)); @@ -126,10 +132,15 @@ export function uiSidebar(context) { .classed('inspector-hover', true); } else if (datum instanceof krError) { - _was_krError = true; - var kr_errors = d3_selectAll('.kr_error'); - kr_errors - .classed('hover', function(d) { return d === datum; }); + _wasKRError = true; + + var keepRight = services.keepRight; + if (keepRight) { + datum = keepRight.getError(datum.id); // marker may contain stale data - get latest + } + + d3_selectAll('.kr_error') + .classed('hover', function(d) { return d.id === datum.id; }); sidebar .show(keepRightEditor.error(datum)); @@ -162,14 +173,12 @@ export function uiSidebar(context) { inspector .state('hide'); - } else if (_wasData || _wasNote) { + } else if (_wasData || _wasNote || _wasKRError) { _wasNote = false; _wasData = false; + _wasKRError = false; d3_selectAll('.note').classed('hover', false); - sidebar.hide(); - } else if (_was_krError) { - d3_selectAll('.kr_error') - .classed('hover', false); + d3_selectAll('.kr_error').classed('hover', false); sidebar.hide(); } } diff --git a/modules/ui/view_on_keepRight.js b/modules/ui/view_on_keepRight.js index 0574a3e57..d0b28a4cb 100644 --- a/modules/ui/view_on_keepRight.js +++ b/modules/ui/view_on_keepRight.js @@ -1,21 +1,21 @@ import { t } from '../util/locale'; +import { services } from '../services'; import { svgIcon } from '../svg'; import { krError } from '../osm'; -export function uiViewOnKeepRight(context) { +export function uiViewOnKeepRight() { var _error; // a keepright error function viewOnKeepRight(selection) { var url; - if (_error instanceof krError) { - url = context.connection().keepRightURL(_error); + if (services.keepRight && (_error instanceof krError)) { + url = services.keepRight.errorURL(_error); } - var data = ((!_error) ? [] : [_error]); var link = selection.selectAll('.view-on-keepRight') - .data(data, function(d) { return d.id; }); + .data(url ? [url] : []); // exit link.exit() @@ -26,7 +26,7 @@ export function uiViewOnKeepRight(context) { .append('a') .attr('class', 'view-on-keepRight') .attr('target', '_blank') - .attr('href', url) + .attr('href', function(d) { return d; }) .call(svgIcon('#iD-icon-out-link', 'inline')); linkEnter @@ -35,9 +35,9 @@ export function uiViewOnKeepRight(context) { } - viewOnKeepRight.what = function(_) { + viewOnKeepRight.what = function(val) { if (!arguments.length) return _error; - _error = _; + _error = val; return viewOnKeepRight; }; diff --git a/modules/ui/view_on_osm.js b/modules/ui/view_on_osm.js index b013f158b..7bf265275 100644 --- a/modules/ui/view_on_osm.js +++ b/modules/ui/view_on_osm.js @@ -1,9 +1,6 @@ import { t } from '../util/locale'; +import { osmEntity, osmNote } from '../osm'; import { svgIcon } from '../svg'; -import { - osmEntity, - osmNote -} from '../osm'; export function uiViewOnOSM(context) { From 35f8078f98e369ba716da491c938c939f9f2a449 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 15:30:36 -0500 Subject: [PATCH 070/114] Reduce number of marker colors, increase contrast, tweak language --- css/65_data.css | 165 ++++++++------------------------- data/core.yaml | 18 ++-- data/keepRight.json | 2 +- dist/locales/en.json | 18 ++-- modules/services/keepRight.js | 12 ++- modules/svg/keepRight.js | 2 +- modules/ui/keepRight_header.js | 2 +- 7 files changed, 67 insertions(+), 152 deletions(-) diff --git a/css/65_data.css b/css/65_data.css index b4159ddd6..42905af5c 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -67,159 +67,72 @@ /* Keep Right Errors ------------------------------------------------------- */ +.kr_error_type_20, /* multiple nodes on same spot */ .kr_error_type_40, /* impossible oneways */ -.kr_error_type_41, -.kr_error_type_42, -.kr_error_type_43 { - color: #fd007f; +.kr_error_type_210, /* self intersecting ways */ +.kr_error_type_270, /* unusual motorway connection */ +.kr_error_type_310, /* roundabout issues */ +.kr_error_type_320, /* improper _link */ +.kr_error_type_350 { /* improper bridge tag */ + color: #ff9; } .kr_error_type_50 { /* almost junctions */ - color: #c827fe; + color: #88f; } -.kr_error_type_70, /* missing tags */ -.kr_error_type_71, -.kr_error_type_72 { - color: #74aeaf; +.kr_error_type_60, /* deprecated tags */ +.kr_error_type_70, /* tagging issues */ +.kr_error_type_90, /* motorway without ref */ +.kr_error_type_100, /* place of worship without religion */ +.kr_error_type_110, /* poi without name */ +.kr_error_type_150, /* railway crossing without tag */ +.kr_error_type_220, /* misspelled tag */ +.kr_error_type_300, /* missing maxspeed */ +.kr_error_type_380, /* non-physical sport tag */ +.kr_error_type_390 { /* missing tracktype */ + color: #5d0; } -.kr_error_type_90 { /* motorway without ref */ - color: #3124af; -} - -.kr_error_type_100 { /* place of worship without religion */ - color: #a80000; -} - -.kr_error_type_110 { /* poi without name */ - color: #44650b; -} - -.kr_error_type_120 { /* way without nodes */ - color: #99274d; -} - -.kr_error_type_130 { /* disconnected way */ - color: #eb7310; -} - -.kr_error_type_150 { /* railway crossing without tag */ - color: #7218c1; -} - -.kr_error_type_160 { /* wrong railway tag */ - color: #c903ae; +.kr_error_type_130 { /* disconnected ways */ + color: #fa3; } .kr_error_type_170 { /* FIXME tag */ - color: #07d40b; + color: #ff0; } -.kr_error_type_180 { /* relation without type */ - color: #01ff0a; +.kr_error_type_190 { /* intersection without junction */ + color: #f33; } -.kr_error_type_190, -.kr_error_type_191, -.kr_error_type_192, -.kr_error_type_193, -.kr_error_type_194, -.kr_error_type_195, -.kr_error_type_196, -.kr_error_type_197, -.kr_error_type_198 { /* intersection without junction */ - color: #e6fcb0; -} - -.kr_error_type_200, -.kr_error_type_201, -.kr_error_type_202, -.kr_error_type_203, -.kr_error_type_204, -.kr_error_type_205, -.kr_error_type_206, -.kr_error_type_207, -.kr_error_type_208 { /* overlapping ways */ +.kr_error_type_200 { /* overlapping ways */ color: #fdbf6f; } -.kr_error_type_210, -.kr_error_type_211, -.kr_error_type_212 { /* self intersecting ways */ - color: #4a7601; +.kr_error_type_160, /* railway layer conflict */ +.kr_error_type_230 { /* layer conflict */ + color: #b60; } -.kr_error_type_220, -.kr_error_type_221 { /* misspelled tag */ - color: #ef7cf2; -} - -.kr_error_type_230, -.kr_error_type_231, -.kr_error_type_232 { /* layer conflict */ - color: #b15928; -} - -.kr_error_type_270 { /* unusual motorway connection */ - color: #2aaf92; -} - -.kr_error_type_280, /* boundary issue */ -.kr_error_type_281, -.kr_error_type_282, -.kr_error_type_283, -.kr_error_type_284, -.kr_error_type_285 { +.kr_error_type_280 { /* boundary issues */ color: #5f47a0; } -.kr_error_type_290, /* restriction issue */ -.kr_error_type_291, -.kr_error_type_292, -.kr_error_type_293, -.kr_error_type_294, -.kr_error_type_295, -.kr_error_type_296, -.kr_error_type_297, -.kr_error_type_298 { - color: #a6cee3; +.kr_error_type_180, /* relation without type */ +.kr_error_type_290 { /* turn restriction issues */ + color: #ace; } -.kr_error_type_310, /* roundabout issue */ -.kr_error_type_311, -.kr_error_type_312, -.kr_error_type_313 { - color: #0550e8; +.kr_error_type_360, /* language unknown */ +.kr_error_type_370, /* doubled places */ +.kr_error_type_410 { /* website issues */ + color: #f9b; } -.kr_error_type_320 { /* improper _link */ - color: #28d9bb; -} - -.kr_error_type_350 { /* improper bridge tag */ - color: #ffff99; -} - -.kr_error_type_370 { /* doubled places */ - color: #ff8fdf; -} - -.kr_error_type_380 { /* non-physical sport tag */ - color: #b3b465; -} - -.kr_error_type_400, /* geometry / turn angles */ -.kr_error_type_401, -.kr_error_type_402 { - color: #b64f69; -} - -.kr_error_type_410, /* website issue */ -.kr_error_type_411, -.kr_error_type_412, -.kr_error_type_413 { - color: #b07f7e; +.kr_error_type_120, /* way without nodes */ +.kr_error_type_400 { /* geometry / turn angles */ + color: #c35; } diff --git a/data/core.yaml b/data/core.yaml index eaf6a52dd..03e6c35bc 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -717,7 +717,7 @@ en: title: 'Railway crossing without tag' description: 'This crossing of a highway and a railway needs to be tagged as "railway=crossing" or "railway=level_crossing".' 160: - title: 'Wrongly used railway tag' + title: 'Railway layer conflict' description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing.' 170: title: 'FIXME tagged items' @@ -744,7 +744,7 @@ en: 221: description: 'This {var1} has a tag with key "{var2}".' 230: - title: 'Layer Conflict' + title: 'Layer conflict' description: 'This node is a junction of ways on different layers.' 231: description: 'This node is a junction of ways on different layers: {var1}.' @@ -777,7 +777,7 @@ en: description: 'There is an unspecified issue with this restriction.' 291: title: 'Restriction missing type' - description: 'This turn restriction has no known restriction type.' + description: 'This turn restriction has an unrecognized restriction type.' 292: title: 'Restriction missing "from" way' description: 'A turn restriction needs exactly one "from" member. This one has {var1}.' @@ -788,10 +788,10 @@ en: title: 'Restriction "from" or "to" is not a way' description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' 295: - title: 'Restriction "via" is not on the way ends' + title: 'Restriction "via" is not an endpoint' description: '"via" (node {var1}) is not the first or the last member of "{var2}" (way {var3}).' 296: - title: 'Wrong restriction angle' + title: 'Unusual restriction angle' description: 'Restriction type is "{var1}" but angle is {var2} degrees. Maybe the restriction type is not appropriate?' 297: title: 'Wrong direction of to member' @@ -801,16 +801,16 @@ en: description: 'Entry already prohibited by "oneway" tag on {var1}.' 300: title: 'Missing maxspeed' - description: 'Missing maxspeed tag.' + description: 'This road is missing a "maxspeed" tag and is tagged as motorway, trunk, primary, or secondary.' 310: title: 'Roundabout issue' description: 'There is an unspecified issue with this roundabout.' 311: title: 'Roundabout not closed loop' - description: 'This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout).' + description: 'This way is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout).' 312: title: 'Roundabout wrong direction' - description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around' + description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around.' 313: title: 'Roundabout weakly connected' description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' @@ -849,7 +849,7 @@ en: 412: description: 'Possible domain squatting: The URL has suspicious text: "{var1}".' 413: - description: 'Possible non-match. Content of the URL did not contain these keywords: ({var1}).' + description: 'Possible non-match. Content of the URL did not contain these keywords: ({var1}).' streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/data/keepRight.json b/data/keepRight.json index cf9b3fcb7..0f9cdec9a 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -45,7 +45,7 @@ "regex": "way #(\\d+)" }, "60": { - "title": "depreciated tags", + "title": "deprecated tags", "severity": "warning", "description": "This $1 uses deprecated tag $2. Please use $3 instead!", "regex": "This (node|way|relation) uses deprecated tag '(.+)'\\. Please use "(.+)"" diff --git a/dist/locales/en.json b/dist/locales/en.json index 65609b06d..724bb79bf 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -878,7 +878,7 @@ "description": "This crossing of a highway and a railway needs to be tagged as \"railway=crossing\" or \"railway=level_crossing\"." }, "160": { - "title": "Wrongly used railway tag", + "title": "Railway layer conflict", "description": "There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing." }, "170": { @@ -915,7 +915,7 @@ "description": "This {var1} has a tag with key \"{var2}\"." }, "230": { - "title": "Layer Conflict", + "title": "Layer conflict", "description": "This node is a junction of ways on different layers." }, "231": { @@ -959,7 +959,7 @@ }, "291": { "title": "Restriction missing type", - "description": "This turn restriction has no known restriction type." + "description": "This turn restriction has an unrecognized restriction type." }, "292": { "title": "Restriction missing \"from\" way", @@ -974,11 +974,11 @@ "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {var1}." }, "295": { - "title": "Restriction \"via\" is not on the way ends", + "title": "Restriction \"via\" is not an endpoint", "description": "\"via\" (node {var1}) is not the first or the last member of \"{var2}\" (way {var3})." }, "296": { - "title": "Wrong restriction angle", + "title": "Unusual restriction angle", "description": "Restriction type is \"{var1}\" but angle is {var2} degrees. Maybe the restriction type is not appropriate?" }, "297": { @@ -991,7 +991,7 @@ }, "300": { "title": "Missing maxspeed", - "description": "Missing maxspeed tag." + "description": "This road is missing a \"maxspeed\" tag and is tagged as motorway, trunk, primary, or secondary." }, "310": { "title": "Roundabout issue", @@ -999,11 +999,11 @@ }, "311": { "title": "Roundabout not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)." + "description": "This way is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout)." }, "312": { "title": "Roundabout wrong direction", - "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around" + "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around." }, "313": { "title": "Roundabout weakly connected", @@ -1056,7 +1056,7 @@ "description": "Possible domain squatting: The URL has suspicious text: \"{var1}\"." }, "413": { - "description": "Possible non-match. Content of the URL did not contain these keywords: ({var1})." + "description": "Possible non-match. Content of the URL did not contain these keywords: ({var1})." } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 9bae8840e..23d7d47c6 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -23,11 +23,13 @@ var _krCache; var _krZoom = 14; var apibase = 'https://www.keepright.at/'; var defaultRuleset = [ - 0, 30, 40, 50, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, - 191, 192, 193, 194, 195, 196, 197, 198, 201, 202, 203, 204, 205, - 206, 207, 208, 210, 220, 231, 232, 270, 281, 282, 283, 284, 285, - 291, 292 ,293, 294, 295, 296, 297, 298, 311, 312, 313, 320, 350, - 370, 380, 401, 402, 411, 412, 413 + // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads + 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, + 190, 191, 192, 193, 194, 195, 196, 197, 198, + 200, 201, 202, 203, 204, 205, 206, 207, 208, 210, 220, + 230, 231, 232, 270, 280, 281, 282, 283, 284, 285, + 290, 291, 292, 293, 294, 295, 296, 297, 298, 300, 310, 311, 312, 313, + 320, 350, 360, 370, 380, 390, 400, 401, 402, 410, 411, 412, 413 ]; diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 0ef3c6146..8e294bb18 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -97,7 +97,7 @@ export function svgKeepRight(projection, context, dispatch) { var kr_errorsEnter = kr_errors.enter() .append('g') .attr('class', function(d) { - return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; } + return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.parent_error_type; } ); kr_errorsEnter diff --git a/modules/ui/keepRight_header.js b/modules/ui/keepRight_header.js index e2a63e2f4..1c8d9533c 100644 --- a/modules/ui/keepRight_header.js +++ b/modules/ui/keepRight_header.js @@ -49,7 +49,7 @@ export function uiKeepRightHeader() { iconEnter .append('div') .attr('class', function(d) { - return 'preset-icon-28 kr_error kr_error-' + d.id + ' kr_error_type_' + d.error_type; + return 'preset-icon-28 kr_error kr_error-' + d.id + ' kr_error_type_' + d.parent_error_type; }) .call(svgIcon('#iD-icon-bolt', 'kr_error-fill')); From d0343c9fa7832f6bf18ac1433a49073d3e4db6c6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 18:16:28 -0500 Subject: [PATCH 071/114] Reintroduce some error localization, fix some nagging parse issues --- data/core.yaml | 64 ++++++++++++++++++-------- data/keepRight.json | 16 +++---- dist/locales/en.json | 66 ++++++++++++++++++--------- modules/services/keepRight.js | 84 +++++++++++++++++++++++++++-------- 4 files changed, 164 insertions(+), 66 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 03e6c35bc..ebec1d8f9 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -659,19 +659,44 @@ en: save_comment: Save Comment close_comment: Close and Comment ignore_comment: Ignore and Comment - entities: - node: "Node {id}" - way: "Way {id}" - relation: "Relation {id}" + error_parts: + node: node + way: way + relation: relation + highway: highway + railway: railway + waterway: waterway + cycleway: cycleway + cycleway_footpath: 'cycleway/footpath' + riverbank: riverbank + bridge: bridge + tunnel: tunnel + place_of_worship: 'place of worship' + pub: pub + restaurant: restaurant + school: school + university: university + hospital: hospital + library: library + theatre: theatre + courthouse: courthouse + bank: bank + cinema: cinema + pharmacy: pharmacy + cafe: cafe + fast_food: 'fast food' + fuel: fuel + from: from + to: to errorTypes: 20: title: 'Multiple nodes on the same spot' description: 'There is more than one node in this spot. Node IDs: {var1}.' 30: - title: 'Non-closed areas' + title: 'Non-closed area' description: 'This way is tagged with "{var1}" and should be a closed loop.' 40: - title: 'Impossible oneways' + title: 'Impossible oneway' description: 'The first node {var1} of this oneway is not connected to any other way.' 41: description: 'The last node {var1} of this oneway is not connected to any other way.' @@ -680,13 +705,13 @@ en: 43: description: 'You cannot escape from this node because all ways leading to it are oneway.' 50: - title: 'Almost junctions' + title: 'Almost junction' description: 'This node is very close but not connected to way {var1}.' 60: - title: 'Deprecated tags' + title: 'Deprecated tag' description: 'This {var1} uses deprecated tag "{var2}". Please use "{var3}" instead.' 70: - title: 'Missing tags' + title: 'Missing tag' description: 'This {var1} has an empty tag: "{var2}".' 71: description: 'This way has no tags.' @@ -720,19 +745,19 @@ en: title: 'Railway layer conflict' description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing.' 170: - title: 'FIXME tagged items' + title: 'FIXME tagged item' description: '{var1}' 180: title: 'Relation without type' description: 'This relation is missing a "type" tag.' 190: - title: 'Intersection without junctions' + title: 'Intersection without junction' description: 'This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel.' 200: title: 'Overlapping ways' description: 'This {var1} overlaps the {var2} {var3}.' 210: - title: 'Self-intersecting ways' + title: 'Self-intersecting way' description: 'There is an unspecified issue with self intersecting ways.' 211: description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' @@ -742,7 +767,7 @@ en: title: 'Misspelled tag' description: 'This {var1} is tagged "{var2}" where "{var3}" looks like "{var4}".' 221: - description: 'This {var1} has a tag with key "{var2}".' + description: 'This {var1} has a suspicious tag "{var2}".' 230: title: 'Layer conflict' description: 'This node is a junction of ways on different layers.' @@ -816,19 +841,20 @@ en: description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' 320: title: 'Improper link connection' - description: 'This way is tagged as "highway={var1}_link" but doesn''t have a connection to any other "{var2}" or "{var3}_link".' + description: 'This way is tagged as "{var1}" but doesn''t have a connection to any other "{var2}" or "{var3}".' 350: - title: 'Improper bridge tags' + title: 'Improper bridge tag' description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' 360: - title: 'Language unknown' - description: 'It would be nice if this {var1} had an additional tag "name:XX"="{var2}" where XX shows the language of its name "{var2}".' + title: 'Missing local name tag' + description: 'It would be nice if this {var1} had a local name tag "name:XX={var2}" where XX shows the language of its common name "{var2}".' 370: title: 'Doubled places' description: 'This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant.' + including_the_name: "(including the name {name})" 380: title: 'Non-physical use of sport tag' - description: 'This way is tagged "sport={var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' + description: 'This way is tagged "{var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' 390: title: 'Missing tracktype' description: This track doesn't have a "tracktype" tag. @@ -839,7 +865,7 @@ en: title: 'Missing turn restriction' description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning.' 402: - title: 'Impossible angles' + title: 'Impossible angle' description: 'This way bends in a very sharp angle here.' 410: title: 'Website issue' diff --git a/data/keepRight.json b/data/keepRight.json index 0f9cdec9a..e9a168260 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -169,7 +169,7 @@ "title": "", "severity": "error", "description": "The key of this $1's tag is 'key': $2", - "regex": "this (node|way|relation)''s tag is ''key'': key=(.+)" + "regex": "this (node|way|relation)\\'s tag is \\'key\\': (.+)" }, "230": { "title": "layer conflicts", @@ -214,13 +214,13 @@ "title": "no closed loop", "severity": "error", "description": "The boundary of $1 is not closed-loop", - "regex": "boundary of (.+)" + "regex": "boundary of (.+) is" }, "284": { "title": "splitting boundary", "severity": "error", "description": "The boundary of $1 splits here", - "regex": "boundary of (.+)" + "regex": "boundary of (.+) splits" }, "285": { "title": "admin_level too high", @@ -262,7 +262,7 @@ "title": "via is not on the way ends", "severity": "error", "description": "via (node #$1) is not the first or the last member of (from|to) (way #$3)", - "IDs": ["n","w"], + "IDs": ["n", "", "w"], "regex": "via \\(node #(\\d+)\\) is not the first or the last member of (from|to) \\(way #(\\d+)\\)" }, "296": { @@ -316,7 +316,7 @@ "title": "*_link connections", "severity": "error", "description": "This way is tagged as highway=$1_link but doesn't have a connection to any other $1 or $1_link", - "regex": "highway=(\\w+)_link" + "regex": "(highway=.+) but doesn't have a connection to any other (.+) or (.+)" }, "350": { "title": "bridge-tags", @@ -335,14 +335,14 @@ "title": "doubled places", "severity": "error", "description": "This node has tags in common with the surrounding way #$1 ((?:\\(including the name '.+'\\) )?)and seems to be redundand", - "IDs": ["w"], + "IDs": ["w","370"], "regex": "way #(\\d+) ((?:\\(including the name '.+'\\) )?)and" }, "380": { "title": "non-physical use of sport-tag", "severity": "error", "description": "This way is tagged sport=$1 but has no physical tag like e.g. leisure, building, amenity or highway", - "regex": "sport=(\\w+) but" + "regex": "(sport=.+) but" }, "390": { "title": "missing tracktype", @@ -381,7 +381,7 @@ "title": "domain hijacking", "severity": "error", "description": "Possible domain squatting: $1. Suspicious text is: \"$2\"", - "regex": "Possible domain squatting: \\1\\. Suspicious text is: ''(.+)''" + "regex": "Possible domain squatting: \\1\\. Suspicious text is: "(.+)"" }, "413": { "title": "non-match", diff --git a/dist/locales/en.json b/dist/locales/en.json index 724bb79bf..e4b68378b 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -799,10 +799,35 @@ "save_comment": "Save Comment", "close_comment": "Close and Comment", "ignore_comment": "Ignore and Comment", - "entities": { - "node": "Node {id}", - "way": "Way {id}", - "relation": "Relation {id}" + "error_parts": { + "node": "node", + "way": "way", + "relation": "relation", + "highway": "highway", + "railway": "railway", + "waterway": "waterway", + "cycleway": "cycleway", + "cycleway_footpath": "cycleway/footpath", + "riverbank": "riverbank", + "bridge": "bridge", + "tunnel": "tunnel", + "place_of_worship": "place of worship", + "pub": "pub", + "restaurant": "restaurant", + "school": "school", + "university": "university", + "hospital": "hospital", + "library": "library", + "theatre": "theatre", + "courthouse": "courthouse", + "bank": "bank", + "cinema": "cinema", + "pharmacy": "pharmacy", + "cafe": "cafe", + "fast_food": "fast food", + "fuel": "fuel", + "from": "from", + "to": "to" }, "errorTypes": { "20": { @@ -810,11 +835,11 @@ "description": "There is more than one node in this spot. Node IDs: {var1}." }, "30": { - "title": "Non-closed areas", + "title": "Non-closed area", "description": "This way is tagged with \"{var1}\" and should be a closed loop." }, "40": { - "title": "Impossible oneways", + "title": "Impossible oneway", "description": "The first node {var1} of this oneway is not connected to any other way." }, "41": { @@ -827,15 +852,15 @@ "description": "You cannot escape from this node because all ways leading to it are oneway." }, "50": { - "title": "Almost junctions", + "title": "Almost junction", "description": "This node is very close but not connected to way {var1}." }, "60": { - "title": "Deprecated tags", + "title": "Deprecated tag", "description": "This {var1} uses deprecated tag \"{var2}\". Please use \"{var3}\" instead." }, "70": { - "title": "Missing tags", + "title": "Missing tag", "description": "This {var1} has an empty tag: \"{var2}\"." }, "71": { @@ -882,7 +907,7 @@ "description": "There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing." }, "170": { - "title": "FIXME tagged items", + "title": "FIXME tagged item", "description": "{var1}" }, "180": { @@ -890,7 +915,7 @@ "description": "This relation is missing a \"type\" tag." }, "190": { - "title": "Intersection without junctions", + "title": "Intersection without junction", "description": "This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel." }, "200": { @@ -898,7 +923,7 @@ "description": "This {var1} overlaps the {var2} {var3}." }, "210": { - "title": "Self-intersecting ways", + "title": "Self-intersecting way", "description": "There is an unspecified issue with self intersecting ways." }, "211": { @@ -912,7 +937,7 @@ "description": "This {var1} is tagged \"{var2}\" where \"{var3}\" looks like \"{var4}\"." }, "221": { - "description": "This {var1} has a tag with key \"{var2}\"." + "description": "This {var1} has a suspicious tag \"{var2}\"." }, "230": { "title": "Layer conflict", @@ -1011,23 +1036,24 @@ }, "320": { "title": "Improper link connection", - "description": "This way is tagged as \"highway={var1}_link\" but doesn't have a connection to any other \"{var2}\" or \"{var3}_link\"." + "description": "This way is tagged as \"{var1}\" but doesn't have a connection to any other \"{var2}\" or \"{var3}\"." }, "350": { - "title": "Improper bridge tags", + "title": "Improper bridge tag", "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." }, "360": { - "title": "Language unknown", - "description": "It would be nice if this {var1} had an additional tag \"name:XX\"=\"{var2}\" where XX shows the language of its name \"{var2}\"." + "title": "Missing local name tag", + "description": "It would be nice if this {var1} had a local name tag \"name:XX={var2}\" where XX shows the language of its common name \"{var2}\"." }, "370": { "title": "Doubled places", - "description": "This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant." + "description": "This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant.", + "including_the_name": "(including the name {name})" }, "380": { "title": "Non-physical use of sport tag", - "description": "This way is tagged \"sport={var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." + "description": "This way is tagged \"{var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." }, "390": { "title": "Missing tracktype", @@ -1042,7 +1068,7 @@ "description": "Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning." }, "402": { - "title": "Impossible angles", + "title": "Impossible angle", "description": "This way bends in a very sharp angle here." }, "410": { diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 23d7d47c6..a1be47f5d 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -21,7 +21,39 @@ var dispatch = d3_dispatch('loaded'); var _krCache; var _krZoom = 14; -var apibase = 'https://www.keepright.at/'; +var _krUrlRoot = 'https://www.keepright.at/'; +var _krLocalize = { + node: 'node', + way: 'way', + relation: 'relation', + highway: 'highway', + railway: 'railway', + waterway: 'waterway', + cycleway: 'cycleway', + footpath: 'footpath', + 'cycleway/footpath': 'cycleway_footpath', + riverbank: 'riverbank', + bridge: 'bridge', + tunnel: 'tunnel', + place_of_worship: 'place_of_worship', + pub: 'pub', + restaurant: 'restaurant', + school: 'school', + university: 'university', + hospital: 'hospital', + library: 'library', + theatre: 'theatre', + courthouse: 'courthouse', + bank: 'bank', + cinema: 'cinema', + pharmacy: 'pharmacy', + cafe: 'cafe', + fast_food: 'fast_food', + fuel: 'fuel', + from: 'from', + to: 'to' +}; + var defaultRuleset = [ // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, @@ -72,13 +104,13 @@ function updateRtree(item, replace) { function tokenReplacements(d) { if (!(d instanceof krError)) return; + var htmlRegex = new RegExp(/<\/[a-z][\s\S]*>/); var replacements = {}; - var html_re = new RegExp(/<\/[a-z][\s\S]*>/); var errorTemplate = errorTypes[d.which_type]; if (!errorTemplate) { /* eslint-disable no-console */ - console.log('No Template: ', d.error_type); + console.log('No Template: ', d.which_type); console.log(' ', d.description); /* eslint-enable no-console */ return; @@ -92,7 +124,7 @@ function tokenReplacements(d) { var errorMatch = errorRegex.exec(d.description); if (!errorMatch) { /* eslint-disable no-console */ - console.log('Unmatched: ', d.error_type); + console.log('Unmatched: ', d.which_type); console.log(' ', d.description); console.log(' ', errorRegex); /* eslint-enable no-console */ @@ -103,13 +135,13 @@ function tokenReplacements(d) { var group = errorMatch[i]; var idType; - // link IDs if present in the group idType = 'IDs' in errorTemplate ? errorTemplate.IDs[i-1] : ''; - if (idType && group) { + if (idType && group) { // link IDs if present in the group group = parseError(group, idType); - } else if (html_re.test(group)) { - // escape any html in non-IDs + } else if (htmlRegex.test(group)) { // escape any html in non-IDs group = '\\' + group + '\\'; + } else if (_krLocalize[group]) { // some replacement strings can be localized + group = t('QA.keepRight.error_parts.' + _krLocalize[group]); } replacements['var' + i] = group; @@ -126,9 +158,9 @@ function parseError(group, idType) { } // arbitrary node list of form: #ID, #ID, #ID... - function parseError211(list) { + function parseError211(capture) { var newList = []; - var items = list.split(', '); + var items = capture.split(', '); items.forEach(function(item) { // ID has # at the front @@ -140,10 +172,10 @@ function parseError(group, idType) { } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... - function parseError231(list) { + function parseError231(capture) { var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),' - var items = list.split('),'); + var items = capture.split('),'); items.forEach(function(item) { var match = item.match(/\#(\d+)\((.+)\)?/); @@ -158,9 +190,9 @@ function parseError(group, idType) { } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... - function parseError294(list) { + function parseError294(capture) { var newList = []; - var items = list.split(','); + var items = capture.split(','); items.forEach(function(item) { var role; @@ -187,10 +219,21 @@ function parseError(group, idType) { return newList.join(', '); } + // may or may not include the string "(including the name 'name')" + function parseError370(capture) { + if (!capture) return ''; + + var match = capture.match(/\(including the name (\'.+\')\)/); + if (match !== null && match.length) { + return t('QA.keepRight.errorTypes.370.including_the_name', { name: match[1] }); + } + return ''; + } + // arbitrary node list of form: #ID,#ID,#ID... - function parseWarning20(list) { + function parseWarning20(capture) { var newList = []; - var items = list.split(','); + var items = capture.split(','); items.forEach(function(item) { // ID has # at the front @@ -218,6 +261,9 @@ function parseError(group, idType) { case '294': group = parseError294(group); break; + case '370': + group = parseError370(group); + break; case '20': group = parseWarning20(group); } @@ -262,7 +308,7 @@ export default { var rect = tile.extent.rectangle(); var params = _extend({}, options, { left: rect[0], bottom: rect[3], right: rect[2], top: rect[1] }); - var url = apibase + 'export.php?' + utilQsString(params) + '&ch=' + rules; + var url = _krUrlRoot + 'export.php?' + utilQsString(params) + '&ch=' + rules; _krCache.inflight[tile.id] = d3_json(url, function(err, data) { @@ -343,7 +389,7 @@ export default { // NOTE: This throws a CORS err, but it seems successful. // We don't care too much about the response, so this is fine. - var url = apibase + 'comment.php?' + utilQsString(params); + var url = _krUrlRoot + 'comment.php?' + utilQsString(params); _krCache.inflight[d.id] = d3_request(url) .post(function(err) { delete _krCache.inflight[d.id]; @@ -402,7 +448,7 @@ export default { errorURL: function(error) { - return apibase + 'report_map.php?schema=' + error.schema + '&error=' + error.id; + return _krUrlRoot + 'report_map.php?schema=' + error.schema + '&error=' + error.id; } }; From 10ece546f47cd721285f8c9d58db4466d0e88a29 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 18:45:21 -0500 Subject: [PATCH 072/114] Give missing maxspeed its own marker color style There are a lot of these --- css/65_data.css | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/css/65_data.css b/css/65_data.css index 42905af5c..1ee9b1cd2 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -88,7 +88,6 @@ .kr_error_type_110, /* poi without name */ .kr_error_type_150, /* railway crossing without tag */ .kr_error_type_220, /* misspelled tag */ -.kr_error_type_300, /* missing maxspeed */ .kr_error_type_380, /* non-physical sport tag */ .kr_error_type_390 { /* missing tracktype */ color: #5d0; @@ -124,6 +123,10 @@ color: #ace; } +.kr_error_type_300 { /* missing maxspeed */ + color: #090; +} + .kr_error_type_360, /* language unknown */ .kr_error_type_370, /* doubled places */ .kr_error_type_410 { /* website issues */ From 67403a2141479a4ca591b38628b080092ec46285 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 3 Jan 2019 21:39:16 -0500 Subject: [PATCH 073/114] Style missing tracktype like missing maxspeed --- css/65_data.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/css/65_data.css b/css/65_data.css index 1ee9b1cd2..a008021c8 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -88,8 +88,7 @@ .kr_error_type_110, /* poi without name */ .kr_error_type_150, /* railway crossing without tag */ .kr_error_type_220, /* misspelled tag */ -.kr_error_type_380, /* non-physical sport tag */ -.kr_error_type_390 { /* missing tracktype */ +.kr_error_type_380 { /* non-physical sport tag */ color: #5d0; } @@ -123,7 +122,8 @@ color: #ace; } -.kr_error_type_300 { /* missing maxspeed */ +.kr_error_type_300, /* missing maxspeed */ +.kr_error_type_390 { /* missing tracktype */ color: #090; } From a1bc6b3e31a899179b98bb51d402db518192a68a Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 00:57:18 -0500 Subject: [PATCH 074/114] Rename "Pharmacy" preset to "Pharmacy Counter" (re: #3213, https://github.com/osmlab/name-suggestion-index/issues/30) --- data/presets.yaml | 4 ++-- data/presets/presets.json | 2 +- data/presets/presets/amenity/pharmacy.json | 2 +- data/taginfo.json | 2 +- dist/locales/en.json | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 65cbdc346..fb785aef9 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -2553,9 +2553,9 @@ en: terms: '' amenity/pharmacy: # amenity=pharmacy - name: Pharmacy + name: Pharmacy Counter # 'terms: apothecary,drug*,med*,prescription' - terms: '' + terms: '' amenity/place_of_worship: # amenity=place_of_worship name: Place of Worship diff --git a/data/presets/presets.json b/data/presets/presets.json index 09de5cce3..e6b3b3420 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -117,7 +117,7 @@ "amenity/parking/multi-storey": {"icon": "maki-car", "fields": ["name", "operator", "building", "levels", "height", "address", "capacity", "fee", "access_simple"], "geometry": ["area"], "tags": {"amenity": "parking", "parking": "multi-storey"}, "addTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "removeTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "reference": {"key": "parking", "value": "multi-storey"}, "terms": ["car", "indoor parking", "multistorey car park", "parkade", "parking building", "parking deck", "parking garage", "parking ramp", "parking structure"], "name": "Multilevel Parking Garage"}, "amenity/payment_centre": {"icon": "maki-bank", "fields": ["name", "brand", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["check", "tax pay", "bill pay", "currency", "finance", "cash", "money"], "tags": {"amenity": "payment_centre"}, "name": "Payment Center"}, "amenity/payment_terminal": {"icon": "maki-bank", "fields": ["name", "brand", "address", "opening_hours", "payment_multi"], "geometry": ["point"], "terms": ["interactive kiosk", "ekiosk", "atm", "bill pay", "tax pay", "phone pay", "finance", "cash", "money transfer", "card"], "tags": {"amenity": "payment_terminal"}, "name": "Payment Terminal"}, - "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug*", "med*", "prescription"], "name": "Pharmacy"}, + "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug*", "med*", "prescription"], "name": "Pharmacy Counter"}, "amenity/place_of_worship": {"icon": "maki-place-of-worship", "fields": ["name", "religion", "denomination", "address", "building_area", "service_times"], "geometry": ["point", "area"], "terms": ["abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "church", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "mosque", "oratory", "parish", "sacellum", "sanctuary", "shrine", "synagogue", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship"}, "name": "Place of Worship"}, "amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"}, "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"}, diff --git a/data/presets/presets/amenity/pharmacy.json b/data/presets/presets/amenity/pharmacy.json index 69f0a5d9e..815956f29 100644 --- a/data/presets/presets/amenity/pharmacy.json +++ b/data/presets/presets/amenity/pharmacy.json @@ -35,5 +35,5 @@ "med*", "prescription" ], - "name": "Pharmacy" + "name": "Pharmacy Counter" } diff --git a/data/taginfo.json b/data/taginfo.json index 83f457bf9..9eeff62dd 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -794,7 +794,7 @@ { "key": "amenity", "value": "pharmacy", - "description": "🄿 Pharmacy", + "description": "🄿 Pharmacy Counter", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/pharmacy-15.svg?sanitize=true" }, diff --git a/dist/locales/en.json b/dist/locales/en.json index 3fa139d53..b50e0117d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -3688,7 +3688,7 @@ "terms": "interactive kiosk,ekiosk,atm,bill pay,tax pay,phone pay,finance,cash,money transfer,card" }, "amenity/pharmacy": { - "name": "Pharmacy", + "name": "Pharmacy Counter", "terms": "apothecary,drug*,med*,prescription" }, "amenity/place_of_worship": { From 21f1891899fd47b72d5428d57036530b7c210404 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 01:37:18 -0500 Subject: [PATCH 075/114] Adjust pharmacy/chemist terms so they both appear on searches (re: 3213) --- data/presets.yaml | 4 ++-- data/presets/presets.json | 4 ++-- data/presets/presets/amenity/pharmacy.json | 3 ++- data/presets/presets/shop/chemist.json | 10 ++++++++-- dist/locales/en.json | 4 ++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index fb785aef9..5e65082f4 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -2554,7 +2554,7 @@ en: amenity/pharmacy: # amenity=pharmacy name: Pharmacy Counter - # 'terms: apothecary,drug*,med*,prescription' + # 'terms: apothecary,drug store,drugstore,med*,prescription' terms: '' amenity/place_of_worship: # amenity=place_of_worship @@ -5982,7 +5982,7 @@ en: shop/chemist: # shop=chemist name: Drugstore - # 'terms: apothecary,med*,drug*,gift' + # 'terms: apothecary,beauty,drug store,drugstore,gift,hair,med*,pharmacy,prescription,tooth' terms: '' shop/chocolate: # shop=chocolate diff --git a/data/presets/presets.json b/data/presets/presets.json index e6b3b3420..c1a223348 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -117,7 +117,7 @@ "amenity/parking/multi-storey": {"icon": "maki-car", "fields": ["name", "operator", "building", "levels", "height", "address", "capacity", "fee", "access_simple"], "geometry": ["area"], "tags": {"amenity": "parking", "parking": "multi-storey"}, "addTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "removeTags": {"building": "parking", "amenity": "parking", "parking": "multi-storey"}, "reference": {"key": "parking", "value": "multi-storey"}, "terms": ["car", "indoor parking", "multistorey car park", "parkade", "parking building", "parking deck", "parking garage", "parking ramp", "parking structure"], "name": "Multilevel Parking Garage"}, "amenity/payment_centre": {"icon": "maki-bank", "fields": ["name", "brand", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["check", "tax pay", "bill pay", "currency", "finance", "cash", "money"], "tags": {"amenity": "payment_centre"}, "name": "Payment Center"}, "amenity/payment_terminal": {"icon": "maki-bank", "fields": ["name", "brand", "address", "opening_hours", "payment_multi"], "geometry": ["point"], "terms": ["interactive kiosk", "ekiosk", "atm", "bill pay", "tax pay", "phone pay", "finance", "cash", "money transfer", "card"], "tags": {"amenity": "payment_terminal"}, "name": "Payment Terminal"}, - "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug*", "med*", "prescription"], "name": "Pharmacy Counter"}, + "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug store", "drugstore", "med*", "prescription"], "name": "Pharmacy Counter"}, "amenity/place_of_worship": {"icon": "maki-place-of-worship", "fields": ["name", "religion", "denomination", "address", "building_area", "service_times"], "geometry": ["point", "area"], "terms": ["abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "church", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "mosque", "oratory", "parish", "sacellum", "sanctuary", "shrine", "synagogue", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship"}, "name": "Place of Worship"}, "amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"}, "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"}, @@ -862,7 +862,7 @@ "shop/catalogue": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "catalogue"}, "name": "Catalog Shop"}, "shop/charity": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "second_hand", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["thrift", "op shop", "nonprofit"], "tags": {"shop": "charity"}, "name": "Charity Store"}, "shop/cheese": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "cheese"}, "name": "Cheese Store"}, - "shop/chemist": {"icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chemist"}, "terms": ["apothecary", "med*", "drug*", "gift"], "name": "Drugstore"}, + "shop/chemist": {"icon": "maki-grocery", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chemist"}, "terms": ["apothecary", "beauty", "drug store", "drugstore", "gift", "hair", "med*", "pharmacy", "prescription", "tooth"], "name": "Drugstore"}, "shop/chocolate": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "chocolate"}, "name": "Chocolate Store"}, "shop/clothes": {"icon": "maki-clothing-store", "fields": ["name", "clothes", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "clothes"}, "name": "Clothing Store"}, "shop/coffee": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "coffee"}, "name": "Coffee Store"}, diff --git a/data/presets/presets/amenity/pharmacy.json b/data/presets/presets/amenity/pharmacy.json index 815956f29..495097150 100644 --- a/data/presets/presets/amenity/pharmacy.json +++ b/data/presets/presets/amenity/pharmacy.json @@ -31,7 +31,8 @@ }, "terms": [ "apothecary", - "drug*", + "drug store", + "drugstore", "med*", "prescription" ], diff --git a/data/presets/presets/shop/chemist.json b/data/presets/presets/shop/chemist.json index f093f4610..5dd15f2e0 100644 --- a/data/presets/presets/shop/chemist.json +++ b/data/presets/presets/shop/chemist.json @@ -17,9 +17,15 @@ }, "terms": [ "apothecary", + "beauty", + "drug store", + "drugstore", + "gift", + "hair", "med*", - "drug*", - "gift" + "pharmacy", + "prescription", + "tooth" ], "name": "Drugstore" } diff --git a/dist/locales/en.json b/dist/locales/en.json index b50e0117d..7f0522eab 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -3689,7 +3689,7 @@ }, "amenity/pharmacy": { "name": "Pharmacy Counter", - "terms": "apothecary,drug*,med*,prescription" + "terms": "apothecary,drug store,drugstore,med*,prescription" }, "amenity/place_of_worship": { "name": "Place of Worship", @@ -6669,7 +6669,7 @@ }, "shop/chemist": { "name": "Drugstore", - "terms": "apothecary,med*,drug*,gift" + "terms": "apothecary,beauty,drug store,drugstore,gift,hair,med*,pharmacy,prescription,tooth" }, "shop/chocolate": { "name": "Chocolate Store", From cd9203975d7ba15a567ac0d2a8bc9cba07aae330 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 15:48:39 -0500 Subject: [PATCH 076/114] Use touch targets for notes, fix a few bugs with note dragging (closes #5213) --- css/20_map.css | 9 +- css/55_cursors.css | 5 + css/65_data.css | 31 +------ modules/behavior/drag.js | 4 +- modules/behavior/hover.js | 6 +- modules/modes/drag_note.js | 37 +++++--- modules/modes/select_note.js | 1 + modules/renderer/map.js | 2 +- modules/svg/notes.js | 174 +++++++++++++++++++++++------------ modules/svg/touch.js | 2 +- 10 files changed, 163 insertions(+), 108 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index 0200a8317..fc575d830 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -31,7 +31,9 @@ /* No interactivity except what we specifically allow */ -.layer-osm * { +.data-layer.osm *, +.data-layer.notes *, +.data-layer.keepRight * { pointer-events: none; } @@ -42,6 +44,7 @@ /* `.target` objects are interactive */ /* They can be picked up, clicked, hovered, or things can connect to them */ +.note.target, .node.target, .turn .target { pointer-events: fill; @@ -78,7 +81,7 @@ /* NOTE: when more QA layers are added, replace kr_error with generic QA layer selector */ /* points, notes & QA */ -/* points & notes */ +/* points, notes, markers */ g.kr_error .stroke, g.note .stroke { stroke: #222; @@ -110,9 +113,7 @@ g.note .shadow { stroke-opacity: 0; } -g.kr_error.related:not(.selected) .shadow, g.kr_error.hover:not(.selected) .shadow, -g.note.related:not(.selected) .shadow, g.note.hover:not(.selected) .shadow, g.point.related:not(.selected) .shadow, g.point.hover:not(.selected) .shadow { diff --git a/css/55_cursors.css b/css/55_cursors.css index 466e21ae2..f473301c4 100644 --- a/css/55_cursors.css +++ b/css/55_cursors.css @@ -96,7 +96,12 @@ cursor: url(img/cursor-draw.png) 9 9, crosshair; /* FF */ } +.mode-browse .note, +.mode-browse .kr_error, +.mode-select .note, +.mode-select .kr_error, .turn rect, .turn circle { cursor: pointer; } + diff --git a/css/65_data.css b/css/65_data.css index a008021c8..71aafab9c 100644 --- a/css/65_data.css +++ b/css/65_data.css @@ -1,28 +1,5 @@ /* OSM Notes and KeepRight Layers */ -.layer-keepRight, -.layer-notes { - pointer-events: none; -} -.layer-keepRight .kr_error, -.layer-notes .note * { - pointer-events: none; -} -.mode-browse .layer-notes .note .note-fill, -.mode-select .layer-notes .note .note-fill, -.mode-select-data .layer-notes .note .note-fill, -.mode-select-note .layer-notes .note .note-fill, -.layer-keepRight .kr_error .kr_error-fill, -.layer-notes .note .note-fill { - pointer-events: visible; - cursor: pointer; /* Opera */ - cursor: url(img/cursor-select-point.png), pointer; /* FF */ -} - -.note-header-icon .note-shadow, -.layer-notes .note .note-shadow { - color: #000; -} .kr_error-header-icon .kr_error-fill, .layer-keepRight .kr_error .kr_error-fill { @@ -32,19 +9,19 @@ .note-header-icon .note-fill, .layer-notes .note .note-fill { - color: #ff3300; + color: #f30; stroke: #333; stroke-width: 40px; } .note-header-icon.new .note-fill, .layer-notes .note.new .note-fill { - color: #ffee00; + color: #fe0; stroke: #333; stroke-width: 40px; } .note-header-icon.closed .note-fill, .layer-notes .note.closed .note-fill { - color: #55dd00; + color: #5d0; stroke: #333; stroke-width: 40px; } @@ -88,7 +65,7 @@ .kr_error_type_110, /* poi without name */ .kr_error_type_150, /* railway crossing without tag */ .kr_error_type_220, /* misspelled tag */ -.kr_error_type_380 { /* non-physical sport tag */ +.kr_error_type_380 { /* non-physical sport tag */ color: #5d0; } diff --git a/modules/behavior/drag.js b/modules/behavior/drag.js index 0221149ec..5dd432975 100644 --- a/modules/behavior/drag.js +++ b/modules/behavior/drag.js @@ -160,8 +160,8 @@ export function behaviorDrag() { for (; target && target !== root; target = target.parentNode) { var datum = target.__data__; - var entity = datum instanceof osmNote ? - datum : datum && datum.properties && datum.properties.entity; + var entity = datum instanceof osmNote ? datum + : datum && datum.properties && datum.properties.entity; if (entity && target[matchesSelector](_selector)) { return dragstart.call(target, entity); diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index 10ba3a3bd..ad5d1c43d 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -112,7 +112,11 @@ export function behaviorHover(context) { entity = datum; selector = '.data' + datum.__featurehash__; - } else if (datum instanceof osmNote || datum instanceof krError) { + } else if (datum instanceof krError) { + entity = datum; + selector = '.error-' + datum.id; + + } else if (datum instanceof osmNote) { entity = datum; selector = '.note-' + datum.id; diff --git a/modules/modes/drag_note.js b/modules/modes/drag_note.js index 7f834cb10..5c9714fc6 100644 --- a/modules/modes/drag_note.js +++ b/modules/modes/drag_note.js @@ -20,13 +20,14 @@ export function modeDragNote(context) { var _nudgeInterval; var _lastLoc; + var _note; // most current note.. dragged note may have stale datum. - function startNudge(note, nudge) { + function startNudge(nudge) { if (_nudgeInterval) window.clearInterval(_nudgeInterval); _nudgeInterval = window.setInterval(function() { context.pan(nudge); - doMove(note, nudge); + doMove(nudge); }, 50); } @@ -45,58 +46,66 @@ export function modeDragNote(context) { function start(note) { - context.surface().selectAll('.note-' + note.id) + _note = note; + var osm = services.osm; + if (osm) { + // Get latest note from cache.. The marker may have a stale datum bound to it + // and dragging it around can sometimes delete the users note comment. + _note = osm.getNote(_note.id); + } + + context.surface().selectAll('.note-' + _note.id) .classed('active', true); context.perform(actionNoop()); context.enter(mode); - context.selectedNoteID(note.id); + context.selectedNoteID(_note.id); } - function move(note) { + function move() { d3_event.sourceEvent.stopPropagation(); _lastLoc = context.projection.invert(d3_event.point); - doMove(note); + doMove(); var nudge = geoViewportEdge(d3_event.point, context.map().dimensions()); if (nudge) { - startNudge(note, nudge); + startNudge(nudge); } else { stopNudge(); } } - function doMove(note, nudge) { + function doMove(nudge) { nudge = nudge || [0, 0]; var currPoint = (d3_event && d3_event.point) || context.projection(_lastLoc); var currMouse = geoVecSubtract(currPoint, nudge); var loc = context.projection.invert(currMouse); - note = note.move(loc); + _note = _note.move(loc); var osm = services.osm; if (osm) { - osm.replaceNote(note); // update note cache + osm.replaceNote(_note); // update note cache } context.replace(actionNoop()); // trigger redraw } - function end(note) { + function end() { context.replace(actionNoop()); // trigger redraw context - .selectedNoteID(note.id) - .enter(modeSelectNote(context, note.id)); + .selectedNoteID(_note.id) + .enter(modeSelectNote(context, _note.id)); } var drag = behaviorDrag() - .selector('.layer-notes .new') + .selector('.layer-touch.markers .target.note.new') .surface(d3_select('#map').node()) .origin(origin) .on('start', start) diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index 4e5fd5d15..dd24ffd78 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -72,6 +72,7 @@ export function modeSelectNote(context, selectedNoteID) { } else { selection .classed('selected', true); + context.selectedNoteID(selectedNoteID); } } diff --git a/modules/renderer/map.js b/modules/renderer/map.js index ed6895871..3c4c0c3f8 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -351,7 +351,7 @@ export function rendererMap(context) { function editOff() { context.features().resetStats(); surface.selectAll('.layer-osm *').remove(); - surface.selectAll('.layer-touch *').remove(); + surface.selectAll('.layer-touch:not(.markers) *').remove(); var mode = context.mode(); if (mode && mode.id !== 'save' && mode.id !== 'select-note' && diff --git a/modules/svg/notes.js b/modules/svg/notes.js index 9e1d2c1aa..7df4d876a 100644 --- a/modules/svg/notes.js +++ b/modules/svg/notes.js @@ -8,12 +8,18 @@ import { svgPointTransform } from './index'; import { services } from '../services'; +var _notesEnabled = false; +var _osmService; + + export function svgNotes(projection, context, dispatch) { if (!dispatch) { dispatch = d3_dispatch('change'); } var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); var minZoom = 12; - var layer = d3_select(null); - var _notes; + var touchLayer = d3_select(null); + var drawLayer = d3_select(null); + var _notesVisible = false; + function markerPath(selection, klass) { selection @@ -22,40 +28,49 @@ export function svgNotes(projection, context, dispatch) { .attr('d', 'm17.5,0l-15,0c-1.37,0 -2.5,1.12 -2.5,2.5l0,11.25c0,1.37 1.12,2.5 2.5,2.5l3.75,0l0,3.28c0,0.38 0.43,0.6 0.75,0.37l4.87,-3.65l5.62,0c1.37,0 2.5,-1.12 2.5,-2.5l0,-11.25c0,-1.37 -1.12,-2.5 -2.5,-2.5z'); } - function init() { - if (svgNotes.initialized) return; // run once - svgNotes.enabled = false; - svgNotes.initialized = true; - } - - function editOn() { - layer.style('display', 'block'); - } - - - function editOff() { - layer.selectAll('.note').remove(); - layer.style('display', 'none'); - } - + // Loosely-coupled osm service for fetching notes. function getService() { - if (services.osm && !_notes) { - _notes = services.osm; - _notes.on('loadedNotes', throttledRedraw); - } else if (!services.osm && _notes) { - _notes = null; + if (services.osm && !_osmService) { + _osmService = services.osm; + _osmService.on('loadedNotes', throttledRedraw); + } else if (!services.osm && _osmService) { + _osmService = null; } - return _notes; + return _osmService; } - function showLayer() { + // Show the notes + function editOn() { + if (!_notesVisible) { + _notesVisible = true; + drawLayer + .style('display', 'block'); + } + } + + + // Immediately remove the notes and their touch targets + function editOff() { + if (_notesVisible) { + _notesVisible = false; + drawLayer + .style('display', 'none'); + drawLayer.selectAll('.note') + .remove(); + touchLayer.selectAll('.note') + .remove(); + } + } + + + // Enable the layer. This shows the notes and transitions them to visible. + function layerOn() { editOn(); - layer - .classed('disabled', false) + drawLayer .style('opacity', 0) .transition() .duration(250) @@ -66,30 +81,35 @@ export function svgNotes(projection, context, dispatch) { } - function hideLayer() { - editOff(); - + // Disable the layer. This transitions the layer invisible and then hides the notes. + function layerOff() { throttledRedraw.cancel(); - layer.interrupt(); + drawLayer.interrupt(); + touchLayer.selectAll('.note') + .remove(); - layer + drawLayer .transition() .duration(250) .style('opacity', 0) .on('end interrupt', function () { - layer.classed('disabled', true); + editOff(); dispatch.call('change'); }); - } - function update() { + // Update the note markers + function updateMarkers() { + if (!_notesVisible || !_notesEnabled) return; + var service = getService(); var selectedID = context.selectedNoteID(); var data = (service ? service.notes(projection) : []); - var transform = svgPointTransform(projection); - var notes = layer.selectAll('.note') + var getTransform = svgPointTransform(projection); + + // Draw markers.. + var notes = drawLayer.selectAll('.note') .data(data, function(d) { return d.status + d.id; }); // exit @@ -139,51 +159,89 @@ export function svgNotes(projection, context, dispatch) { // update notes .merge(notesEnter) - .sort(function(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : b.loc[1] - a.loc[1]; // sort Y + .sort(sortY) + .classed('selected', function(d) { + var mode = context.mode(); + var isMoving = mode && mode.id === 'drag-note'; // no shadows when dragging + return !isMoving && d.id === selectedID; }) - .classed('selected', function(d) { return d.id === selectedID; }) - .attr('transform', transform); + .attr('transform', getTransform); + + + // Draw targets.. + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + + var targets = touchLayer.selectAll('.note') + .data(data, function(d) { return d.id; }); + + // exit + targets.exit() + .remove(); + + // enter/update + targets.enter() + .append('rect') + .attr('width', '20px') + .attr('height', '20px') + .attr('x', '-8px') + .attr('y', '-22px') + .merge(targets) + .sort(sortY) + .attr('class', function(d) { + var newClass = (d.id < 0 ? 'new' : ''); + return 'note target note-' + d.id + ' ' + fillClass + newClass; + }) + .attr('transform', getTransform); + + + function sortY(a, b) { + return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1]; + } } + // Draw the notes layer and schedule loading notes and updating markers. function drawNotes(selection) { - var enabled = svgNotes.enabled; var service = getService(); - layer = selection.selectAll('.layer-notes') + var surface = context.surface(); + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-notes') .data(service ? [0] : []); - layer.exit() + drawLayer.exit() .remove(); - layer.enter() + drawLayer.enter() .append('g') .attr('class', 'layer-notes') - .style('display', enabled ? 'block' : 'none') - .merge(layer); + .style('display', _notesEnabled ? 'block' : 'none'); - if (enabled) { + if (_notesEnabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); service.loadNotes(projection); - update(); + updateMarkers(); } else { editOff(); } } } - drawNotes.enabled = function(val) { - if (!arguments.length) return svgNotes.enabled; - svgNotes.enabled = val; - if (svgNotes.enabled) { - showLayer(); + // Toggles the layer on and off + drawNotes.enabled = function(val) { + if (!arguments.length) return _notesEnabled; + + _notesEnabled = val; + if (_notesEnabled) { + layerOn(); } else { - hideLayer(); + layerOff(); if (context.selectedNoteID()) { context.enter(modeBrowse(context)); } @@ -193,6 +251,6 @@ export function svgNotes(projection, context, dispatch) { return this; }; - init(); + return drawNotes; } diff --git a/modules/svg/touch.js b/modules/svg/touch.js index 96bb1c871..860f95bd1 100644 --- a/modules/svg/touch.js +++ b/modules/svg/touch.js @@ -2,7 +2,7 @@ export function svgTouch() { function drawTouch(selection) { selection.selectAll('.layer-touch') - .data(['areas', 'lines', 'points', 'turns', 'notes']) + .data(['areas', 'lines', 'points', 'turns', 'markers']) .enter() .append('g') .attr('class', function(d) { return 'layer-touch ' + d; }); From 0582faff1d23e38647f5ed0d9ae41c7d5304b75c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 20:27:22 -0500 Subject: [PATCH 077/114] Convert KeepRight error 73 to regex (re: https://github.com/openstreetmap/iD/pull/5201#discussion_r245349395 ) --- data/core.yaml | 2 +- data/keepRight.json | 3 ++- dist/locales/en.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index ebec1d8f9..619f13b30 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -718,7 +718,7 @@ en: 72: description: 'This node is not member of any way and doesn''t have any tags.' 73: - description: 'This way has a "tracktype" tag but no "highway" tag.' + description: 'This way has a "{var1}" tag but no "highway" tag.' 74: description: 'This {var1} has an empty tag: "{var2}".' 75: diff --git a/data/keepRight.json b/data/keepRight.json index e9a168260..a5ef35c71 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -68,7 +68,8 @@ "73": { "title": "", "severity": "error", - "description": "This way has a tracktype tag but no highway tag" + "description": "This way has a $1 tag but no highway tag", + "regex": "has a (.+) tag" }, "74": { "title": "missing tags", diff --git a/dist/locales/en.json b/dist/locales/en.json index e4b68378b..7ffd79d99 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -870,7 +870,7 @@ "description": "This node is not member of any way and doesn't have any tags." }, "73": { - "description": "This way has a \"tracktype\" tag but no \"highway\" tag." + "description": "This way has a \"{var1}\" tag but no \"highway\" tag." }, "74": { "description": "This {var1} has an empty tag: \"{var2}\"." From fadd8e2e8a75929da6d9863e4de5e5541d18b412 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 21:17:38 -0500 Subject: [PATCH 078/114] Switch KeepRight markers to use touch targets This makes it easier to select the KeepRight issue and avoid selecting the OSM geometry underneath them --- css/20_map.css | 1 + modules/behavior/hover.js | 2 +- modules/modes/select_error.js | 1 + modules/services/keepRight.js | 6 +- modules/svg/keepRight.js | 185 ++++++++++++++++++++++------------ modules/svg/notes.js | 5 +- 6 files changed, 128 insertions(+), 72 deletions(-) diff --git a/css/20_map.css b/css/20_map.css index fc575d830..6c9ed5c4b 100644 --- a/css/20_map.css +++ b/css/20_map.css @@ -44,6 +44,7 @@ /* `.target` objects are interactive */ /* They can be picked up, clicked, hovered, or things can connect to them */ +.kr_error.target, .note.target, .node.target, .turn .target { diff --git a/modules/behavior/hover.js b/modules/behavior/hover.js index ad5d1c43d..58b9187a9 100644 --- a/modules/behavior/hover.js +++ b/modules/behavior/hover.js @@ -114,7 +114,7 @@ export function behaviorHover(context) { } else if (datum instanceof krError) { entity = datum; - selector = '.error-' + datum.id; + selector = '.kr_error-' + datum.id; } else if (datum instanceof osmNote) { entity = datum; diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js index b66be72d6..abd6ea8bf 100644 --- a/modules/modes/select_error.js +++ b/modules/modes/select_error.js @@ -73,6 +73,7 @@ export function modeSelectError(context, selectedErrorID) { } else { selection .classed('selected', true); + context.selectedErrorID(selectedErrorID); } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index a1be47f5d..4ebe9ad01 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -54,7 +54,7 @@ var _krLocalize = { to: 'to' }; -var defaultRuleset = [ +var _krRuleset = [ // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads 30, 40, 50, 60, 70, 90, 100, 110, 120, 130, 150, 160, 170, 180, 190, 191, 192, 193, 194, 195, 196, 197, 198, @@ -290,9 +290,9 @@ export default { // KeepRight API: http://osm.mueschelsoft.de/keepright/interfacing.php - loadErrors: function(context, projection) { + loadErrors: function(projection) { var options = { format: 'geojson' }; - var rules = defaultRuleset.join(); + var rules = _krRuleset.join(); // determine the needed tiles to cover the view var tiles = tiler diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index 8e294bb18..a4ddd9e55 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -5,12 +5,17 @@ import { modeBrowse } from '../modes'; import { svgPointTransform } from './index'; import { services } from '../services'; +var _keepRightEnabled = false; +var _keepRightService; + export function svgKeepRight(projection, context, dispatch) { var throttledRedraw = _throttle(function () { dispatch.call('change'); }, 1000); var minZoom = 12; - var layer = d3_select(null); - var _keepRight; + var touchLayer = d3_select(null); + var drawLayer = d3_select(null); + var _keepRightVisible = false; + function markerPath(selection, klass) { selection @@ -20,31 +25,48 @@ export function svgKeepRight(projection, context, dispatch) { } - function init() { - if (svgKeepRight.initialized) return; // run once - svgKeepRight.enabled = false; - svgKeepRight.initialized = true; - } - - + // Loosely-coupled keepRight service for fetching errors. function getService() { - if (services.keepRight && !_keepRight) { - _keepRight = services.keepRight; - _keepRight.event.on('loaded', throttledRedraw); - } else if (!services.keepRight && _keepRight) { - _keepRight = null; + if (services.keepRight && !_keepRightService) { + _keepRightService = services.keepRight; + _keepRightService.on('loaded', throttledRedraw); + } else if (!services.keepRight && _keepRightService) { + _keepRightService = null; } - return _keepRight; + + return _keepRightService; } - function showLayer() { - var service = getService(); - if (!service) return; + // Show the errors + function editOn() { + if (!_keepRightVisible) { + _keepRightVisible = true; + drawLayer + .style('display', 'block'); + } + } + + + // Immediately remove the errors and their touch targets + function editOff() { + if (_keepRightVisible) { + _keepRightVisible = false; + drawLayer + .style('display', 'none'); + drawLayer.selectAll('.kr_error') + .remove(); + touchLayer.selectAll('.kr_error') + .remove(); + } + } + + + // Enable the layer. This shows the errors and transitions them to visible. + function layerOn() { editOn(); - layer - .classed('disabled', false) + drawLayer .style('opacity', 0) .transition() .duration(250) @@ -55,52 +77,49 @@ export function svgKeepRight(projection, context, dispatch) { } - function hideLayer() { + // Disable the layer. This transitions the layer invisible and then hides the errors. + function layerOff() { throttledRedraw.cancel(); - editOff(); + drawLayer.interrupt(); + touchLayer.selectAll('.kr_error') + .remove(); - layer + drawLayer .transition() .duration(250) .style('opacity', 0) .on('end interrupt', function () { - layer.classed('disabled', true); + editOff(); dispatch.call('change'); }); } - function editOn() { - layer.style('display', 'block'); - } + // Update the error markers + function updateMarkers() { + if (!_keepRightVisible || !_keepRightEnabled) return; - - function editOff() { - layer.selectAll('.kr_error').remove(); - layer.style('display', 'none'); - } - - - function update() { var service = getService(); var selectedID = context.selectedErrorID(); var data = (service ? service.getErrors(projection) : []); - var transform = svgPointTransform(projection); - var kr_errors = layer.selectAll('.kr_error') + var getTransform = svgPointTransform(projection); + + // Draw markers.. + var markers = drawLayer.selectAll('.kr_error') .data(data, function(d) { return d.id; }); // exit - kr_errors.exit() + markers.exit() .remove(); // enter - var kr_errorsEnter = kr_errors.enter() + var markersEnter = markers.enter() .append('g') .attr('class', function(d) { return 'kr_error kr_error-' + d.id + ' kr_error_type_' + d.parent_error_type; } ); - kr_errorsEnter + markersEnter .append('ellipse') .attr('cx', 0.5) .attr('cy', 1) @@ -108,11 +127,11 @@ export function svgKeepRight(projection, context, dispatch) { .attr('ry', 3) .attr('class', 'stroke'); - kr_errorsEnter + markersEnter .append('path') .call(markerPath, 'shadow'); - kr_errorsEnter + markersEnter .append('use') .attr('class', 'kr_error-fill') .attr('width', '20px') @@ -122,39 +141,71 @@ export function svgKeepRight(projection, context, dispatch) { .attr('xlink:href', '#iD-icon-bolt'); // update - kr_errors - .merge(kr_errorsEnter) - .sort(function(a, b) { - return (a.id === selectedID) ? 1 - : (b.id === selectedID) ? -1 - : b.loc[1] - a.loc[1]; // sort Y - }) + markers + .merge(markersEnter) + .sort(sortY) .classed('selected', function(d) { return d.id === selectedID; }) - .attr('transform', transform); + .attr('transform', getTransform); + + + // Draw targets.. + if (touchLayer.empty()) return; + var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor '; + + var targets = touchLayer.selectAll('.kr_error') + .data(data, function(d) { return d.id; }); + + // exit + targets.exit() + .remove(); + + // enter/update + targets.enter() + .append('rect') + .attr('width', '20px') + .attr('height', '20px') + .attr('x', '-8px') + .attr('y', '-22px') + .merge(targets) + .sort(sortY) + .attr('class', function(d) { + return 'kr_error target kr_error-' + d.id + ' ' + fillClass; + }) + .attr('transform', getTransform); + + + function sortY(a, b) { + return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1]; + } } + // Draw the keepRight layer and schedule loading errors and updating markers. function drawKeepRight(selection) { - var enabled = svgKeepRight.enabled; var service = getService(); - layer = selection.selectAll('.layer-keepRight') + var surface = context.surface(); + if (surface && !surface.empty()) { + touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers'); + } + + drawLayer = selection.selectAll('.layer-keepRight') .data(service ? [0] : []); - layer.exit() + drawLayer.exit() .remove(); - layer = layer.enter() + drawLayer = drawLayer.enter() .append('g') .attr('class', 'layer-keepRight') - .style('display', enabled ? 'block' : 'none') - .merge(layer); + .style('display', _keepRightEnabled ? 'block' : 'none') + .merge(drawLayer); - if (enabled) { + if (_keepRightEnabled) { if (service && ~~context.map().zoom() >= minZoom) { editOn(); - update(); - service.loadErrors(context, projection); + service.loadErrors(projection); + updateMarkers(); } else { editOff(); } @@ -162,17 +213,20 @@ export function svgKeepRight(projection, context, dispatch) { } - drawKeepRight.enabled = function(_) { - if (!arguments.length) return svgKeepRight.enabled; - svgKeepRight.enabled = _; - if (svgKeepRight.enabled) { - showLayer(); + // Toggles the layer on and off + drawKeepRight.enabled = function(val) { + if (!arguments.length) return _keepRightEnabled; + + _keepRightEnabled = val; + if (_keepRightEnabled) { + layerOn(); } else { - hideLayer(); + layerOff(); if (context.selectedErrorID()) { context.enter(modeBrowse(context)); } } + dispatch.call('change'); return this; }; @@ -183,6 +237,5 @@ export function svgKeepRight(projection, context, dispatch) { }; - init(); return drawKeepRight; } diff --git a/modules/svg/notes.js b/modules/svg/notes.js index 7df4d876a..d49fb3223 100644 --- a/modules/svg/notes.js +++ b/modules/svg/notes.js @@ -216,10 +216,11 @@ export function svgNotes(projection, context, dispatch) { drawLayer.exit() .remove(); - drawLayer.enter() + drawLayer = drawLayer.enter() .append('g') .attr('class', 'layer-notes') - .style('display', _notesEnabled ? 'block' : 'none'); + .style('display', _notesEnabled ? 'block' : 'none') + .merge(drawLayer); if (_notesEnabled) { if (service && ~~context.map().zoom() >= minZoom) { From 48cc06fdbb1fb3eab2a4d33a2f4e66d27884b7ed Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 21:28:35 -0500 Subject: [PATCH 079/114] Adjust distance from KeepRight marker and underlying geometry Makes it easier to select the point/vertex that the error is about. --- modules/services/keepRight.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 4ebe9ad01..3eb853e02 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -338,7 +338,7 @@ export default { var coincident = false; do { // first time, move marker up. after that, move marker right. - var delta = coincident ? [0.00001, 0] : [0, 0.000005]; + var delta = coincident ? [0.00002, 0] : [0, 0.00002]; loc = geoVecAdd(loc, delta); var bbox = geoExtent(loc).bbox(); coincident = _krCache.rtree.search(bbox).length; From da4cb156a0d0fcf6831ee96e6870b0732dd2768c Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 5 Jan 2019 01:15:01 -0500 Subject: [PATCH 080/114] Fix missing parenthesis in error string 380 --- data/core.yaml | 2 +- dist/locales/en.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 4e224cbfb..c67c8b09c 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -854,7 +854,7 @@ en: including_the_name: "(including the name {name})" 380: title: 'Non-physical use of sport tag' - description: 'This way is tagged "{var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway".' + description: 'This way is tagged "{var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway").' 390: title: 'Missing tracktype' description: This track doesn't have a "tracktype" tag. diff --git a/dist/locales/en.json b/dist/locales/en.json index 3fc87f37b..e0c901b26 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1053,7 +1053,7 @@ }, "380": { "title": "Non-physical use of sport tag", - "description": "This way is tagged \"{var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\"." + "description": "This way is tagged \"{var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\")." }, "390": { "title": "Missing tracktype", From 3c82174f03e1cde26d06034143cb261978034afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguy=E1=BB=85n?= Date: Sat, 5 Jan 2019 00:32:45 -0800 Subject: [PATCH 081/114] Capitalize first letter of KeepRight error detail --- modules/ui/keepRight_details.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 01702365d..d73de470f 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -18,13 +18,15 @@ export function uiKeepRightDetails(context) { var et = dataEn.QA.keepRight.errorTypes[errorType]; var pt = dataEn.QA.keepRight.errorTypes[parentErrorType]; + var detail; if (et && et.description) { - return t('QA.keepRight.errorTypes.' + errorType + '.description', d.replacements); + detail = t('QA.keepRight.errorTypes.' + errorType + '.description', d.replacements); } else if (pt && pt.description) { - return t('QA.keepRight.errorTypes.' + parentErrorType + '.description', d.replacements); + detail = t('QA.keepRight.errorTypes.' + parentErrorType + '.description', d.replacements); } else { - return unknown; + detail = unknown; } + return detail.substr(0, 1).toUpperCase() + detail.substr(1); } From 0776d28aa05ea9d671161e241b958ebb4e981ef0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 5 Jan 2019 23:29:54 -0500 Subject: [PATCH 082/114] Update to rollup v1.0.2 (closes #5660) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 233276883..7fca8b898 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "osm-community-index": "0.5.0", "phantomjs-prebuilt": "~2.1.11", "request": "^2.88.0", - "rollup": "~0.68.0", + "rollup": "~1.0.2", "rollup-plugin-commonjs": "^9.0.0", "rollup-plugin-includepaths": "~0.2.3", "rollup-plugin-json": "^3.0.0", From 5567b2b077b6f447820f587c2a913cdf306abd70 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Tue, 1 Jan 2019 00:51:40 +0000 Subject: [PATCH 083/114] fix(package): update marked to version 0.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7fca8b898..fb72db9a1 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "diacritics": "1.3.0", "fast-json-stable-stringify": "2.0.0", "lodash-es": "4.17.11", - "marked": "0.5.2", + "marked": "0.6.0", "martinez-polygon-clipping": "0.5.0", "node-diff3": "1.0.0", "osm-auth": "1.0.2", From 55d2dddb7080ec129f5a4adb217ae943e3722933 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sun, 6 Jan 2019 00:33:35 -0500 Subject: [PATCH 084/114] =?UTF-8?q?Keybind=20all=20of=20=5Funiq([t(sidebar?= =?UTF-8?q?.key),=20'`',=20'=C2=B2'])=20to=20toggle=20sidebar=20(closes=20?= =?UTF-8?q?#5663)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/ui/init.js | 5 ++++- modules/ui/shortcuts.js | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/ui/init.js b/modules/ui/init.js index 29d4defaa..0ec03e0d7 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -1,3 +1,5 @@ +import _uniq from 'lodash-es/uniq'; + import { event as d3_event, select as d3_select @@ -298,9 +300,10 @@ export function uiInit(context) { var panPixels = 80; + var sidebarKeys = _uniq([t('sidebar.key'), '`', '²']); // #5663 context.keybinding() .on('⌫', function() { d3_event.preventDefault(); }) - .on(t('sidebar.key'), ui.sidebar.toggle) + .on(sidebarKeys, ui.sidebar.toggle) .on('←', pan([panPixels, 0])) .on('↑', pan([0, panPixels])) .on('→', pan([-panPixels, 0])) diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index 43ee2b0b5..99c31e9bd 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -1,3 +1,5 @@ +import _uniq from 'lodash-es/uniq'; + import { select as d3_select, selectAll as d3_selectAll @@ -177,6 +179,8 @@ export function uiShortcuts(context) { arr = ['Y']; } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { arr = ['F11']; + } else if (d.text === 'shortcuts.browsing.display_options.sidebar') { + arr = _uniq([t('sidebar.key'), '`', '²']); // #5663 } return arr.map(function(s) { From 826f66a91bda6ef88a9d53a9fc6e9b53d397f2dc Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 7 Jan 2019 11:12:24 -0500 Subject: [PATCH 085/114] Unique values for key codes moved to utilKeybinding, uiShortcuts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us supply lists of keys which might contain duplicates, but will be made unique. For example: [t('sidebar.key'), '`', '²'] 'sidebar.key' may be one of the other choices, but will be deduplicated. --- data/shortcuts.json | 26 +++++++++++++------------- modules/ui/init.js | 5 +---- modules/ui/shortcuts.js | 16 ++++++++-------- modules/util/keybinding.js | 5 +++-- 4 files changed, 25 insertions(+), 27 deletions(-) diff --git a/data/shortcuts.json b/data/shortcuts.json index 39a81889f..5097a8965 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -62,12 +62,12 @@ "text": "shortcuts.browsing.display_options.map_data" }, { - "modifiers": ["⌃","⌘"], + "modifiers": ["⌃", "⌘"], "shortcuts": ["F", "F11"], "text": "shortcuts.browsing.display_options.fullscreen" }, { - "shortcuts": ["sidebar.key"], + "shortcuts": ["sidebar.key", "`", "²"], "text": "shortcuts.browsing.display_options.sidebar" }, { @@ -127,19 +127,19 @@ "text": "shortcuts.browsing.vertex_selected.previous" }, { - "shortcuts": ["]","↘"], + "shortcuts": ["]", "↘"], "text": "shortcuts.browsing.vertex_selected.next" }, { - "shortcuts": ["{","⇞"], + "shortcuts": ["{", "⇞"], "text": "shortcuts.browsing.vertex_selected.first" }, { - "shortcuts": ["}","⇟"], + "shortcuts": ["}", "⇟"], "text": "shortcuts.browsing.vertex_selected.last" }, { - "shortcuts": ["\\","shortcuts.key.pause"], + "shortcuts": ["\\", "shortcuts.key.pause"], "text": "shortcuts.browsing.vertex_selected.change_parent" } ] @@ -173,7 +173,7 @@ "text": "shortcuts.editing.drawing.add_note" }, { - "shortcuts": ["Left-click","shortcuts.key.space"], + "shortcuts": ["Left-click", "shortcuts.key.space"], "text": "shortcuts.editing.drawing.place_point" }, { @@ -181,7 +181,7 @@ "text": "shortcuts.editing.drawing.disable_snap" }, { - "shortcuts": ["↵","⎋"], + "shortcuts": ["↵", "⎋"], "text": "shortcuts.editing.drawing.stop_line" }, { @@ -204,7 +204,7 @@ "text": "shortcuts.editing.commands.undo" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["Z"], "text": "shortcuts.editing.commands.redo" }, @@ -294,22 +294,22 @@ "text": "shortcuts.tools.info.all" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["info_panels.background.key"], "text": "shortcuts.tools.info.background" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["info_panels.history.key"], "text": "shortcuts.tools.info.history" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["info_panels.location.key"], "text": "shortcuts.tools.info.location" }, { - "modifiers": ["⌘","⇧"], + "modifiers": ["⌘", "⇧"], "shortcuts": ["info_panels.measurement.key"], "text": "shortcuts.tools.info.measurement" } diff --git a/modules/ui/init.js b/modules/ui/init.js index 0ec03e0d7..ac45e1b44 100644 --- a/modules/ui/init.js +++ b/modules/ui/init.js @@ -1,5 +1,3 @@ -import _uniq from 'lodash-es/uniq'; - import { event as d3_event, select as d3_select @@ -300,10 +298,9 @@ export function uiInit(context) { var panPixels = 80; - var sidebarKeys = _uniq([t('sidebar.key'), '`', '²']); // #5663 context.keybinding() .on('⌫', function() { d3_event.preventDefault(); }) - .on(sidebarKeys, ui.sidebar.toggle) + .on([t('sidebar.key'), '`', '²'], ui.sidebar.toggle) // #5663 - common QWERTY, AZERTY .on('←', pan([panPixels, 0])) .on('↑', pan([0, panPixels])) .on('→', pan([-panPixels, 0])) diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index 99c31e9bd..e9d95cb87 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -179,11 +179,14 @@ export function uiShortcuts(context) { arr = ['Y']; } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') { arr = ['F11']; - } else if (d.text === 'shortcuts.browsing.display_options.sidebar') { - arr = _uniq([t('sidebar.key'), '`', '²']); // #5663 } - return arr.map(function(s) { + // replace translations + arr = arr.map(function(s) { + return uiCmd.display(s.indexOf('.') !== -1 ? t(s) : s); + }); + + return _uniq(arr).map(function(s) { return { shortcut: s, separator: d.separator @@ -195,17 +198,14 @@ export function uiShortcuts(context) { var selection = d3_select(this); var click = d.shortcut.toLowerCase().match(/(.*).click/); - if (click && click[1]) { + if (click && click[1]) { // replace "left_click", "right_click" with mouse icon selection .call(svgIcon('#iD-walkthrough-mouse', 'mouseclick', click[1])); } else { selection .append('kbd') .attr('class', 'shortcut') - .text(function (d) { - var key = d.shortcut; - return key.indexOf('.') !== -1 ? uiCmd.display(t(key)) : uiCmd.display(key); - }); + .text(function (d) { return d.shortcut; }); } if (i < nodes.length - 1) { diff --git a/modules/util/keybinding.js b/modules/util/keybinding.js index ec643b09c..6ca9ab1db 100644 --- a/modules/util/keybinding.js +++ b/modules/util/keybinding.js @@ -1,4 +1,5 @@ import _isFunction from 'lodash-es/isFunction'; +import _uniq from 'lodash-es/uniq'; import { event as d3_event, @@ -125,7 +126,7 @@ export function utilKeybinding(namespace) { // Remove one or more keycode bindings. keybinding.off = function(codes, capture) { - var arr = [].concat(codes); + var arr = _uniq([].concat(codes)); for (var i = 0; i < arr.length; i++) { var id = arr[i] + (capture ? '-capture' : '-bubble'); @@ -141,7 +142,7 @@ export function utilKeybinding(namespace) { return keybinding.off(codes, capture); } - var arr = [].concat(codes); + var arr = _uniq([].concat(codes)); for (var i = 0; i < arr.length; i++) { var id = arr[i] + (capture ? '-capture' : '-bubble'); From 4ed2858cc53415aa388fed716b1835a130639fa6 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 7 Jan 2019 11:39:12 -0500 Subject: [PATCH 086/114] Explicitly include '?' as a fallback key for keyboard shortcuts (closes #5675) This allows for a localized version of the key, but iD will still respond to the '?' --- data/shortcuts.json | 2 +- modules/ui/shortcuts.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/shortcuts.json b/data/shortcuts.json index 5097a8965..7f6bbe700 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -41,7 +41,7 @@ "text": "shortcuts.browsing.help.help" }, { - "shortcuts": ["shortcuts.toggle.key"], + "shortcuts": ["shortcuts.toggle.key", "?"], "text": "shortcuts.browsing.help.keyboard" }, { diff --git a/modules/ui/shortcuts.js b/modules/ui/shortcuts.js index e9d95cb87..252375c4b 100644 --- a/modules/ui/shortcuts.js +++ b/modules/ui/shortcuts.js @@ -21,7 +21,7 @@ export function uiShortcuts(context) { context.keybinding() - .on(t('shortcuts.toggle.key'), function () { + .on([t('shortcuts.toggle.key'), '?'], function () { if (d3_selectAll('.modal-shortcuts').size()) { // already showing if (_modalSelection) { _modalSelection.close(); From f1b5c4e5b007a7e94aa1d4c05019490101df3b41 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 7 Jan 2019 12:07:15 -0500 Subject: [PATCH 087/114] Wherever we show 'denomination' field, also show 'religion' field (closes #5611) --- data/presets/presets.json | 16 ++++++++-------- .../amenity/place_of_worship/buddhist.json | 1 + .../amenity/place_of_worship/christian.json | 1 + .../presets/amenity/place_of_worship/hindu.json | 1 + .../presets/amenity/place_of_worship/jewish.json | 1 + .../presets/amenity/place_of_worship/muslim.json | 1 + .../presets/amenity/place_of_worship/shinto.json | 1 + .../presets/amenity/place_of_worship/sikh.json | 1 + .../presets/amenity/place_of_worship/taoist.json | 1 + 9 files changed, 16 insertions(+), 8 deletions(-) diff --git a/data/presets/presets.json b/data/presets/presets.json index c1a223348..59d5cdfde 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -119,14 +119,14 @@ "amenity/payment_terminal": {"icon": "maki-bank", "fields": ["name", "brand", "address", "opening_hours", "payment_multi"], "geometry": ["point"], "terms": ["interactive kiosk", "ekiosk", "atm", "bill pay", "tax pay", "phone pay", "finance", "cash", "money transfer", "card"], "tags": {"amenity": "payment_terminal"}, "name": "Payment Terminal"}, "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug store", "drugstore", "med*", "prescription"], "name": "Pharmacy Counter"}, "amenity/place_of_worship": {"icon": "maki-place-of-worship", "fields": ["name", "religion", "denomination", "address", "building_area", "service_times"], "geometry": ["point", "area"], "terms": ["abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "church", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "mosque", "oratory", "parish", "sacellum", "sanctuary", "shrine", "synagogue", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship"}, "name": "Place of Worship"}, - "amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"}, - "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"}, - "amenity/place_of_worship/hindu": {"icon": "temaki-hinduism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kovil", "devasthana", "mandir", "kshetram", "alayam", "shrine", "temple"], "tags": {"amenity": "place_of_worship", "religion": "hindu"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Hindu Temple"}, - "amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Synagogue"}, - "amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Mosque"}, - "amenity/place_of_worship/shinto": {"icon": "temaki-shinto", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kami", "torii"], "tags": {"amenity": "place_of_worship", "religion": "shinto"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Shinto Shrine"}, - "amenity/place_of_worship/sikh": {"icon": "temaki-sikhism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["gurudwara", "temple"], "tags": {"amenity": "place_of_worship", "religion": "sikh"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Sikh Temple"}, - "amenity/place_of_worship/taoist": {"icon": "temaki-taoism", "fields": ["name", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["daoist", "monastery", "temple"], "tags": {"amenity": "place_of_worship", "religion": "taoist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Taoist Temple"}, + "amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"}, + "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"}, + "amenity/place_of_worship/hindu": {"icon": "temaki-hinduism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kovil", "devasthana", "mandir", "kshetram", "alayam", "shrine", "temple"], "tags": {"amenity": "place_of_worship", "religion": "hindu"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Hindu Temple"}, + "amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Synagogue"}, + "amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Mosque"}, + "amenity/place_of_worship/shinto": {"icon": "temaki-shinto", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kami", "torii"], "tags": {"amenity": "place_of_worship", "religion": "shinto"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Shinto Shrine"}, + "amenity/place_of_worship/sikh": {"icon": "temaki-sikhism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["gurudwara", "temple"], "tags": {"amenity": "place_of_worship", "religion": "sikh"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Sikh Temple"}, + "amenity/place_of_worship/taoist": {"icon": "temaki-taoism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["daoist", "monastery", "temple"], "tags": {"amenity": "place_of_worship", "religion": "taoist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Taoist Temple"}, "amenity/planetarium": {"icon": "maki-museum", "fields": ["name", "operator", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "terms": ["museum", "astronomy", "observatory"], "tags": {"amenity": "planetarium"}, "name": "Planetarium"}, "amenity/police": {"icon": "maki-police", "fields": ["name", "operator", "address", "building_area", "opening_hours"], "geometry": ["point", "area"], "terms": ["badge", "constable", "constabulary", "cop", "detective", "fed", "law", "enforcement", "officer", "patrol"], "tags": {"amenity": "police"}, "name": "Police"}, "amenity/post_box": {"icon": "maki-post", "fields": ["operator", "collection_times", "drive_through", "ref"], "geometry": ["point", "vertex"], "tags": {"amenity": "post_box"}, "terms": ["letter", "post"], "name": "Mailbox"}, diff --git a/data/presets/presets/amenity/place_of_worship/buddhist.json b/data/presets/presets/amenity/place_of_worship/buddhist.json index f2745d213..ace4cf645 100644 --- a/data/presets/presets/amenity/place_of_worship/buddhist.json +++ b/data/presets/presets/amenity/place_of_worship/buddhist.json @@ -2,6 +2,7 @@ "icon": "maki-religious-buddhist", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/christian.json b/data/presets/presets/amenity/place_of_worship/christian.json index 4824d4b1e..618bc0e77 100644 --- a/data/presets/presets/amenity/place_of_worship/christian.json +++ b/data/presets/presets/amenity/place_of_worship/christian.json @@ -2,6 +2,7 @@ "icon": "maki-religious-christian", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/hindu.json b/data/presets/presets/amenity/place_of_worship/hindu.json index d6dba5466..2a5b53567 100644 --- a/data/presets/presets/amenity/place_of_worship/hindu.json +++ b/data/presets/presets/amenity/place_of_worship/hindu.json @@ -2,6 +2,7 @@ "icon": "temaki-hinduism", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/jewish.json b/data/presets/presets/amenity/place_of_worship/jewish.json index 1c626cb0f..bff8c16f1 100644 --- a/data/presets/presets/amenity/place_of_worship/jewish.json +++ b/data/presets/presets/amenity/place_of_worship/jewish.json @@ -2,6 +2,7 @@ "icon": "maki-religious-jewish", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/muslim.json b/data/presets/presets/amenity/place_of_worship/muslim.json index c802138b7..ec827de12 100644 --- a/data/presets/presets/amenity/place_of_worship/muslim.json +++ b/data/presets/presets/amenity/place_of_worship/muslim.json @@ -2,6 +2,7 @@ "icon": "maki-religious-muslim", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/shinto.json b/data/presets/presets/amenity/place_of_worship/shinto.json index 7a940b181..1bfe24aeb 100644 --- a/data/presets/presets/amenity/place_of_worship/shinto.json +++ b/data/presets/presets/amenity/place_of_worship/shinto.json @@ -2,6 +2,7 @@ "icon": "temaki-shinto", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/sikh.json b/data/presets/presets/amenity/place_of_worship/sikh.json index fb2aba2fc..83196bc56 100644 --- a/data/presets/presets/amenity/place_of_worship/sikh.json +++ b/data/presets/presets/amenity/place_of_worship/sikh.json @@ -2,6 +2,7 @@ "icon": "temaki-sikhism", "fields": [ "name", + "religion", "denomination", "building_area", "address", diff --git a/data/presets/presets/amenity/place_of_worship/taoist.json b/data/presets/presets/amenity/place_of_worship/taoist.json index a4fbcde3d..5c01f1f3b 100644 --- a/data/presets/presets/amenity/place_of_worship/taoist.json +++ b/data/presets/presets/amenity/place_of_worship/taoist.json @@ -2,6 +2,7 @@ "icon": "temaki-taoism", "fields": [ "name", + "religion", "denomination", "building_area", "address", From 58665d0d2e5da52f7041bf6b78e5d91b3695ebb2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 7 Jan 2019 12:10:13 -0500 Subject: [PATCH 088/114] Embed name of religion in preset label (re: #5611) --- data/presets.yaml | 12 ++++++------ data/presets/presets.json | 6 +++--- .../presets/amenity/place_of_worship/christian.json | 2 +- .../presets/amenity/place_of_worship/jewish.json | 2 +- .../presets/amenity/place_of_worship/muslim.json | 2 +- data/taginfo.json | 6 +++--- dist/locales/en.json | 6 +++--- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/data/presets.yaml b/data/presets.yaml index 5e65082f4..8665ac920 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -2568,9 +2568,9 @@ en: terms: '' amenity/place_of_worship/christian: # 'amenity=place_of_worship, religion=christian' - name: Church + name: Christian Church # 'terms: christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple' - terms: '' + terms: '' amenity/place_of_worship/hindu: # 'amenity=place_of_worship, religion=hindu' name: Hindu Temple @@ -2578,14 +2578,14 @@ en: terms: '' amenity/place_of_worship/jewish: # 'amenity=place_of_worship, religion=jewish' - name: Synagogue + name: Jewish Synagogue # 'terms: jewish' - terms: '' + terms: '' amenity/place_of_worship/muslim: # 'amenity=place_of_worship, religion=muslim' - name: Mosque + name: Muslim Mosque # 'terms: muslim' - terms: '' + terms: '' amenity/place_of_worship/shinto: # 'amenity=place_of_worship, religion=shinto' name: Shinto Shrine diff --git a/data/presets/presets.json b/data/presets/presets.json index 59d5cdfde..90024dbd0 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -120,10 +120,10 @@ "amenity/pharmacy": {"icon": "maki-pharmacy", "fields": ["name", "operator", "address", "building_area", "drive_through", "opening_hours", "payment_multi", "dispensing"], "geometry": ["point", "area"], "tags": {"amenity": "pharmacy"}, "addTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "removeTags": {"amenity": "pharmacy", "healthcare": "pharmacy"}, "reference": {"key": "amenity", "value": "pharmacy"}, "terms": ["apothecary", "drug store", "drugstore", "med*", "prescription"], "name": "Pharmacy Counter"}, "amenity/place_of_worship": {"icon": "maki-place-of-worship", "fields": ["name", "religion", "denomination", "address", "building_area", "service_times"], "geometry": ["point", "area"], "terms": ["abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "church", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "mosque", "oratory", "parish", "sacellum", "sanctuary", "shrine", "synagogue", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship"}, "name": "Place of Worship"}, "amenity/place_of_worship/buddhist": {"icon": "maki-religious-buddhist", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["stupa", "vihara", "monastery", "temple", "pagoda", "zendo", "dojo"], "tags": {"amenity": "place_of_worship", "religion": "buddhist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Buddhist Temple"}, - "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Church"}, + "amenity/place_of_worship/christian": {"icon": "maki-religious-christian", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["christian", "abbey", "basilica", "bethel", "cathedral", "chancel", "chantry", "chapel", "fold", "house of God", "house of prayer", "house of worship", "minster", "mission", "oratory", "parish", "sacellum", "sanctuary", "shrine", "tabernacle", "temple"], "tags": {"amenity": "place_of_worship", "religion": "christian"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Christian Church"}, "amenity/place_of_worship/hindu": {"icon": "temaki-hinduism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kovil", "devasthana", "mandir", "kshetram", "alayam", "shrine", "temple"], "tags": {"amenity": "place_of_worship", "religion": "hindu"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Hindu Temple"}, - "amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Synagogue"}, - "amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Mosque"}, + "amenity/place_of_worship/jewish": {"icon": "maki-religious-jewish", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["jewish"], "tags": {"amenity": "place_of_worship", "religion": "jewish"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Jewish Synagogue"}, + "amenity/place_of_worship/muslim": {"icon": "maki-religious-muslim", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["muslim"], "tags": {"amenity": "place_of_worship", "religion": "muslim"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Muslim Mosque"}, "amenity/place_of_worship/shinto": {"icon": "temaki-shinto", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["kami", "torii"], "tags": {"amenity": "place_of_worship", "religion": "shinto"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Shinto Shrine"}, "amenity/place_of_worship/sikh": {"icon": "temaki-sikhism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["gurudwara", "temple"], "tags": {"amenity": "place_of_worship", "religion": "sikh"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Sikh Temple"}, "amenity/place_of_worship/taoist": {"icon": "temaki-taoism", "fields": ["name", "religion", "denomination", "building_area", "address", "service_times"], "geometry": ["point", "area"], "terms": ["daoist", "monastery", "temple"], "tags": {"amenity": "place_of_worship", "religion": "taoist"}, "reference": {"key": "amenity", "value": "place_of_worship"}, "name": "Taoist Temple"}, diff --git a/data/presets/presets/amenity/place_of_worship/christian.json b/data/presets/presets/amenity/place_of_worship/christian.json index 618bc0e77..25f0fdb18 100644 --- a/data/presets/presets/amenity/place_of_worship/christian.json +++ b/data/presets/presets/amenity/place_of_worship/christian.json @@ -43,5 +43,5 @@ "key": "amenity", "value": "place_of_worship" }, - "name": "Church" + "name": "Christian Church" } diff --git a/data/presets/presets/amenity/place_of_worship/jewish.json b/data/presets/presets/amenity/place_of_worship/jewish.json index bff8c16f1..c0b406055 100644 --- a/data/presets/presets/amenity/place_of_worship/jewish.json +++ b/data/presets/presets/amenity/place_of_worship/jewish.json @@ -23,5 +23,5 @@ "key": "amenity", "value": "place_of_worship" }, - "name": "Synagogue" + "name": "Jewish Synagogue" } diff --git a/data/presets/presets/amenity/place_of_worship/muslim.json b/data/presets/presets/amenity/place_of_worship/muslim.json index ec827de12..bc7d80459 100644 --- a/data/presets/presets/amenity/place_of_worship/muslim.json +++ b/data/presets/presets/amenity/place_of_worship/muslim.json @@ -23,5 +23,5 @@ "key": "amenity", "value": "place_of_worship" }, - "name": "Mosque" + "name": "Muslim Mosque" } diff --git a/data/taginfo.json b/data/taginfo.json index 9eeff62dd..3ebc49bfd 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -815,7 +815,7 @@ { "key": "religion", "value": "christian", - "description": "🄿 Church", + "description": "🄿 Christian Church", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-christian-15.svg?sanitize=true" }, @@ -829,14 +829,14 @@ { "key": "religion", "value": "jewish", - "description": "🄿 Synagogue", + "description": "🄿 Jewish Synagogue", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-jewish-15.svg?sanitize=true" }, { "key": "religion", "value": "muslim", - "description": "🄿 Mosque", + "description": "🄿 Muslim Mosque", "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/religious-muslim-15.svg?sanitize=true" }, diff --git a/dist/locales/en.json b/dist/locales/en.json index e0c901b26..288a7d483 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -4013,7 +4013,7 @@ "terms": "stupa,vihara,monastery,temple,pagoda,zendo,dojo" }, "amenity/place_of_worship/christian": { - "name": "Church", + "name": "Christian Church", "terms": "christian,abbey,basilica,bethel,cathedral,chancel,chantry,chapel,fold,house of God,house of prayer,house of worship,minster,mission,oratory,parish,sacellum,sanctuary,shrine,tabernacle,temple" }, "amenity/place_of_worship/hindu": { @@ -4021,11 +4021,11 @@ "terms": "kovil,devasthana,mandir,kshetram,alayam,shrine,temple" }, "amenity/place_of_worship/jewish": { - "name": "Synagogue", + "name": "Jewish Synagogue", "terms": "jewish" }, "amenity/place_of_worship/muslim": { - "name": "Mosque", + "name": "Muslim Mosque", "terms": "muslim" }, "amenity/place_of_worship/shinto": { From d9015baa470e4da75289535dae069bb5b73236af Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Mon, 7 Jan 2019 16:10:01 -0500 Subject: [PATCH 089/114] Improve links in KeepRight error messages (re: #5679) Also move the localizeable string dictionary to data/keepRight.json --- data/keepRight.json | 31 ++++++++ modules/services/keepRight.js | 122 ++++++++++++-------------------- modules/ui/keepRight_details.js | 59 ++++++++++++--- modules/util/util.js | 73 ++++++++++--------- 4 files changed, 167 insertions(+), 118 deletions(-) diff --git a/data/keepRight.json b/data/keepRight.json index a5ef35c71..52b47b638 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -1,4 +1,35 @@ { + "localizeStrings": { + "node": "node", + "way": "way", + "relation": "relation", + "highway": "highway", + "railway": "railway", + "waterway": "waterway", + "cycleway": "cycleway", + "footpath": "footpath", + "'cycleway/footpath": "cycleway_footpath", + "riverbank": "riverbank", + "bridge": "bridge", + "tunnel": "tunnel", + "place_of_worship": "place_of_worship", + "pub": "pub", + "restaurant": "restaurant", + "school": "school", + "university": "university", + "hospital": "hospital", + "library": "library", + "theatre": "theatre", + "courthouse": "courthouse", + "bank": "bank", + "cinema": "cinema", + "pharmacy": "pharmacy", + "cafe": "cafe", + "fast_food": "fast_food", + "fuel": "fuel", + "from": "from", + "to": "to" + }, "errorTypes": { "20": { "title": "multiple nodes on the same spot", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 3eb853e02..f25bd1494 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -13,7 +13,7 @@ import { krError } from '../osm'; import { t } from '../util/locale'; import { utilRebind, utilTiler, utilQsString } from '../util'; -import { errorTypes } from '../../data/keepRight.json'; +import { errorTypes, localizeStrings } from '../../data/keepRight.json'; var tiler = utilTiler(); @@ -22,37 +22,6 @@ var dispatch = d3_dispatch('loaded'); var _krCache; var _krZoom = 14; var _krUrlRoot = 'https://www.keepright.at/'; -var _krLocalize = { - node: 'node', - way: 'way', - relation: 'relation', - highway: 'highway', - railway: 'railway', - waterway: 'waterway', - cycleway: 'cycleway', - footpath: 'footpath', - 'cycleway/footpath': 'cycleway_footpath', - riverbank: 'riverbank', - bridge: 'bridge', - tunnel: 'tunnel', - place_of_worship: 'place_of_worship', - pub: 'pub', - restaurant: 'restaurant', - school: 'school', - university: 'university', - hospital: 'hospital', - library: 'library', - theatre: 'theatre', - courthouse: 'courthouse', - bank: 'bank', - cinema: 'cinema', - pharmacy: 'pharmacy', - cafe: 'cafe', - fast_food: 'fast_food', - fuel: 'fuel', - from: 'from', - to: 'to' -}; var _krRuleset = [ // no 20 - multiple node on same spot - these are mostly boundaries overlapping roads @@ -119,7 +88,7 @@ function tokenReplacements(d) { // some descriptions are just fixed text if (!errorTemplate.regex) return; - // regex pattern should match description with variable details captured as groups + // regex pattern should match description with variable details captured var errorRegex = new RegExp(errorTemplate.regex, 'i'); var errorMatch = errorRegex.exec(d.description); if (!errorMatch) { @@ -132,33 +101,62 @@ function tokenReplacements(d) { } for (var i = 1; i < errorMatch.length; i++) { // skip first - var group = errorMatch[i]; + var capture = errorMatch[i]; var idType; idType = 'IDs' in errorTemplate ? errorTemplate.IDs[i-1] : ''; - if (idType && group) { // link IDs if present in the group - group = parseError(group, idType); - } else if (htmlRegex.test(group)) { // escape any html in non-IDs - group = '\\' + group + '\\'; - } else if (_krLocalize[group]) { // some replacement strings can be localized - group = t('QA.keepRight.error_parts.' + _krLocalize[group]); + if (idType && capture) { // link IDs if present in the capture + capture = parseError(capture, idType); + } else if (htmlRegex.test(capture)) { // escape any html in non-IDs + capture = '\\' + capture + '\\'; + } else if (localizeStrings[capture]) { // some replacement strings can be localized + capture = t('QA.keepRight.error_parts.' + localizeStrings[capture]); } - replacements['var' + i] = group; + replacements['var' + i] = capture; } return replacements; } -function parseError(group, idType) { +function parseError(capture, idType) { + + switch (idType) { + // simple case just needs a linking span + case 'n': + case 'w': + case 'r': + capture = linkEntity(idType + capture); + break; + + // some errors have more complex ID lists/variance + case '20': + capture = parse20(capture); + break; + case '211': + capture = parse211(capture); + break; + case '231': + capture = parse231(capture); + break; + case '294': + capture = parse294(capture); + break; + case '370': + capture = parse370(capture); + break; + } + + return capture; + function linkEntity(d) { - return '' + d + ''; + return '' + d + ''; } // arbitrary node list of form: #ID, #ID, #ID... - function parseError211(capture) { + function parse211(capture) { var newList = []; var items = capture.split(', '); @@ -172,7 +170,7 @@ function parseError(group, idType) { } // arbitrary way list of form: #ID(layer),#ID(layer),#ID(layer)... - function parseError231(capture) { + function parse231(capture) { var newList = []; // unfortunately 'layer' can itself contain commas, so we split on '),' var items = capture.split('),'); @@ -190,7 +188,7 @@ function parseError(group, idType) { } // arbitrary node/relation list of form: from node #ID,to relation #ID,to node #ID... - function parseError294(capture) { + function parse294(capture) { var newList = []; var items = capture.split(','); @@ -220,7 +218,7 @@ function parseError(group, idType) { } // may or may not include the string "(including the name 'name')" - function parseError370(capture) { + function parse370(capture) { if (!capture) return ''; var match = capture.match(/\(including the name (\'.+\')\)/); @@ -231,7 +229,7 @@ function parseError(group, idType) { } // arbitrary node list of form: #ID,#ID,#ID... - function parseWarning20(capture) { + function parse20(capture) { var newList = []; var items = capture.split(','); @@ -243,32 +241,6 @@ function parseError(group, idType) { return newList.join(', '); } - - switch (idType) { - // simple case just needs a linking span - case 'n': - case 'w': - case 'r': - group = linkEntity(idType + group); - break; - // some errors have more complex ID lists/variance - case '211': - group = parseError211(group); - break; - case '231': - group = parseError231(group); - break; - case '294': - group = parseError294(group); - break; - case '370': - group = parseError370(group); - break; - case '20': - group = parseWarning20(group); - } - - return group; } @@ -338,7 +310,7 @@ export default { var coincident = false; do { // first time, move marker up. after that, move marker right. - var delta = coincident ? [0.00002, 0] : [0, 0.00002]; + var delta = coincident ? [0.00001, 0] : [0, 0.00001]; loc = geoVecAdd(loc, delta); var bbox = geoExtent(loc).bbox(); coincident = _krCache.rtree.search(bbox).length; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index d73de470f..38047a9ad 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -1,7 +1,12 @@ -import { event as d3_event } from 'd3-selection'; +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; import { dataEn } from '../../data'; +import { modeSelect } from '../modes'; import { t } from '../util/locale'; +import { utilDisplayName, utilEntityOrMemberSelector } from '../util'; export function uiKeepRightDetails(context) { @@ -46,28 +51,60 @@ export function uiKeepRightDetails(context) { // description - var description = detailsEnter + var descriptionEnter = detailsEnter .append('div') .attr('class', 'kr_error-details-description'); - description + descriptionEnter .append('h4') .text(function() { return t('QA.keepRight.detail_description'); }); - description + descriptionEnter .append('div') .attr('class', 'kr_error-details-description-text') .html(errorDetail); - description.selectAll('.kr_error_description-id') - .on('click', function() { clickLink(context, this.text); }); + // If there are entity links in the error message.. + descriptionEnter.selectAll('.kr_error_entity_link') + .each(function() { + var link = d3_select(this); + var entityID = this.innerText; + var entity = context.hasEntity(entityID); + // Add click handler + link + .on('mouseover', function() { + context.surface().selectAll(utilEntityOrMemberSelector([entityID], context.graph())) + .classed('hover', true); + }) + .on('mouseout', function() { + context.surface().selectAll('.hover') + .classed('hover', false); + }) + .on('click', function() { + d3_event.preventDefault(); + var osmlayer = context.layers().layer('osm'); + if (!osmlayer.enabled()) { + osmlayer.enabled(true); + } + context.map().centerZoom(_error.loc, 20); + context.enter(modeSelect(context, [entityID])); + }); - function clickLink(context, entityID) { - d3_event.preventDefault(); - context.layers().layer('osm').enabled(true); - context.zoomToEntity(entityID); - } + // Replace with friendly name if possible + // (The entity may not yet be loaded into the graph) + if (entity) { + var name = utilDisplayName(entity); // try to use common name + if (!name) { + var preset = context.presets().match(entity, context.graph()); + name = preset && !preset.isFallback() && preset.name(); // fallback to preset name + } + + if (name) { + this.innerText = name; + } + } + }); } diff --git a/modules/util/util.js b/modules/util/util.js index 605378ece..cdc928da2 100644 --- a/modules/util/util.js +++ b/modules/util/util.js @@ -37,6 +37,7 @@ export function utilEntityOrMemberSelector(ids, graph) { export function utilEntityOrDeepMemberSelector(ids, graph) { var seen = {}; var allIDs = []; + function addEntityAndMembersIfNotYetSeen(id) { // avoid infinite recursion for circular relations by skipping seen entities if (seen[id]) return; @@ -53,6 +54,7 @@ export function utilEntityOrDeepMemberSelector(ids, graph) { } } } + ids.forEach(function(id) { addEntityAndMembersIfNotYetSeen(id); }); @@ -85,9 +87,9 @@ export function utilGetAllNodes(ids, graph) { export function utilDisplayName(entity) { - var localizedNameKey = 'name:' + utilDetect().locale.toLowerCase().split('-')[0], - name = entity.tags[localizedNameKey] || entity.tags.name || '', - network = entity.tags.cycle_network || entity.tags.network; + var localizedNameKey = 'name:' + utilDetect().locale.toLowerCase().split('-')[0]; + var name = entity.tags[localizedNameKey] || entity.tags.name || ''; + var network = entity.tags.cycle_network || entity.tags.network; if (!name && entity.tags.ref) { name = entity.tags.ref; @@ -145,11 +147,12 @@ export function utilStringQs(str) { export function utilQsString(obj, noencode) { + // encode everything except special characters used in certain hash parameters: + // "/" in map states, ":", ",", {" and "}" in background function softEncode(s) { - // encode everything except special characters used in certain hash parameters: - // "/" in map states, ":", ",", {" and "}" in background - return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent); + return encodeURIComponent(s).replace(/(%2F|%3A|%2C|%7B|%7D)/g, decodeURIComponent); } + return Object.keys(obj).sort().map(function(key) { return encodeURIComponent(key) + '=' + ( noencode ? softEncode(obj[key]) : encodeURIComponent(obj[key])); @@ -158,36 +161,41 @@ export function utilQsString(obj, noencode) { export function utilPrefixDOMProperty(property) { - var prefixes = ['webkit', 'ms', 'moz', 'o'], - i = -1, - n = prefixes.length, - s = document.body; + var prefixes = ['webkit', 'ms', 'moz', 'o']; + var i = -1; + var n = prefixes.length; + var s = document.body; if (property in s) return property; property = property.substr(0, 1).toUpperCase() + property.substr(1); - while (++i < n) - if (prefixes[i] + property in s) + while (++i < n) { + if (prefixes[i] + property in s) { return prefixes[i] + property; + } + } return false; } export function utilPrefixCSSProperty(property) { - var prefixes = ['webkit', 'ms', 'Moz', 'O'], - i = -1, - n = prefixes.length, - s = document.body.style; + var prefixes = ['webkit', 'ms', 'Moz', 'O']; + var i = -1; + var n = prefixes.length; + var s = document.body.style; - if (property.toLowerCase() in s) + if (property.toLowerCase() in s) { return property.toLowerCase(); + } - while (++i < n) - if (prefixes[i] + property in s) + while (++i < n) { + if (prefixes[i] + property in s) { return '-' + prefixes[i].toLowerCase() + property.replace(/([A-Z])/g, '-$1').toLowerCase(); + } + } return false; } @@ -195,10 +203,9 @@ export function utilPrefixCSSProperty(property) { var transformProperty; export function utilSetTransform(el, x, y, scale) { - var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'), - translate = utilDetect().opera ? - 'translate(' + x + 'px,' + y + 'px)' : - 'translate3d(' + x + 'px,' + y + 'px,0)'; + var prop = transformProperty = transformProperty || utilPrefixCSSProperty('Transform'); + var translate = utilDetect().opera ? 'translate(' + x + 'px,' + y + 'px)' + : 'translate3d(' + x + 'px,' + y + 'px,0)'; return el.style(prop, translate + (scale ? ' scale(' + scale + ')' : '')); } @@ -233,11 +240,12 @@ export function utilEditDistance(a, b) { // 1. Only works on HTML elements, not SVG // 2. Does not cause style recalculation export function utilFastMouse(container) { - var rect = container.getBoundingClientRect(), - rectLeft = rect.left, - rectTop = rect.top, - clientLeft = +container.clientLeft, - clientTop = +container.clientTop; + var rect = container.getBoundingClientRect(); + var rectLeft = rect.left; + var rectTop = rect.top; + var clientLeft = +container.clientLeft; + var clientTop = +container.clientTop; + if (textDirection === 'rtl') { rectLeft = 0; } @@ -255,9 +263,9 @@ export var utilGetPrototypeOf = Object.getPrototypeOf || function(obj) { return export function utilAsyncMap(inputs, func, callback) { - var remaining = inputs.length, - results = [], - errors = []; + var remaining = inputs.length; + var results = []; + var errors = []; inputs.forEach(function(d, i) { func(d, function done(err, data) { @@ -272,8 +280,9 @@ export function utilAsyncMap(inputs, func, callback) { // wraps an index to an interval [0..length-1] export function utilWrap(index, length) { - if (index < 0) + if (index < 0) { index += Math.ceil(-index/length)*length; + } return index % length; } From 528cbb29e14b6c26cce3335c0b32f89a6bdcdc90 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 00:36:41 -0500 Subject: [PATCH 090/114] Store the transform and selectedIDs with the rest of the history (closes #5664) --- modules/core/history.js | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/core/history.js b/modules/core/history.js index db837d45c..000220c02 100644 --- a/modules/core/history.js +++ b/modules/core/history.js @@ -51,9 +51,6 @@ export function coreHistory(context) { annotation = actions.pop(); } - _stack[_index].transform = context.projection.transform(); - _stack[_index].selectedIDs = context.selectedIDs(); - var graph = _stack[_index].graph; for (var i = 0; i < actions.length; i++) { graph = actions[i](graph, t); @@ -62,7 +59,9 @@ export function coreHistory(context) { return { graph: graph, annotation: annotation, - imageryUsed: _imageryUsed + imageryUsed: _imageryUsed, + transform: context.projection.transform(), + selectedIDs: context.selectedIDs() }; } @@ -401,7 +400,8 @@ export function coreHistory(context) { var base = _stack[0]; var s = _stack.map(function(i) { - var modified = [], deleted = []; + var modified = []; + var deleted = []; _forEach(i.graph.entities, function(entity, id) { if (entity) { @@ -431,6 +431,8 @@ export function coreHistory(context) { if (deleted.length) x.deleted = deleted; if (i.imageryUsed) x.imageryUsed = i.imageryUsed; if (i.annotation) x.annotation = i.annotation; + if (i.transform) x.transform = i.transform; + if (i.selectedIDs) x.selectedIDs = i.selectedIDs; return x; }); @@ -528,7 +530,9 @@ export function coreHistory(context) { return { graph: coreGraph(_stack[0].graph).load(entities), annotation: d.annotation, - imageryUsed: d.imageryUsed + imageryUsed: d.imageryUsed, + transform: d.transform, + selectedIDs: d.selectedIDs }; }); @@ -546,6 +550,11 @@ export function coreHistory(context) { }); } + var transform = _stack[_index].transform; + if (transform) { + context.map().transformEase(transform, 0); // 0 = immediate, no easing + } + if (loadComplete) { dispatch.call('change'); } From 5f20218f1fb49be429e2e544c027926a20d7b3d7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 02:15:28 -0500 Subject: [PATCH 091/114] 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({ From 5f2d80c7a66829e1c79cd0aa1c7909972e5ca102 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 10:58:47 -0500 Subject: [PATCH 092/114] pacify eslint --- modules/services/osm_wikibase.js | 22 +++++++++++----------- modules/ui/combobox.js | 11 ++++------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/modules/services/osm_wikibase.js b/modules/services/osm_wikibase.js index 366f6c5fc..1ef3637d7 100644 --- a/modules/services/osm_wikibase.js +++ b/modules/services/osm_wikibase.js @@ -9,7 +9,7 @@ import { utilQsString } from '../util'; var apibase = 'https://wiki.openstreetmap.org/w/api.php'; var _inflight = {}; var _wikibaseCache = {}; -var _localeIds = { en: false }; +var _localeIDs = { en: false }; var debouncedRequest = _debounce(request, 500, { leading: false }); @@ -45,7 +45,7 @@ export default { init: function() { _inflight = {}; _wikibaseCache = {}; - _localeIds = {}; + _localeIDs = {}; }, @@ -63,7 +63,7 @@ export default { */ claimToValue: function(entity, property, langCode) { if (!entity.claims[property]) return undefined; - var locale = _localeIds[langCode]; + var locale = _localeIDs[langCode]; var preferredPick, localePick; _forEach(entity.claims[property], function(stmt) { // If exists, use value limited to the needed language (has a qualifier P26 = locale) @@ -103,7 +103,7 @@ export default { var tagSitelink = params.value ? this.toSitelink(params.key, params.value) : false; var localeSitelink; - if (params.langCode && _localeIds[params.langCode] === undefined) { + if (params.langCode && _localeIDs[params.langCode] === undefined) { // If this is the first time we are asking about this locale, // fetch corresponding entity (if it exists), and cache it. // If there is no such entry, cache `false` value to avoid re-requesting it. @@ -155,7 +155,7 @@ export default { } else if (!d.success || d.error) { callback(d.error.messages.map(function(v) { return v.html['*']; }).join('
')); } else { - var localeId = false; + var localeID = false; _forEach(d.entities, function(res) { if (res.missing !== '') { var title = res.sitelinks.wiki.title; @@ -169,16 +169,16 @@ export default { _wikibaseCache[tagSitelink] = res; result.tag = res; } else if (title === localeSitelink) { - localeId = res.id; + localeID = res.id; } else { - console.log('Unexpected title ' + title); + console.log('Unexpected title ' + title); // eslint-disable-line no-console } } }); if (localeSitelink) { // If locale ID is not found, store false to prevent repeated queries - self.addLocale(params.langCode, localeId); + self.addLocale(params.langCode, localeID); } callback(null, result); @@ -189,12 +189,12 @@ export default { addLocale: function(langCode, qid) { // Makes it easier to unit test - _localeIds[langCode] = qid; + _localeIDs[langCode] = qid; }, - apibase: function(_) { + apibase: function(val) { if (!arguments.length) return apibase; - apibase = _; + apibase = val; return this; } diff --git a/modules/ui/combobox.js b/modules/ui/combobox.js index f3ae72464..961b9adb6 100644 --- a/modules/ui/combobox.js +++ b/modules/ui/combobox.js @@ -56,15 +56,12 @@ export function uiCombobox(context, klass) { var parent = this.parentNode; var sibling = this.nextSibling; - var caret = d3_select(parent).selectAll('.combobox-caret') + d3_select(parent).selectAll('.combobox-caret') .filter(function(d) { return d === input.node(); }) - .data([input.node()]); - - caret = caret.enter() + .data([input.node()]) + .enter() .insert('div', function() { return sibling; }) - .attr('class', 'combobox-caret') - .merge(caret); - + .attr('class', 'combobox-caret'); } From d9ee93e5784f65bc2b6fabd89591eb6b1e75e5c5 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 12:05:36 -0500 Subject: [PATCH 093/114] Fix secondary color in circularize icon It was hardcoded blue instead of using `currentColor`, preventing it from showing grey for disabled style --- svg/iD-sprite/operations/operation-circularize.svg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/svg/iD-sprite/operations/operation-circularize.svg b/svg/iD-sprite/operations/operation-circularize.svg index 547af94e9..a691303fb 100644 --- a/svg/iD-sprite/operations/operation-circularize.svg +++ b/svg/iD-sprite/operations/operation-circularize.svg @@ -1,6 +1,6 @@ - + From 6c5ea96ab6cf530e295c396b3e6156845114a0da Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 13:43:40 -0500 Subject: [PATCH 094/114] Adjust generic name validation to better check tag values e.g. "name=Fast Food" should match "amenity=fast_food" --- modules/validations/generic_name.js | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/validations/generic_name.js b/modules/validations/generic_name.js index 2e560d577..7cc27834a 100644 --- a/modules/validations/generic_name.js +++ b/modules/validations/generic_name.js @@ -7,16 +7,20 @@ export function validationGenericName() { 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; + var i, re; + + // test if the name is just the tag value (e.g. "park") + var keys = ['amenity', 'leisure', 'shop', 'man_made', 'tourism']; + for (i = 0; i < keys.length; i++) { + var val = entity.tags[keys[i]]; + if (val && val.replace(/\_/g, ' ').toLowerCase() === name.toLowerCase()) { + return name; + } } - for (var i = 0; i < discardNames.length; i++) { - var re = new RegExp(discardNames[i], 'i'); + // test if the name is a generic name (e.g. "pizzaria") + for (i = 0; i < discardNames.length; i++) { + re = new RegExp(discardNames[i], 'i'); if (re.test(name)) { return name; } From 5c6406ea63cfc3f48d68db220843e0ba96916db2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Tue, 8 Jan 2019 14:04:07 -0500 Subject: [PATCH 095/114] Remove section borders from inspector There is enough separation with the disclosure headings that we don't need to break the sidebar up with lines --- css/80_app.css | 6 +----- modules/ui/data_editor.js | 2 +- modules/ui/entity_editor.js | 32 ++++++++++++++++---------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 5fcf099b2..427945b7b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -784,10 +784,6 @@ a.hide-toggle { bottom: 0; } -.inspector-border { - border-bottom: 1px solid #ccc -} - .feature-list-pane .inspector-body, .preset-list-pane .inspector-body { top: 120px; @@ -1161,7 +1157,7 @@ img.tag-reference-wiki-image { ------------------------------------------------------- */ .preset-editor { overflow: hidden; - padding-bottom: 10px; + padding: 20px 0 10px 0; } .preset-editor a.hide-toggle { margin: 0 20px 5px 20px; diff --git a/modules/ui/data_editor.js b/modules/ui/data_editor.js index 593517de6..fda6bebd8 100644 --- a/modules/ui/data_editor.js +++ b/modules/ui/data_editor.js @@ -57,7 +57,7 @@ export function uiDataEditor(context) { rte.enter() .append('div') - .attr('class', 'inspector-border raw-tag-editor inspector-inner data-editor') + .attr('class', 'raw-tag-editor inspector-inner data-editor') .merge(rte) .call(rawTagEditor .expanded(true) diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index 68f41dea6..a056efb23 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -47,28 +47,28 @@ export function uiEntityEditor(context) { .data([0]); // Enter - var enter = header.enter() + var headerEnter = header.enter() .append('div') .attr('class', 'header fillL cf'); - enter + headerEnter .append('button') .attr('class', 'fl preset-reset preset-choose') .call(svgIcon((textDirection === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward')); - enter + headerEnter .append('button') .attr('class', 'fr preset-close') .on('click', function() { context.enter(modeBrowse(context)); }) .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close')); - enter + headerEnter .append('h3') .text(t('inspector.edit')); // Update header = header - .merge(enter); + .merge(headerEnter); header.selectAll('.preset-reset') .on('click', function() { @@ -81,11 +81,11 @@ export function uiEntityEditor(context) { .data([0]); // Enter - enter = body.enter() + var bodyEnter = body.enter() .append('div') .attr('class', 'inspector-body'); - enter + bodyEnter .append('div') .attr('class', 'preset-list-item inspector-inner') .append('div') @@ -98,23 +98,23 @@ export function uiEntityEditor(context) { .append('div') .attr('class', 'label-inner'); - enter + bodyEnter .append('div') - .attr('class', 'inspector-border preset-editor'); + .attr('class', 'preset-editor'); - enter + bodyEnter .append('div') - .attr('class', 'inspector-border raw-tag-editor inspector-inner'); + .attr('class', 'raw-tag-editor inspector-inner'); - enter + bodyEnter .append('div') - .attr('class', 'inspector-border raw-member-editor inspector-inner'); + .attr('class', 'raw-member-editor inspector-inner'); - enter + bodyEnter .append('div') .attr('class', 'raw-membership-editor inspector-inner'); - enter + bodyEnter .append('input') .attr('type', 'text') .attr('class', 'key-trap'); @@ -122,7 +122,7 @@ export function uiEntityEditor(context) { // Update body = body - .merge(enter); + .merge(bodyEnter); if (_tagReference) { body.selectAll('.preset-list-button-wrap') From 9c190e83e92e0a822159446588260ba1b8073344 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 9 Jan 2019 21:56:08 -0500 Subject: [PATCH 096/114] Add quickLinks to preset editor, zoom to selected feature --- css/80_app.css | 64 +++++++------ data/core.yaml | 8 ++ data/shortcuts.json | 4 +- dist/locales/en.json | 11 ++- modules/core/context.js | 4 +- modules/modes/select.js | 178 +++++++++++++++++++----------------- modules/renderer/map.js | 2 +- modules/ui/entity_editor.js | 51 ++++++++--- modules/ui/index.js | 1 + modules/ui/map_data.js | 6 +- modules/ui/quick_links.js | 62 +++++++++++++ 11 files changed, 253 insertions(+), 138 deletions(-) create mode 100644 modules/ui/quick_links.js diff --git a/css/80_app.css b/css/80_app.css index 427945b7b..551f63e8b 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -794,7 +794,7 @@ a.hide-toggle { } .inspector-inner { - padding: 20px; + padding: 20px 20px 5px 20px; position: relative; } @@ -1153,11 +1153,24 @@ img.tag-reference-wiki-image { } +/* Quick links +------------------------------------------------------- */ +.quick-links { + display: flex; + flex-flow: row wrap; + justify-content: flex-end; + padding: 0 20px; +} +.quick-link { + margin: 0 5px; +} + + /* Entity/Preset Editor ------------------------------------------------------- */ .preset-editor { overflow: hidden; - padding: 20px 0 10px 0; + padding: 10px 0px 5px 0px; } .preset-editor a.hide-toggle { margin: 0 20px 5px 20px; @@ -2352,6 +2365,7 @@ input.key-trap { /* hide but preserve in layout */ .inspector-hover .combobox-caret, .inspector-hover .header button, +.inspector-hover .quick-links, .inspector-hover .form-field-input-multicombo .chips .remove, .inspector-hover .hide-toggle:before, .inspector-hover .more-fields, @@ -4321,29 +4335,24 @@ svg.mouseclick use.right { font-size: 12px; white-space: initial; } - .tooltip.in { opacity: 0.9; z-index: 1030; height: auto; display: block; } - .tooltip.top { margin-top: -20px; text-align: center; } - .tooltip.right { margin-left: 20px; text-align: left; } - .tooltip.bottom { margin-top: 20px; text-align: center; } - .tooltip.left { margin-left: -20px; text-align: right; @@ -4368,7 +4377,6 @@ svg.mouseclick use.right { position: absolute; background: transparent; } - .tail::after { content: ""; position: absolute; @@ -4415,7 +4423,6 @@ svg.mouseclick use.right { border-color: transparent; border-style: solid; } - .tooltip.top .tooltip-arrow { bottom: -5px; left: 50%; @@ -4423,7 +4430,6 @@ svg.mouseclick use.right { border-top-color: #fff; border-width: 5px 5px 0; } - .tooltip.right .tooltip-arrow { top: 50%; left: -5px; @@ -4431,7 +4437,6 @@ svg.mouseclick use.right { border-right-color: #fff; border-width: 5px 5px 5px 0; } - .tooltip.left .tooltip-arrow { top: 50%; right: -5px; @@ -4439,7 +4444,6 @@ svg.mouseclick use.right { border-left-color: #fff; border-width: 5px 0 5px 5px; } - .tooltip.bottom .tooltip-arrow { top: -5px; left: 50%; @@ -4450,7 +4454,7 @@ svg.mouseclick use.right { .tooltip-heading { font-weight: bold; - background: #F6F6F6; + background: #f6f6f6; padding: 10px; margin: -10px -10px 10px -10px; border-radius: 3px 3px 0 0; @@ -4458,52 +4462,46 @@ svg.mouseclick use.right { } .keyhint-wrap { - background: #F6F6F6; + background: #f6f6f6; padding: 10px; margin: 10px -10px -10px -10px; border-radius: 0 0 3px 3px; } - .tooltip-inner .keyhint { font-weight: bold; margin-left: 5px; } -/* Exceptions for tooltip layouts */ -/* make tooltips in panels dark */ +/* dark tooltips for sidebar / panels */ .map-pane .tooltip.top .tooltip-arrow, -.entity-editor-pane .tooltip.top .tooltip-arrow, -.warning-section .tooltip.top .tooltip-arrow { +#sidebar .tooltip.top .tooltip-arrow { border-top-color: #000; } - .map-pane .tooltip.bottom .tooltip-arrow, -.entity-editor-pane .tooltip.bottom .tooltip-arrow, -.warning-section .tooltip.bottom .tooltip-arrow { +#sidebar .tooltip.bottom .tooltip-arrow { border-bottom-color: #000; } - .map-pane .tooltip.left .tooltip-arrow, -.entity-editor-pane .tooltip.left .tooltip-arrow, -.warning-section .tooltip.left .tooltip-arrow { +#sidebar .tooltip.left .tooltip-arrow { border-left-color: #000; } - .map-pane .tooltip.right .tooltip-arrow, -.entity-editor-pane .tooltip.right .tooltip-arrow, -.warning-section .tooltip.right .tooltip-arrow { +#sidebar .tooltip.right .tooltip-arrow { border-right-color: #000; } - .map-pane .tooltip-inner, .map-pane .tooltip-heading, .map-pane .keyhint-wrap, -.entity-editor-pane .tooltip-inner, -.warning-section .tooltip-inner { +#sidebar .tooltip-inner, +#sidebar .tooltip-heading, +#sidebar .keyhint-wrap { background: #000; color: #ccc; } + +/* Exceptions for tooltip layouts */ + /* commit warning tooltips need to be closer */ .warning-section .tooltip.top { margin-top: -5px; @@ -4519,11 +4517,11 @@ svg.mouseclick use.right { /* Move over tooltips that are near the edge of screen */ button.sidebar-toggle .tooltip .tooltip-arrow { - left: 32px; + left: 36px; } [dir='rtl'] button.sidebar-toggle .tooltip .tooltip-arrow { left: auto; - right: 32px; + right: 36px; } li:first-of-type .badge .tooltip, diff --git a/data/core.yaml b/data/core.yaml index be05db377..de0f1ddb1 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -403,6 +403,13 @@ en: title: Show My Location locating: "Locating, please wait..." inspector: + zoom_to: + key: Z + title: Zoom to this + tooltip_feature: "Center and zoom the map to focus on this feature." + tooltip_note: "Center and zoom the map to focus on this note." + tooltip_data: "Center and zoom the map to focus on this data." + tooltip_issue: "Center and zoom the map to focus on this issue." no_documentation_combination: There is no documentation available for this tag combination no_documentation_key: There is no documentation available for this key show_more: Show More @@ -1419,6 +1426,7 @@ en: with_selected: title: "With feature selected" edit_menu: "Toggle edit menu" + zoom_to: "Zoom to selected feature" vertex_selected: title: "With node selected" previous: "Jump to previous node" diff --git a/data/shortcuts.json b/data/shortcuts.json index 7f6bbe700..376cb9a94 100644 --- a/data/shortcuts.json +++ b/data/shortcuts.json @@ -115,8 +115,8 @@ "text": "shortcuts.browsing.with_selected.edit_menu" }, { - "shortcuts": [], - "text": "" + "shortcuts": ["inspector.zoom_to.key"], + "text": "shortcuts.browsing.with_selected.zoom_to" }, { "section": "vertex_selected", diff --git a/dist/locales/en.json b/dist/locales/en.json index f9bf8e766..7b6ca5a70 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -497,6 +497,14 @@ "locating": "Locating, please wait..." }, "inspector": { + "zoom_to": { + "key": "Z", + "title": "Zoom to this", + "tooltip_feature": "Center and zoom the map to focus on this feature.", + "tooltip_note": "Center and zoom the map to focus on this note.", + "tooltip_data": "Center and zoom the map to focus on this data.", + "tooltip_issue": "Center and zoom the map to focus on this issue." + }, "no_documentation_combination": "There is no documentation available for this tag combination", "no_documentation_key": "There is no documentation available for this key", "show_more": "Show More", @@ -1674,7 +1682,8 @@ }, "with_selected": { "title": "With feature selected", - "edit_menu": "Toggle edit menu" + "edit_menu": "Toggle edit menu", + "zoom_to": "Zoom in on selected feature" }, "vertex_selected": { "title": "With node selected", diff --git a/modules/core/context.js b/modules/core/context.js index 341c24901..3639eb453 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -145,7 +145,9 @@ export function coreContext() { this.loadEntity(entityID, function(err, result) { if (err) return; var entity = _find(result.data, function(e) { return e.id === entityID; }); - if (entity) { map.zoomTo(entity); } + if (entity) { + map.zoomTo(entity); + } }); } diff --git a/modules/modes/select.js b/modules/modes/select.js index ff6e93191..0b4c8e4e9 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -192,6 +192,14 @@ export function modeSelect(context, selectedIDs) { }; + mode.zoomToSelected = function() { + var entity = singular(); + if (entity) { + context.map().zoomTo(entity); + } + }; + + mode.reselect = function() { if (!checkSelectedIDs()) return; @@ -229,6 +237,91 @@ export function modeSelect(context, selectedIDs) { mode.enter = function() { + if (!checkSelectedIDs()) return; + + var operations = _without(_values(Operations), Operations.operationDelete) + .map(function(o) { return o(selectedIDs, context); }) + .filter(function(o) { return o.available(); }); + + // deprecation warning - Radial Menu to be removed in iD v3 + var isRadialMenu = context.storage('edit-menu-style') === 'radial'; + if (isRadialMenu) { + operations = operations.slice(0,7); + operations.unshift(Operations.operationDelete(selectedIDs, context)); + } else { + operations.push(Operations.operationDelete(selectedIDs, context)); + } + + operations.forEach(function(operation) { + if (operation.behavior) { + behaviors.push(operation.behavior); + } + }); + + behaviors.forEach(context.install); + + keybinding + .on(t('inspector.zoom_to.key'), mode.zoomToSelected) + .on(['[', 'pgup'], previousVertex) + .on([']', 'pgdown'], nextVertex) + .on(['{', uiCmd('⌘['), 'home'], firstVertex) + .on(['}', uiCmd('⌘]'), 'end'], lastVertex) + .on(['\\', 'pause'], nextParent) + .on('⎋', esc, true) + .on('space', toggleMenu); + + d3_select(document) + .call(keybinding); + + + // deprecation warning - Radial Menu to be removed in iD v3 + editMenu = isRadialMenu + ? uiRadialMenu(context, operations) + : uiEditMenu(context, operations); + + context.ui().sidebar + .select(singular() ? singular().id : null, newFeature); + + context.history() + .on('undone.select', update) + .on('redone.select', update); + + context.map() + .on('move.select', closeMenu) + .on('drawn.select', selectElements); + + context.surface() + .on('dblclick.select', dblclick); + + + selectElements(); + + if (selectedIDs.length > 1) { + var entities = uiSelectionList(context, selectedIDs); + context.ui().sidebar.show(entities); + } + + if (follow) { + var extent = geoExtent(); + var graph = context.graph(); + selectedIDs.forEach(function(id) { + var entity = context.entity(id); + extent._extend(entity.extent(graph)); + }); + + var loc = extent.center(); + context.map().centerEase(loc); + } else if (singular() && singular().type === 'way') { + context.map().pan([0,0]); // full redraw, to adjust z-sorting #2914 + } + + timeout = window.setTimeout(function() { + positionMenu(); + if (!suppressMenu) { + showMenu(); + } + }, 270); /* after any centerEase completes */ + function update() { closeMenu(); @@ -419,91 +512,6 @@ export function modeSelect(context, selectedIDs) { .classed('related', true); } } - - - if (!checkSelectedIDs()) return; - - var operations = _without(_values(Operations), Operations.operationDelete) - .map(function(o) { return o(selectedIDs, context); }) - .filter(function(o) { return o.available(); }); - - // deprecation warning - Radial Menu to be removed in iD v3 - var isRadialMenu = context.storage('edit-menu-style') === 'radial'; - if (isRadialMenu) { - operations = operations.slice(0,7); - operations.unshift(Operations.operationDelete(selectedIDs, context)); - } else { - operations.push(Operations.operationDelete(selectedIDs, context)); - } - - operations.forEach(function(operation) { - if (operation.behavior) { - behaviors.push(operation.behavior); - } - }); - - behaviors.forEach(context.install); - - keybinding - .on(['[', 'pgup'], previousVertex) - .on([']', 'pgdown'], nextVertex) - .on(['{', uiCmd('⌘['), 'home'], firstVertex) - .on(['}', uiCmd('⌘]'), 'end'], lastVertex) - .on(['\\', 'pause'], nextParent) - .on('⎋', esc, true) - .on('space', toggleMenu); - - d3_select(document) - .call(keybinding); - - - // deprecation warning - Radial Menu to be removed in iD v3 - editMenu = isRadialMenu - ? uiRadialMenu(context, operations) - : uiEditMenu(context, operations); - - context.ui().sidebar - .select(singular() ? singular().id : null, newFeature); - - context.history() - .on('undone.select', update) - .on('redone.select', update); - - context.map() - .on('move.select', closeMenu) - .on('drawn.select', selectElements); - - context.surface() - .on('dblclick.select', dblclick); - - - selectElements(); - - if (selectedIDs.length > 1) { - var entities = uiSelectionList(context, selectedIDs); - context.ui().sidebar.show(entities); - } - - if (follow) { - var extent = geoExtent(); - var graph = context.graph(); - selectedIDs.forEach(function(id) { - var entity = context.entity(id); - extent._extend(entity.extent(graph)); - }); - - var loc = extent.center(); - context.map().centerEase(loc); - } else if (singular() && singular().type === 'way') { - context.map().pan([0,0]); // full redraw, to adjust z-sorting #2914 - } - - timeout = window.setTimeout(function() { - positionMenu(); - if (!suppressMenu) { - showMenu(); - } - }, 270); /* after any centerEase completes */ }; diff --git a/modules/renderer/map.js b/modules/renderer/map.js index 3c4c0c3f8..e5ba177ec 100644 --- a/modules/renderer/map.js +++ b/modules/renderer/map.js @@ -858,7 +858,7 @@ export function rendererMap(context) { if (!isFinite(extent.area())) return; var z2 = map.trimmedExtentZoom(extent); - zoomLimits = zoomLimits || [context.minEditableZoom(), 19]; + zoomLimits = zoomLimits || [context.minEditableZoom(), 20]; map.centerZoom(extent.center(), Math.min(Math.max(z2, zoomLimits[0]), zoomLimits[1])); }; diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index a056efb23..12bce49d1 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -14,12 +14,14 @@ import { tooltip } from '../util/tooltip'; import { actionChangeTags } from '../actions'; import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; +import { uiPresetEditor } from './preset_editor'; import { uiPresetIcon } from './preset_icon'; +import { uiQuickLinks } from './quick_links'; import { uiRawMemberEditor } from './raw_member_editor'; import { uiRawMembershipEditor } from './raw_membership_editor'; import { uiRawTagEditor } from './raw_tag_editor'; import { uiTagReference } from './tag_reference'; -import { uiPresetEditor } from './preset_editor'; +import { uiTooltipHtml } from './tooltipHtml'; import { utilCleanTags, utilRebind } from '../util'; @@ -33,6 +35,7 @@ export function uiEntityEditor(context) { var _activePreset; var _tagReference; + var quickLinks = uiQuickLinks(); var presetEditor = uiPresetEditor(context).on('change', changeTags); var rawTagEditor = uiRawTagEditor(context).on('change', changeTags); var rawMemberEditor = uiRawMemberEditor(context); @@ -98,6 +101,10 @@ export function uiEntityEditor(context) { .append('div') .attr('class', 'label-inner'); + bodyEnter + .append('div') + .attr('class', 'preset-quick-links'); + bodyEnter .append('div') .attr('class', 'preset-editor'); @@ -124,6 +131,7 @@ export function uiEntityEditor(context) { body = body .merge(bodyEnter); + // update header if (_tagReference) { body.selectAll('.preset-list-button-wrap') .call(_tagReference.button); @@ -143,7 +151,6 @@ export function uiEntityEditor(context) { .preset(_activePreset) ); - var label = body.select('.label-inner'); var nameparts = label.selectAll('.namepart') .data(_activePreset.name().split(' - '), function(d) { return d; }); @@ -158,6 +165,28 @@ export function uiEntityEditor(context) { .text(function(d) { return d; }); + // update quick links + var choices = [{ + id: 'zoom_to', + label: 'inspector.zoom_to.title', + tooltip: function() { + return uiTooltipHtml(t('inspector.zoom_to.tooltip_feature'), t('inspector.zoom_to.key')); + }, + click: function zoomTo() { + context.mode().zoomToSelected(); + // d3_event.preventDefault(); + // var entity = context.hasEntity(_entityID); + // if (entity) { + // context.map().zoomTo(entity) + // } + } + }]; + + body.select('.preset-quick-links') + .call(quickLinks.choices(choices)); + + + // update editor sections body.select('.preset-editor') .call(presetEditor .preset(_activePreset) @@ -256,25 +285,25 @@ export function uiEntityEditor(context) { } - entityEditor.modified = function(_) { + entityEditor.modified = function(val) { if (!arguments.length) return _modified; - _modified = _; + _modified = val; d3_selectAll('button.preset-close use') .attr('xlink:href', (_modified ? '#iD-icon-apply' : '#iD-icon-close')); return entityEditor; }; - entityEditor.state = function(_) { + entityEditor.state = function(val) { if (!arguments.length) return _state; - _state = _; + _state = val; return entityEditor; }; - entityEditor.entityID = function(_) { + entityEditor.entityID = function(val) { if (!arguments.length) return _entityID; - _entityID = _; + _entityID = val; _base = context.graph(); _coalesceChanges = false; @@ -292,10 +321,10 @@ export function uiEntityEditor(context) { }; - entityEditor.preset = function(_) { + entityEditor.preset = function(val) { if (!arguments.length) return _activePreset; - if (_ !== _activePreset) { - _activePreset = _; + if (val !== _activePreset) { + _activePreset = val; _tagReference = uiTagReference(_activePreset.reference(context.geometry(_entityID)), context) .showing(false); } diff --git a/modules/ui/index.js b/modules/ui/index.js index d92bd0a7d..de4ff07c7 100644 --- a/modules/ui/index.js +++ b/modules/ui/index.js @@ -47,6 +47,7 @@ export { uiNoteReport } from './note_report'; export { uiPresetEditor } from './preset_editor'; export { uiPresetIcon } from './preset_icon'; export { uiPresetList } from './preset_list'; +export { uiQuickLinks } from './quick_links'; export { uiRadialMenu } from './radial_menu'; export { uiRawMemberEditor } from './raw_member_editor'; export { uiRawMembershipEditor } from './raw_membership_editor'; diff --git a/modules/ui/map_data.js b/modules/ui/map_data.js index a6cee9fd7..0adfc3100 100644 --- a/modules/ui/map_data.js +++ b/modules/ui/map_data.js @@ -496,10 +496,8 @@ export function uiMapData(context) { .call(tooltip() .html(true) .title(function(d) { - var tip = t(name + '.' + d + '.tooltip'), - key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null); - - + var tip = t(name + '.' + d + '.tooltip'); + var key = (d === 'wireframe' ? t('area_fill.wireframe.key') : null); if ((name === 'feature' || name === 'keepRight') && autoHiddenFeature(d)) { var msg = showsLayer('osm') ? t('map_data.autohidden') : t('map_data.osmhidden'); tip += '
' + msg + '
'; diff --git a/modules/ui/quick_links.js b/modules/ui/quick_links.js new file mode 100644 index 000000000..9b1a7149d --- /dev/null +++ b/modules/ui/quick_links.js @@ -0,0 +1,62 @@ +import { + event as d3_event, + select as d3_select +} from 'd3-selection'; + +import { t } from '../util/locale'; +import { tooltip } from '../util/tooltip'; + + +export function uiQuickLinks() { + var _choices = []; + + + function quickLinks(selection) { + var container = selection.selectAll('.quick-links') + .data([0]); + + container = container.enter() + .append('div') + .attr('class', 'quick-links') + .merge(container); + + var items = container.selectAll('.quick-link') + .data(_choices, function(d) { return d.id; }); + + items.exit() + .remove(); + + items.enter() + .append('a') + .attr('class', function(d) { return 'quick-link quick-link-' + d.id; }) + .attr('href', '#') + .text(function(d) { return t(d.label); }) + .each(function(d) { + if (typeof d.tooltip !== 'function') return; + d3_select(this) + .call(tooltip().html(true).title(d.tooltip).placement('bottom')); + }) + .on('click', function(d) { + if (typeof d.click !== 'function') return; + d3_event.preventDefault(); + d.click(d); + }); + } + + + // val should be an array of choices like: + // [{ + // id: 'link-id', + // label: 'translation.key', + // tooltip: function(d), + // click: function(d) + // }, ..] + quickLinks.choices = function(val) { + if (!arguments.length) return _choices; + _choices = val; + return quickLinks; + }; + + + return quickLinks; +} From 6efbbb8aa3a8fc8f5c0f26d68b01590ba410e126 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 9 Jan 2019 22:24:52 -0500 Subject: [PATCH 097/114] Add "zoom to this" quicklink and keybind for note editor (closes #5169) --- css/80_app.css | 4 ++++ dist/locales/en.json | 2 +- modules/modes/select_note.js | 19 ++++++++++++++++--- modules/ui/entity_editor.js | 5 ----- modules/ui/note_editor.js | 17 +++++++++++++++++ 5 files changed, 38 insertions(+), 9 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 551f63e8b..1f2f0259d 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1165,6 +1165,10 @@ img.tag-reference-wiki-image { margin: 0 5px; } +.note-editor .quick-links { + padding: 5px 0 0 0; +} + /* Entity/Preset Editor ------------------------------------------------------- */ diff --git a/dist/locales/en.json b/dist/locales/en.json index 7b6ca5a70..405536eef 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1683,7 +1683,7 @@ "with_selected": { "title": "With feature selected", "edit_menu": "Toggle edit menu", - "zoom_to": "Zoom in on selected feature" + "zoom_to": "Zoom to selected feature" }, "vertex_selected": { "title": "With node selected", diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index dd24ffd78..0ef32c58b 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -10,6 +10,8 @@ import { behaviorSelect } from '../behavior'; +import { t } from '../util/locale'; + import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { services } from '../services'; import { uiNoteEditor } from '../ui'; @@ -84,9 +86,18 @@ export function modeSelectNote(context, selectedNoteID) { } - mode.newFeature = function(_) { + mode.zoomToSelected = function() { + if (!osm) return; + var note = osm.getNote(selectedNoteID); + if (note) { + context.map().centerZoom(note.loc, 20); + } + }; + + + mode.newFeature = function(val) { if (!arguments.length) return newFeature; - newFeature = _; + newFeature = val; return mode; }; @@ -96,7 +107,9 @@ export function modeSelectNote(context, selectedNoteID) { if (!note) return; behaviors.forEach(context.install); - keybinding.on('⎋', esc, true); + keybinding + .on(t('inspector.zoom_to.key'), mode.zoomToSelected) + .on('⎋', esc, true); d3_select(document) .call(keybinding); diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index 12bce49d1..0097d8144 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -174,11 +174,6 @@ export function uiEntityEditor(context) { }, click: function zoomTo() { context.mode().zoomToSelected(); - // d3_event.preventDefault(); - // var entity = context.hasEntity(_entityID); - // if (entity) { - // context.map().zoomTo(entity) - // } } }]; diff --git a/modules/ui/note_editor.js b/modules/ui/note_editor.js index 5de11ff5d..7cd4b2511 100644 --- a/modules/ui/note_editor.js +++ b/modules/ui/note_editor.js @@ -16,6 +16,8 @@ import { uiNoteComments, uiNoteHeader, uiNoteReport, + uiQuickLinks, + uiTooltipHtml, uiViewOnOSM, } from './index'; @@ -27,6 +29,7 @@ import { export function uiNoteEditor(context) { var dispatch = d3_dispatch('change'); + var quickLinks = uiQuickLinks(); var noteComments = uiNoteComments(); var noteHeader = uiNoteHeader(); @@ -37,6 +40,19 @@ export function uiNoteEditor(context) { function noteEditor(selection) { + // quick links + var choices = [{ + id: 'zoom_to', + label: 'inspector.zoom_to.title', + tooltip: function() { + return uiTooltipHtml(t('inspector.zoom_to.tooltip_note'), t('inspector.zoom_to.key')); + }, + click: function zoomTo() { + context.mode().zoomToSelected(); + } + }]; + + var header = selection.selectAll('.header') .data([0]); @@ -73,6 +89,7 @@ export function uiNoteEditor(context) { .attr('class', 'modal-section note-editor') .merge(editor) .call(noteHeader.note(_note)) + .call(quickLinks.choices(choices)) .call(noteComments.note(_note)) .call(noteSaveSection); From 9282a5179578e688234247d5feec57fecabe4c67 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 9 Jan 2019 22:49:51 -0500 Subject: [PATCH 098/114] Add "zoom to this" quicklink and keybind for data editor (re #5169) --- css/80_app.css | 1 + modules/modes/select_data.js | 13 ++++++++++++- modules/modes/select_note.js | 1 + modules/ui/data_editor.js | 21 +++++++++++++++++++-- 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 1f2f0259d..06066938f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1165,6 +1165,7 @@ img.tag-reference-wiki-image { margin: 0 5px; } +.data-editor .quick-links, .note-editor .quick-links { padding: 5px 0 0 0; } diff --git a/modules/modes/select_data.js b/modules/modes/select_data.js index ad257b8e6..744800654 100644 --- a/modules/modes/select_data.js +++ b/modules/modes/select_data.js @@ -12,6 +12,8 @@ import { behaviorSelect } from '../behavior'; +import { t } from '../util/locale'; + import { geoExtent } from '../geo'; import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { uiDataEditor } from '../ui'; @@ -60,9 +62,18 @@ export function modeSelectData(context, selectedDatum) { } + mode.zoomToSelected = function() { + var extent = geoExtent(d3_geoBounds(selectedDatum)); + context.map().centerZoom(extent.center(), context.map().trimmedExtentZoom(extent)); + }; + + mode.enter = function() { behaviors.forEach(context.install); - keybinding.on('⎋', esc, true); + + keybinding + .on(t('inspector.zoom_to.key'), mode.zoomToSelected) + .on('⎋', esc, true); d3_select(document) .call(keybinding); diff --git a/modules/modes/select_note.js b/modules/modes/select_note.js index 0ef32c58b..099d97a15 100644 --- a/modules/modes/select_note.js +++ b/modules/modes/select_note.js @@ -107,6 +107,7 @@ export function modeSelectNote(context, selectedNoteID) { if (!note) return; behaviors.forEach(context.install); + keybinding .on(t('inspector.zoom_to.key'), mode.zoomToSelected) .on('⎋', esc, true); diff --git a/modules/ui/data_editor.js b/modules/ui/data_editor.js index fda6bebd8..59d25bb3f 100644 --- a/modules/ui/data_editor.js +++ b/modules/ui/data_editor.js @@ -4,17 +4,33 @@ import { svgIcon } from '../svg'; import { uiDataHeader, - uiRawTagEditor + uiQuickLinks, + uiRawTagEditor, + uiTooltipHtml } from './index'; export function uiDataEditor(context) { var dataHeader = uiDataHeader(); + var quickLinks = uiQuickLinks(); var rawTagEditor = uiRawTagEditor(context); var _datum; function dataEditor(selection) { + // quick links + var choices = [{ + id: 'zoom_to', + label: 'inspector.zoom_to.title', + tooltip: function() { + return uiTooltipHtml(t('inspector.zoom_to.tooltip_data'), t('inspector.zoom_to.key')); + }, + click: function zoomTo() { + context.mode().zoomToSelected(); + } + }]; + + var header = selection.selectAll('.header') .data([0]); @@ -50,7 +66,8 @@ export function uiDataEditor(context) { .append('div') .attr('class', 'modal-section data-editor') .merge(editor) - .call(dataHeader.datum(_datum)); + .call(dataHeader.datum(_datum)) + .call(quickLinks.choices(choices)); var rte = body.selectAll('.raw-tag-editor') .data([0]); From 98c08a02efe30aab2482e1a0e89267ff88957a83 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 9 Jan 2019 22:59:44 -0500 Subject: [PATCH 099/114] Add "zoom to this" quicklink and keybind for keepright editor (re #5169) --- css/80_app.css | 1 + modules/modes/select_error.js | 46 +++++++++++++++++++++------------- modules/ui/keepRight_editor.js | 27 ++++++++++++++++++-- 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 06066938f..1f6e116dc 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1166,6 +1166,7 @@ img.tag-reference-wiki-image { } .data-editor .quick-links, +.keepRight-editor .quick-links, .note-editor .quick-links { padding: 5px 0 0 0; } diff --git a/modules/modes/select_error.js b/modules/modes/select_error.js index abd6ea8bf..aa0c8ef3b 100644 --- a/modules/modes/select_error.js +++ b/modules/modes/select_error.js @@ -10,6 +10,7 @@ import { behaviorSelect } from '../behavior'; +import { t } from '../util/locale'; import { services } from '../services'; import { modeBrowse, modeDragNode, modeDragNote } from '../modes'; import { uiKeepRightEditor } from '../ui'; @@ -53,7 +54,35 @@ export function modeSelectError(context, selectedErrorID) { } + mode.zoomToSelected = function() { + if (!keepRight) return; + var error = keepRight.getError(selectedErrorID); + if (error) { + context.map().centerZoom(error.loc, 20); + } + }; + + mode.enter = function() { + var error = checkSelectedID(); + if (!error) return; + + behaviors.forEach(context.install); + keybinding + .on(t('inspector.zoom_to.key'), mode.zoomToSelected) + .on('⎋', esc, true); + + d3_select(document) + .call(keybinding); + + selectError(); + + var sidebar = context.ui().sidebar; + sidebar.show(keepRightEditor.error(error)); + + context.map() + .on('drawn.select-error', selectError); + // class the error as selected, or return to browse mode if the error is gone function selectError(drawn) { @@ -82,23 +111,6 @@ export function modeSelectError(context, selectedErrorID) { if (d3_select('.combobox').size()) return; context.enter(modeBrowse(context)); } - - var error = checkSelectedID(); - if (!error) return; - - behaviors.forEach(context.install); - keybinding.on('⎋', esc, true); - - d3_select(document) - .call(keybinding); - - selectError(); - - var sidebar = context.ui().sidebar; - sidebar.show(keepRightEditor.error(error)); - - context.map() - .on('drawn.select-error', selectError); }; diff --git a/modules/ui/keepRight_editor.js b/modules/ui/keepRight_editor.js index 532c8c3e2..ca502b1e1 100644 --- a/modules/ui/keepRight_editor.js +++ b/modules/ui/keepRight_editor.js @@ -5,7 +5,15 @@ import { t } from '../util/locale'; import { services } from '../services'; import { modeBrowse } from '../modes'; import { svgIcon } from '../svg'; -import { uiKeepRightDetails, uiKeepRightHeader, uiViewOnKeepRight } from './index'; + +import { + uiKeepRightDetails, + uiKeepRightHeader, + uiQuickLinks, + uiTooltipHtml, + uiViewOnKeepRight +} from './index'; + import { utilNoAuto, utilRebind } from '../util'; @@ -13,11 +21,25 @@ export function uiKeepRightEditor(context) { var dispatch = d3_dispatch('change'); var keepRightDetails = uiKeepRightDetails(context); var keepRightHeader = uiKeepRightHeader(context); + var quickLinks = uiQuickLinks(); var _error; function keepRightEditor(selection) { + // quick links + var choices = [{ + id: 'zoom_to', + label: 'inspector.zoom_to.title', + tooltip: function() { + return uiTooltipHtml(t('inspector.zoom_to.tooltip_issue'), t('inspector.zoom_to.key')); + }, + click: function zoomTo() { + context.mode().zoomToSelected(); + } + }]; + + var header = selection.selectAll('.header') .data([0]); @@ -46,7 +68,7 @@ export function uiKeepRightEditor(context) { .attr('class', 'body') .merge(body); - var editor = body.selectAll('.error-editor') + var editor = body.selectAll('.keepRight-editor') .data([0]); editor.enter() @@ -54,6 +76,7 @@ export function uiKeepRightEditor(context) { .attr('class', 'modal-section keepRight-editor') .merge(editor) .call(keepRightHeader.error(_error)) + .call(quickLinks.choices(choices)) .call(keepRightDetails.error(_error)) .call(keepRightSaveSection); From 7475bcdf2fd8660099b13d2d9ecaaaa6c659e547 Mon Sep 17 00:00:00 2001 From: Iman Date: Thu, 10 Jan 2019 14:52:13 +0330 Subject: [PATCH 100/114] geocode-item centering for rtl --- css/80_app.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index 1f6e116dc..dca9a5285 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -847,6 +847,10 @@ a.hide-toggle { border-radius: 2px; } +[dir='rtl'] .geocode-item { + left: -25%; +} + .geocode-item:hover { background-color: #aaa; } From 7f321abbaf2742e18fc6e9639411afacc754c93e Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Jan 2019 17:38:09 -0500 Subject: [PATCH 101/114] Add links to the main error object in KeepRight error messages re: https://github.com/openstreetmap/iD/issues/5679#issuecomment-452281850 --- css/80_app.css | 3 + data/core.yaml | 169 ++++++++++++----------- data/keepRight.json | 235 +++++++++++++++++++++----------- dist/locales/en.json | 141 ++++++++++--------- modules/services/keepRight.js | 44 +++++- modules/ui/keepRight_details.js | 25 +++- 6 files changed, 381 insertions(+), 236 deletions(-) diff --git a/css/80_app.css b/css/80_app.css index 1f6e116dc..647d3158c 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2595,6 +2595,9 @@ input.key-trap { .kr_error-details-description { margin-bottom: 10px; } +.kr_error-details-description-text::first-letter { + text-transform: capitalize; +} /* Custom Data Editor diff --git a/data/core.yaml b/data/core.yaml index de0f1ddb1..9f9d8b8e5 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -669,131 +669,142 @@ en: close_comment: Close and Comment ignore_comment: Ignore and Comment error_parts: - node: node - way: way - relation: relation - highway: highway - railway: railway - waterway: waterway - cycleway: cycleway - cycleway_footpath: 'cycleway/footpath' - riverbank: riverbank - bridge: bridge - tunnel: tunnel + node: 'this node' + way: 'this way' + relation: 'this relation' + oneway: 'this oneway' + highway: 'this highway' + railway: 'this railway' + waterway: 'this waterway' + cycleway: 'this cycleway' + cycleway_footpath: 'this cycleway/footpath' + riverbank: 'this riverbank' + crossing: 'this crossing' + railway_crossing: 'this railway crossing' + bridge: 'this bridge' + tunnel: 'this tunnel' + boundary: 'this boundary' + turn_restriction: 'this turn restriction' + roundabout: 'this roundabout' + mini_roundabout: 'this mini-roundabout' + track: 'this track' + feature: 'this feature' place_of_worship: 'place of worship' - pub: pub - restaurant: restaurant - school: school - university: university - hospital: hospital - library: library - theatre: theatre - courthouse: courthouse - bank: bank - cinema: cinema - pharmacy: pharmacy - cafe: cafe + pub: 'pub' + restaurant: 'restaurant' + school: 'school' + university: 'university' + hospital: 'hospital' + library: 'library' + theatre: 'theatre' + courthouse: 'courthouse' + bank: 'bank' + cinema: 'cinema' + pharmacy: 'pharmacy' + cafe: 'cafe' fast_food: 'fast food' - fuel: fuel - from: from - to: to + fuel: 'fuel' + from: 'from' + to: 'to' + left_hand: 'left-hand' + right_hand: 'right-hand' errorTypes: 20: title: 'Multiple nodes on the same spot' description: 'There is more than one node in this spot. Node IDs: {var1}.' 30: title: 'Non-closed area' - description: 'This way is tagged with "{var1}" and should be a closed loop.' + description: '{var1} is tagged with "{var2}" and should be a closed loop.' 40: title: 'Impossible oneway' - description: 'The first node {var1} of this oneway is not connected to any other way.' + description: 'The first node {var1} of {var2} is not connected to any other way.' 41: - description: 'The last node {var1} of this oneway is not connected to any other way.' + description: 'The last node {var1} of {var2} is not connected to any other way.' 42: - description: 'You cannot reach this node because all ways leading from it are oneway.' + description: 'You cannot reach {var1} because all ways leading from it are oneway.' 43: - description: 'You cannot escape from this node because all ways leading to it are oneway.' + description: 'You cannot escape from {var1} because all ways leading to it are oneway.' 50: title: 'Almost junction' - description: 'This node is very close but not connected to way {var1}.' + description: '{var1} is very close but not connected to way {var2}.' 60: title: 'Deprecated tag' - description: 'This {var1} uses deprecated tag "{var2}". Please use "{var3}" instead.' + description: '{var1} uses deprecated tag "{var2}". Please use "{var3}" instead.' 70: title: 'Missing tag' - description: 'This {var1} has an empty tag: "{var2}".' + description: '{var1} has an empty tag: "{var2}".' 71: - description: 'This way has no tags.' + description: '{var1} has no tags.' 72: - description: 'This node is not member of any way and doesn''t have any tags.' + description: '{var1} is not member of any way and doesn''t have any tags.' 73: - description: 'This way has a "{var1}" tag but no "highway" tag.' + description: '{var1} has a "{var2}" tag but no "highway" tag.' 74: - description: 'This {var1} has an empty tag: "{var2}".' + description: '{var1} has an empty tag: "{var2}".' 75: - description: 'This {var1} has a name "{var2}" but no other tags.' + description: '{var1} has a name "{var2}" but no other tags.' 90: title: 'Motorway without ref tag' - description: 'This way is tagged as a motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' + description: '{var1} is tagged as a motorway and therefore needs a "ref", "nat_ref", or "int_ref" tag.' 100: title: 'Place of worship without religion' - description: 'This {var1} is tagged as a place of worship and therefore needs a religion tag.' + description: '{var1} is tagged as a place of worship and therefore needs a religion tag.' 110: title: 'Point of interest without name' - description: 'This node is tagged as a "{var1}" and therefore needs a name tag.' + description: '{var1} is tagged as a "{var2}" and therefore needs a name tag.' 120: title: 'Way without nodes' - description: 'This way has just one single node.' + description: '{var1} has just one single node.' 130: title: 'Disconnected way' - description: 'This way is not connected to the rest of the map.' + description: '{var1} is not connected to the rest of the map.' 150: title: 'Railway crossing without tag' - description: 'This crossing of a highway and a railway needs to be tagged as "railway=crossing" or "railway=level_crossing".' + description: '{var1} of a highway and a railway needs to be tagged as "railway=crossing" or "railway=level_crossing".' 160: title: 'Railway layer conflict' - description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing.' + description: 'There are ways in different layers (e.g. tunnel or bridge) meeting at {var1}.' 170: title: 'FIXME tagged item' - description: '{var1}' + description: '{var1} has a FIXME tag: {var2}' 180: title: 'Relation without type' - description: 'This relation is missing a "type" tag.' + description: '{var1} is missing a "type" tag.' 190: title: 'Intersection without junction' - description: 'This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel.' + description: '{var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel.' 200: title: 'Overlapping ways' - description: 'This {var1} overlaps the {var2} {var3}.' + description: '{var1} overlaps the {var2} {var3}.' 210: title: 'Self-intersecting way' description: 'There is an unspecified issue with self intersecting ways.' 211: - description: 'This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error.' + description: '{var1} contains more than one node multiple times. Nodes are {var2}. This may or may not be an error.' 212: - description: 'This way has only two different nodes and contains one of them more than once.' + description: '{var1} has only two different nodes and contains one of them more than once.' 220: title: 'Misspelled tag' - description: 'This {var1} is tagged "{var2}" where "{var3}" looks like "{var4}".' + description: '{var1} is tagged "{var2}" where "{var3}" looks like "{var4}".' 221: - description: 'This {var1} has a suspicious tag "{var2}".' + description: '{var1} has a suspicious tag "{var2}".' 230: title: 'Layer conflict' - description: 'This node is a junction of ways on different layers.' + description: '{var1} is a junction of ways on different layers.' 231: - description: 'This node is a junction of ways on different layers: {var1}.' + description: '{var1} is a junction of ways on different layers: {var2}.' layer: '(layer: {layer})' 232: - description: 'This {var1} is tagged with "layer={var2}". This need not be an error but it looks strange.' + description: '{var1} is tagged with "layer={var2}". This need not be an error but it looks strange.' 270: title: 'Unusual motorway connection' - description: 'This node is a junction of a motorway and a highway other than "motorway", "motorway_link", "trunk", "rest_area", or "construction". Connection to "service" or "unclassified" is only valid if it has "access=no/private", or it leads to a motorway service area, or if it is a "service=parking_aisle".' + description: '{var1} is a junction of a motorway and a highway other than "motorway", "motorway_link", "trunk", "rest_area", or "construction". Connection to "service" or "unclassified" is only valid if it has "access=no/private", or it leads to a motorway service area, or if it is a "service=parking_aisle".' 280: title: 'Boundary issue' description: 'There is an unspecified issue with this boundary.' 281: title: 'Boundary missing name' - description: 'This boundary has no name.' + description: '{var1} has no name.' 282: title: 'Boundary missing admin level' description: 'The boundary of {var1} has no valid numeric admin_level. Please do not mix admin levels (e.g. "6;7"). Always tag the lowest admin_level of all boundaries.' @@ -805,68 +816,68 @@ en: description: 'The boundary of {var1} splits here.' 285: title: 'Boundary admin_level too high' - description: 'This boundary way has "admin_level={var1}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.' + description: '{var1} has "admin_level={var2}" but belongs to a relation with lower "admin_level" (e.g. higher priority); it should have the lowest "admin_level" of all relations.' 290: title: 'Restriction issue' description: 'There is an unspecified issue with this restriction.' 291: title: 'Restriction missing type' - description: 'This turn restriction has an unrecognized restriction type.' + description: '{var1} has an unrecognized restriction type.' 292: title: 'Restriction missing "from" way' - description: 'A turn restriction needs exactly one "from" member. This one has {var1}.' + description: '{var1} has {var2} "from" members, but it should have 1.' 293: title: 'Restriction missing "to" way' - description: 'A turn restriction needs exactly one "to" member. This one has {var1}.' + description: '{var1} has {var2} "to" members, but it should have 1.' 294: title: 'Restriction "from" or "to" is not a way' - description: '"from" and "to" members of turn restrictions need to be ways. {var1}.' + description: '{var1} has "from" or "to" members which should be ways. {var2}.' 295: title: 'Restriction "via" is not an endpoint' - description: '"via" (node {var1}) is not the first or the last member of "{var2}" (way {var3}).' + description: '{var1} has a "via" (node {var2}) which is not the first or the last member of "{var3}" (way {var4}).' 296: title: 'Unusual restriction angle' - description: 'Restriction type is "{var1}" but angle is {var2} degrees. Maybe the restriction type is not appropriate?' + description: '{var1} has a restriction type "{var2}" but the angle is {var3} degrees. Maybe the restriction type is not appropriate?' 297: title: 'Wrong direction of to member' - description: 'Wrong direction of "to" way {var1}.' + description: '{var1} does not match the direction of "to" way {var2}.' 298: title: 'Redundant restriction - oneway' - description: 'Entry already prohibited by "oneway" tag on {var1}.' + description: '{var1} may be redundant. Entry already prohibited by "oneway" tag on {var2}.' 300: title: 'Missing maxspeed' - description: 'This road is missing a "maxspeed" tag and is tagged as motorway, trunk, primary, or secondary.' + description: '{var1} is missing a "maxspeed" tag and is tagged as motorway, trunk, primary, or secondary.' 310: title: 'Roundabout issue' description: 'There is an unspecified issue with this roundabout.' 311: title: 'Roundabout not closed loop' - description: 'This way is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout).' + description: '{var1} is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout).' 312: title: 'Roundabout wrong direction' - description: 'If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around.' + description: 'If {var1} is in a country with {var2} traffic then its orientation goes the wrong way around.' 313: title: 'Roundabout weakly connected' - description: 'This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more.' + description: '{var1} has only {var2} other road(s) connected. Roundabouts typically have 3 or more.' 320: title: 'Improper link connection' - description: 'This way is tagged as "{var1}" but doesn''t have a connection to any other "{var2}" or "{var3}".' + description: '{var1} is tagged as "{var2}" but doesn''t have a connection to any other "{var3}" or "{var4}".' 350: title: 'Improper bridge tag' - description: 'This bridge doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}.' + description: '{var1} doesn''t have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var2}.' 360: title: 'Missing local name tag' - description: 'It would be nice if this {var1} had a local name tag "name:XX={var2}" where XX shows the language of its common name "{var2}".' + description: 'It would be nice if {var1} had a local name tag "name:XX={var2}" where XX shows the language of its common name "{var2}".' 370: title: 'Doubled places' - description: 'This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant.' + description: '{var1} has tags in common with the surrounding way {var2} {var3} and seems to be redundant.' including_the_name: "(including the name {name})" 380: title: 'Non-physical use of sport tag' - description: 'This way is tagged "{var1}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway").' + description: '{var1} is tagged "{var2}" but has no physical tag (e.g. "leisure", "building", "amenity", or "highway").' 390: title: 'Missing tracktype' - description: This track doesn't have a "tracktype" tag. + description: '{var1} doesn''t have a "tracktype" tag.' 400: title: 'Geometry issue' description: 'There is an unspecified issue with the geometry here.' @@ -875,7 +886,7 @@ en: description: 'Ways {var1} and {var2} join in a very sharp angle here and there is no oneway tag or turn restriction that prevents turning.' 402: title: 'Impossible angle' - description: 'This way bends in a very sharp angle here.' + description: '{var1} bends in a very sharp angle here.' 410: title: 'Website issue' description: 'There is an unspecified issue with a contact website or URL.' diff --git a/data/keepRight.json b/data/keepRight.json index 52b47b638..519a2f24d 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -1,17 +1,26 @@ { "localizeStrings": { - "node": "node", - "way": "way", - "relation": "relation", - "highway": "highway", - "railway": "railway", - "waterway": "waterway", - "cycleway": "cycleway", - "footpath": "footpath", - "'cycleway/footpath": "cycleway_footpath", - "riverbank": "riverbank", - "bridge": "bridge", - "tunnel": "tunnel", + "this node": "node", + "this way": "way", + "this relation": "relation", + "this one-way": "oneway", + "this highway": "highway", + "this railway": "railway", + "this waterway": "waterway", + "this cycleway": "cycleway", + "this footpath": "footpath", + "this cycleway/footpath": "cycleway_footpath", + "this riverbank": "riverbank", + "this crossing": "crossing", + "this railway crossing": "railway_crossing", + "this bridge": "bridge", + "this tunnel": "tunnel", + "this boundary": "boundary", + "this turn-restriction": "turn_restriction", + "this roundabout": "roundabout", + "this mini-roundabout": "mini_roundabout", + "this track": "track", + "this feature": "feature", "place_of_worship": "place_of_worship", "pub": "pub", "restaurant": "restaurant", @@ -28,7 +37,9 @@ "fast_food": "fast_food", "fuel": "fuel", "from": "from", - "to": "to" + "to": "to", + "left-hand": "left_hand", + "right-hand": "right_hand" }, "errorTypes": { "20": { @@ -42,137 +53,167 @@ "title": "non-closed_areas", "severity": "error", "description": "This way is tagged with '$1' and should be closed-loop.", - "regex": "'(.+)'" + "IDs": ["this", ""], + "regex": "(this way) is tagged with '(.+)'" }, "40": { "title": "dead-ended one-ways", "severity": "error", "description": "The first node (id $1) of this one-way is not connected to any other way", - "IDs": ["n"], - "regex": "\\(id (\\d+)\\)" + "IDs": ["n", "this"], + "regex": "\\(id (\\d+)\\) of (this one-way)" }, "41": { "title": "", "severity": "error", "description": "The last node (id $1) of this one-way is not connected to any other way", - "IDs": ["n"], - "regex": "\\(id (\\d+)\\)" + "IDs": ["n", "this"], + "regex": "\\(id (\\d+)\\) of (this one-way)" }, "42": { "title": "", "severity": "error", - "description": "This node cannot be reached because one-ways only lead away from here" + "description": "This node cannot be reached because one-ways only lead away from here", + "IDs": ["this"], + "regex": "(this node)" }, "43": { "title": "", "severity": "error", - "description": "You cannot escape from this node because one-ways only lead to here" + "description": "You cannot escape from this node because one-ways only lead to here", + "IDs": ["this"], + "regex": "(this node)" }, "50": { "title": "almost-junctions", "severity": "error", "description": "This node is very close but not connected to way #$1", - "IDs": ["w"], - "regex": "way #(\\d+)" + "IDs": ["this", "w"], + "regex": "(this node) is very close but not connected to way #(\\d+)" }, "60": { "title": "deprecated tags", "severity": "warning", "description": "This $1 uses deprecated tag $2. Please use $3 instead!", - "regex": "This (node|way|relation) uses deprecated tag '(.+)'\\. Please use "(.+)"" + "IDs": ["this", "", ""], + "regex": "(this (?:node|way|relation)) uses deprecated tag '(.+)'\\. Please use "(.+)"" }, "70": { "title": "missing tags", "severity": "error", - "description": "" + "description": "This $1 has an empty tag: $2", + "IDs": ["this", ""], + "regex": "(this (?:node|way|relation)) has an empty tag: "(.+)="" }, "71": { "title": "", "severity": "error", - "description": "This way has no tags" + "description": "This way has no tags", + "IDs": ["this"], + "regex": "(this way)" }, "72": { "title": "", "severity": "error", - "description": "This node is not member of any way and does not have any tags" + "description": "This node is not member of any way and does not have any tags", + "IDs": ["this"], + "regex": "(this node)" }, "73": { "title": "", "severity": "error", "description": "This way has a $1 tag but no highway tag", - "regex": "has a (.+) tag" + "IDs": ["this", ""], + "regex": "(this way) has a (.+) tag" }, "74": { "title": "missing tags", "severity": "error", "description": "This $1 has an empty tag: $2", - "regex": "This (node|way|relation) has an empty tag: "(.+)="" + "IDs": ["this", ""], + "regex": "(this (?:node|way|relation)) has an empty tag: "(.+)="" }, "75": { "description": "This (node|way|relation) has a name \\((.+)\\) but no other tag", - "regex": "This (node|way|relation) has a name \\((.+)\\)" + "IDs": ["this", ""], + "regex": "(this (?:node|way|relation)) has a name \\((.+)\\)" }, "90": { "title": "motorways without ref", "severity": "error", - "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag" + "description": "This way is tagged as motorway and therefore needs a ref nat_ref or int_ref tag", + "IDs": ["this"], + "regex": "(this way)" }, "100": { "title": "places of worship without religion", "severity": "error", "description": "This $1 is tagged as place of worship and therefore needs a religion tag", - "regex": "This (node|way|relation) is" + "IDs": ["this"], + "regex": "(this (?:node|way|relation))" }, "110": { "title": "point of interest without name", "severity": "error", "description": "This node is tagged as $1 and therefore needs a name tag", - "regex": "as (.+) and" + "IDs": ["this", ""], + "regex": "(this (?:node|way|relation)) is tagged as (.+) and" }, "120": { "title": "ways without nodes", "severity": "error", - "description": "This way has just one single node" + "description": "This way has just one single node", + "IDs": ["this"], + "regex": "(this way)" }, "130": { "title": "floating islands", "severity": "error", - "description": "This way is not connected to the rest of the map" + "description": "This way is not connected to the rest of the map", + "IDs": ["this"], + "regex": "(this way)" }, "150": { "title": "railway crossing without tag", "severity": "error", - "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing" + "description": "This crossing of a highway and a railway needs to be tagged as railway=crossing or railway=level_crossing", + "IDs": ["this"], + "regex": "(this crossing)" }, "160": { "title": "wrongly used railway tag", "severity": "error", - "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing" + "description": "There are ways in different layers coming together in this railway crossing. There are ways tagged as tunnel or bridge coming together in this railway crossing", + "IDs": ["this"], + "regex": "(this railway crossing)" }, "170": { "title": "FIXME tagged items", "severity": "error", - "description": "(.*)", - "regex": "(.*)" + "description": "This feature has a FIXME tag: (.*)", + "IDs": ["this"], + "regex": "(this feature) has a FIXME tag: (.*)" }, "180": { "title": "relations without type", "severity": "error", - "description": "This relation has no type tag which is mandatory for relations" + "description": "This relation has no type tag which is mandatory for relations", + "IDs": ["this"], + "regex": "(this relation)" }, "190": { "title": "intersections without junctions", "severity": "error", "description": "This $1 intersects the $2 #$3 but there is no junction node", - "IDs": ["", "", "w"], - "regex": "This (.+) intersects the (.+) #(\\d+)" + "IDs": ["this", "", "w"], + "regex": "(this .+) intersects the (.+) #(\\d+)" }, "200": { "title": "overlapping ways", "severity": "error", - "IDs": ["", "","w"], "description": "This $1 overlaps the $2 #$3", - "regex": "This (.+) overlaps the (.+) #(\\d+)" + "IDs": ["this", "", "w"], + "regex": "(this .+) overlaps the (.+) #(\\d+)" }, "210": { "title": "loopings", @@ -183,48 +224,55 @@ "title": "", "severity": "error", "description": "This way contains more than one node at least twice. Nodes are $1.", - "IDs": ["211"], - "regex": "Nodes are ((?:#\\d+(?:, )?)+)\\." + "IDs": ["this", "211"], + "regex": "(this way) contains more than one node at least twice. Nodes are ((?:#\\d+(?:, )?)+)\\." }, "212": { "title": "", "severity": "error", - "description": "This way has only two different nodes and contains one of them more than once" + "description": "This way has only two different nodes and contains one of them more than once", + "IDs": ["this"], + "regex": "(this way)" }, "220": { "title": "misspelled tags", "severity": "error", "description": "This $1 is tagged '$2' where $3 looks like $4", - "regex": "This (node|way|relation) is tagged '(.+)' where "(.+)" looks like "(.+)"" + "regex": "(this (?:node|way|relation)) is tagged '(.+)' where "(.+)" looks like "(.+)"" }, "221": { "title": "", "severity": "error", "description": "The key of this $1's tag is 'key': $2", - "regex": "this (node|way|relation)\\'s tag is \\'key\\': (.+)" + "regex": "(this (?:node|way|relation))\\'s tag is \\'key\\': (.+)" }, "230": { "title": "layer conflicts", "severity": "error", - "description": "Connected ways should be on the same layer. Crossings on intermediate nodes of ways on different layers are obviously wrong. Junctions on end-nodes of ways on different layers are also deprecated, but common practice. So you may ignore this part of the check and switch them off separately. Please note that bridges are set to layer +1, and tunnels to -1, anything else to layer 0 implicitly if no layer tag is present." + "description": "This node is a junction of ways on different layers.", + "IDs": ["this"], + "regex": "(this node)" }, "231": { "title": "mixed layers intersection", "severity": "error", "description": "This node is a junction of ways on different layers: $1", - "IDs": ["231"], - "regex": "layers: (.+)" + "IDs": ["this", "231"], + "regex": "(this node) is a junction of ways on different layers: (.+)" }, "232": { "title": "strange layers", "severity": "error", "description": "This $1 is tagged with layer $2. This need not be an error, but it looks strange", - "regex": "This (bridge|tunnel) is tagged with layer (-?\\d+)\\." + "IDs": ["this", ""], + "regex": "(this (?:bridge|tunnel)) is tagged with layer (-?\\d+)\\." }, "270": { "title": "motorways connected directly", "severity": "error", - "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle." + "description": "This node is a junction of a motorway and a highway other than motorway, motorway_link, trunk, rest_area or construction. Service or unclassified is only valid if it has access=no/private or it leads to a motorway service area or if it is a service=parking_aisle.", + "IDs": ["this"], + "regex": "(this node)" }, "280": { "title": "boundaries", @@ -234,7 +282,9 @@ "281": { "title": "missing name", "severity": "error", - "description": "This boundary has no name" + "description": "This boundary has no name", + "IDs": ["this"], + "regex": "(this boundary)" }, "282": { "title": "missing admin level", @@ -258,7 +308,8 @@ "title": "admin_level too high", "severity": "error", "description": "This boundary-way has admin_level $1 but belongs to a relation with lower admin_level (higher priority); it should have the lowest admin_level of all relations", - "regex": "admin_level (-?\\d+) but" + "IDs": ["this", ""], + "regex": "(this boundary)-way has admin_level (-?\\d+) but" }, "290": { "title": "restrictions", @@ -269,58 +320,64 @@ "title": "missing type", "severity": "error", "description": "This turn-restriction has no (?:known )?restriction type", - "regex": "This turn-restriction has no (?:known )?restriction type" + "IDs": ["this"], + "regex": "(this turn-restriction) has" }, "292": { "title": "missing from way", "severity": "error", - "description": "A turn-restriction needs exactly one from member. This one has $1", - "regex": "has (\\d+)" + "description": "This turn-restriction needs exactly one from member. This one has $1", + "IDs": ["this", ""], + "regex": "(this turn-restriction) needs.+has (\\d+)" }, "293": { "title": "missing to way", "severity": "error", - "description": "A turn-restriction needs exactly one to member. This one has $1", - "regex": "has (\\d+)" + "description": "This turn-restriction needs exactly one to member. This one has $1", + "IDs": ["this", ""], + "regex": "(this turn-restriction) needs.+has (\\d+)" }, "294": { "title": "from or to not a way", "severity": "error", - "description": "From- and To-members of turn restrictions need to be ways. $1", - "IDs": ["294"], - "regex": "ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)" + "description": "From- and To-members of this turn-restriction need to be ways. $1", + "IDs": ["this", "294"], + "regex": "(this turn-restriction)~.+ways\\. ((?:(?:from|to) (?:node|relation) #\\d+,?)+)" }, "295": { "title": "via is not on the way ends", "severity": "error", "description": "via (node #$1) is not the first or the last member of (from|to) (way #$3)", - "IDs": ["n", "", "w"], - "regex": "via \\(node #(\\d+)\\) is not the first or the last member of (from|to) \\(way #(\\d+)\\)" + "IDs": ["this", "n", "", "w"], + "regex": "(this turn-restriction)~via \\(node #(\\d+)\\).+ of (from|to) \\(way #(\\d+)\\)" }, "296": { "title": "wrong restriction angle", "severity": "error", - "description": "restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?", - "regex": "is (\\w+), but angle is (-?\\d+) degrees" + "description": "This turn-restriction type is $1, but angle is $2 degrees. Maybe the restriction type is not appropriate?", + "IDs": ["this", "", ""], + "regex": "(this turn-restriction)~.+is (\\w+), but angle is (-?\\d+)" }, "297": { "title": "wrong direction of to member", "severity": "error", "description": "wrong direction of to way $1", - "IDs": ["w"], - "regex": "way (\\d+)" + "IDs": ["this", "w"], + "regex": "(this turn-restriction)~.+to way (\\d+)" }, "298": { "title": "already restricted by oneway", "severity": "error", "description": "entry already prohibited by oneway tag on $1", - "IDs": ["w"], - "regex": "on (\\d+)" + "IDs": ["this", "w"], + "regex": "(this turn-restriction)~.+tag on (\\d+)" }, "300": { "title": "missing maxspeed", "severity": "warning", - "description": "missing maxspeed tag" + "description": "This highway is missing a maxspeed tag", + "IDs": ["this"], + "regex": "(this highway)" }, "310": { "title": "roundabouts", @@ -330,56 +387,66 @@ "311": { "title": "not closed loop", "severity": "error", - "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)" + "description": "This way is part of a roundabout but is not closed-loop. (split carriageways approaching a roundabout should not be tagged as roundabout)", + "IDs": ["this"], + "regex": "(this way)" }, "312": { "title": "wrong direction", "severity": "error", "description": "If this ((?:mini_)?roundabout) is in a country with (left|right)-hand traffic then its orientation goes the wrong way around", - "regex": "this ((?:mini_)?roundabout) is in a country with (left|right)-hand" + "IDs": ["this", ""], + "regex": "(this (?:mini_)?roundabout) is in a country with ((?:left|right)-hand)" }, "313": { "title": "faintly connected", "severity": "error", "description": "This roundabout has only $1 other roads connected. Roundabouts typically have three", - "regex": "only (\\d) other" + "IDs": ["this", ""], + "regex": "(this roundabout) has only (\\d) other" }, "320": { "title": "*_link connections", "severity": "error", "description": "This way is tagged as highway=$1_link but doesn't have a connection to any other $1 or $1_link", - "regex": "(highway=.+) but doesn't have a connection to any other (.+) or (.+)" + "IDs": ["this", "", "", "", ""], + "regex": "(this way) is tagged as (highway=.+) but doesn't have a connection to any other (.+) or (.+)" }, "350": { "title": "bridge-tags", "severity": "error", "description": "This bridge does not have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: (.+)", "NOTE": "Group can be arbitrary list of form: key=value,key=value,key=value...", - "regex": "these tags: (.+)" + "IDs": ["this", ""], + "regex": "(this bridge).*tags: (.+)" }, "360": { "title": "language unknown", "severity": "warning", "description": "It would be nice if this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'", - "regex": "this (node|way|relation) had an additional tag 'name:XX=(.+)' where XX shows the language of its name '\\2'" + "IDs": ["this", ""], + "regex": "(this (?:node|way|relation)) had an additional tag 'name:XX=(.+)' where" }, "370": { "title": "doubled places", "severity": "error", "description": "This node has tags in common with the surrounding way #$1 ((?:\\(including the name '.+'\\) )?)and seems to be redundand", - "IDs": ["w","370"], - "regex": "way #(\\d+) ((?:\\(including the name '.+'\\) )?)and" + "IDs": ["this", "w", "370"], + "regex": "(this node) has tags in common with the surrounding way #(\\d+) ((?:\\(including the name '.+'\\) )?)and" }, "380": { "title": "non-physical use of sport-tag", "severity": "error", "description": "This way is tagged sport=$1 but has no physical tag like e.g. leisure, building, amenity or highway", - "regex": "(sport=.+) but" + "IDs": ["this", ""], + "regex": "(this way) is tagged (sport=.+) but" }, "390": { "title": "missing tracktype", "severity": "warning", - "description": "This track doesn''t have a tracktype" + "description": "This track doesn''t have a tracktype", + "IDs": ["this"], + "regex": "(this track)" }, "400": { "title": "geometry glitches", @@ -396,7 +463,9 @@ "402": { "title": "impossible angles", "severity": "error", - "description": "this way bends in a very sharp angle here" + "description": "this way bends in a very sharp angle here", + "IDs": ["this"], + "regex": "(this way)" }, "410": { "title": "website", diff --git a/dist/locales/en.json b/dist/locales/en.json index 405536eef..846ba412d 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -810,17 +810,26 @@ "close_comment": "Close and Comment", "ignore_comment": "Ignore and Comment", "error_parts": { - "node": "node", - "way": "way", - "relation": "relation", - "highway": "highway", - "railway": "railway", - "waterway": "waterway", - "cycleway": "cycleway", - "cycleway_footpath": "cycleway/footpath", - "riverbank": "riverbank", - "bridge": "bridge", - "tunnel": "tunnel", + "node": "this node", + "way": "this way", + "relation": "this relation", + "oneway": "this oneway", + "highway": "this highway", + "railway": "this railway", + "waterway": "this waterway", + "cycleway": "this cycleway", + "cycleway_footpath": "this cycleway/footpath", + "riverbank": "this riverbank", + "crossing": "this crossing", + "railway_crossing": "this railway crossing", + "bridge": "this bridge", + "tunnel": "this tunnel", + "boundary": "this boundary", + "turn_restriction": "this turn restriction", + "roundabout": "this roundabout", + "mini_roundabout": "this mini-roundabout", + "track": "this track", + "feature": "this feature", "place_of_worship": "place of worship", "pub": "pub", "restaurant": "restaurant", @@ -837,7 +846,9 @@ "fast_food": "fast food", "fuel": "fuel", "from": "from", - "to": "to" + "to": "to", + "left_hand": "left-hand", + "right_hand": "right-hand" }, "errorTypes": { "20": { @@ -846,123 +857,123 @@ }, "30": { "title": "Non-closed area", - "description": "This way is tagged with \"{var1}\" and should be a closed loop." + "description": "{var1} is tagged with \"{var2}\" and should be a closed loop." }, "40": { "title": "Impossible oneway", - "description": "The first node {var1} of this oneway is not connected to any other way." + "description": "The first node {var1} of {var2} is not connected to any other way." }, "41": { - "description": "The last node {var1} of this oneway is not connected to any other way." + "description": "The last node {var1} of {var2} is not connected to any other way." }, "42": { - "description": "You cannot reach this node because all ways leading from it are oneway." + "description": "You cannot reach {var1} because all ways leading from it are oneway." }, "43": { - "description": "You cannot escape from this node because all ways leading to it are oneway." + "description": "You cannot escape from {var1} because all ways leading to it are oneway." }, "50": { "title": "Almost junction", - "description": "This node is very close but not connected to way {var1}." + "description": "{var1} is very close but not connected to way {var2}." }, "60": { "title": "Deprecated tag", - "description": "This {var1} uses deprecated tag \"{var2}\". Please use \"{var3}\" instead." + "description": "{var1} uses deprecated tag \"{var2}\". Please use \"{var3}\" instead." }, "70": { "title": "Missing tag", - "description": "This {var1} has an empty tag: \"{var2}\"." + "description": "{var1} has an empty tag: \"{var2}\"." }, "71": { - "description": "This way has no tags." + "description": "{var1} has no tags." }, "72": { - "description": "This node is not member of any way and doesn't have any tags." + "description": "{var1} is not member of any way and doesn't have any tags." }, "73": { - "description": "This way has a \"{var1}\" tag but no \"highway\" tag." + "description": "{var1} has a \"{var2}\" tag but no \"highway\" tag." }, "74": { - "description": "This {var1} has an empty tag: \"{var2}\"." + "description": "{var1} has an empty tag: \"{var2}\"." }, "75": { - "description": "This {var1} has a name \"{var2}\" but no other tags." + "description": "{var1} has a name \"{var2}\" but no other tags." }, "90": { "title": "Motorway without ref tag", - "description": "This way is tagged as a motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag." + "description": "{var1} is tagged as a motorway and therefore needs a \"ref\", \"nat_ref\", or \"int_ref\" tag." }, "100": { "title": "Place of worship without religion", - "description": "This {var1} is tagged as a place of worship and therefore needs a religion tag." + "description": "{var1} is tagged as a place of worship and therefore needs a religion tag." }, "110": { "title": "Point of interest without name", - "description": "This node is tagged as a \"{var1}\" and therefore needs a name tag." + "description": "{var1} is tagged as a \"{var2}\" and therefore needs a name tag." }, "120": { "title": "Way without nodes", - "description": "This way has just one single node." + "description": "{var1} has just one single node." }, "130": { "title": "Disconnected way", - "description": "This way is not connected to the rest of the map." + "description": "{var1} is not connected to the rest of the map." }, "150": { "title": "Railway crossing without tag", - "description": "This crossing of a highway and a railway needs to be tagged as \"railway=crossing\" or \"railway=level_crossing\"." + "description": "{var1} of a highway and a railway needs to be tagged as \"railway=crossing\" or \"railway=level_crossing\"." }, "160": { "title": "Railway layer conflict", - "description": "There are ways in different layers (e.g. tunnel or bridge) meeting at this railway crossing." + "description": "There are ways in different layers (e.g. tunnel or bridge) meeting at {var1}." }, "170": { "title": "FIXME tagged item", - "description": "{var1}" + "description": "{var1} has a FIXME tag: {var2}" }, "180": { "title": "Relation without type", - "description": "This relation is missing a \"type\" tag." + "description": "{var1} is missing a \"type\" tag." }, "190": { "title": "Intersection without junction", - "description": "This {var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel." + "description": "{var1} intersects the {var2} {var3} but there is no junction node, bridge, or tunnel." }, "200": { "title": "Overlapping ways", - "description": "This {var1} overlaps the {var2} {var3}." + "description": "{var1} overlaps the {var2} {var3}." }, "210": { "title": "Self-intersecting way", "description": "There is an unspecified issue with self intersecting ways." }, "211": { - "description": "This way contains more than one node multiple times. Nodes are {var1}. This may or may not be an error." + "description": "{var1} contains more than one node multiple times. Nodes are {var2}. This may or may not be an error." }, "212": { - "description": "This way has only two different nodes and contains one of them more than once." + "description": "{var1} has only two different nodes and contains one of them more than once." }, "220": { "title": "Misspelled tag", - "description": "This {var1} is tagged \"{var2}\" where \"{var3}\" looks like \"{var4}\"." + "description": "{var1} is tagged \"{var2}\" where \"{var3}\" looks like \"{var4}\"." }, "221": { - "description": "This {var1} has a suspicious tag \"{var2}\"." + "description": "{var1} has a suspicious tag \"{var2}\"." }, "230": { "title": "Layer conflict", - "description": "This node is a junction of ways on different layers." + "description": "{var1} is a junction of ways on different layers." }, "231": { - "description": "This node is a junction of ways on different layers: {var1}.", + "description": "{var1} is a junction of ways on different layers: {var2}.", "layer": "(layer: {layer})" }, "232": { - "description": "This {var1} is tagged with \"layer={var2}\". This need not be an error but it looks strange." + "description": "{var1} is tagged with \"layer={var2}\". This need not be an error but it looks strange." }, "270": { "title": "Unusual motorway connection", - "description": "This node is a junction of a motorway and a highway other than \"motorway\", \"motorway_link\", \"trunk\", \"rest_area\", or \"construction\". Connection to \"service\" or \"unclassified\" is only valid if it has \"access=no/private\", or it leads to a motorway service area, or if it is a \"service=parking_aisle\"." + "description": "{var1} is a junction of a motorway and a highway other than \"motorway\", \"motorway_link\", \"trunk\", \"rest_area\", or \"construction\". Connection to \"service\" or \"unclassified\" is only valid if it has \"access=no/private\", or it leads to a motorway service area, or if it is a \"service=parking_aisle\"." }, "280": { "title": "Boundary issue", @@ -970,7 +981,7 @@ }, "281": { "title": "Boundary missing name", - "description": "This boundary has no name." + "description": "{var1} has no name." }, "282": { "title": "Boundary missing admin level", @@ -986,7 +997,7 @@ }, "285": { "title": "Boundary admin_level too high", - "description": "This boundary way has \"admin_level={var1}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations." + "description": "{var1} has \"admin_level={var2}\" but belongs to a relation with lower \"admin_level\" (e.g. higher priority); it should have the lowest \"admin_level\" of all relations." }, "290": { "title": "Restriction issue", @@ -994,39 +1005,39 @@ }, "291": { "title": "Restriction missing type", - "description": "This turn restriction has an unrecognized restriction type." + "description": "{var1} has an unrecognized restriction type." }, "292": { "title": "Restriction missing \"from\" way", - "description": "A turn restriction needs exactly one \"from\" member. This one has {var1}." + "description": "{var1} has {var2} \"from\" members, but it should have 1." }, "293": { "title": "Restriction missing \"to\" way", - "description": "A turn restriction needs exactly one \"to\" member. This one has {var1}." + "description": "{var1} has {var2} \"to\" members, but it should have 1." }, "294": { "title": "Restriction \"from\" or \"to\" is not a way", - "description": "\"from\" and \"to\" members of turn restrictions need to be ways. {var1}." + "description": "{var1} has \"from\" or \"to\" members which should be ways. {var2}." }, "295": { "title": "Restriction \"via\" is not an endpoint", - "description": "\"via\" (node {var1}) is not the first or the last member of \"{var2}\" (way {var3})." + "description": "{var1} has a \"via\" (node {var2}) which is not the first or the last member of \"{var3}\" (way {var4})." }, "296": { "title": "Unusual restriction angle", - "description": "Restriction type is \"{var1}\" but angle is {var2} degrees. Maybe the restriction type is not appropriate?" + "description": "{var1} has a restriction type \"{var2}\" but the angle is {var3} degrees. Maybe the restriction type is not appropriate?" }, "297": { "title": "Wrong direction of to member", - "description": "Wrong direction of \"to\" way {var1}." + "description": "{var1} does not match the direction of \"to\" way {var2}." }, "298": { "title": "Redundant restriction - oneway", - "description": "Entry already prohibited by \"oneway\" tag on {var1}." + "description": "{var1} may be redundant. Entry already prohibited by \"oneway\" tag on {var2}." }, "300": { "title": "Missing maxspeed", - "description": "This road is missing a \"maxspeed\" tag and is tagged as motorway, trunk, primary, or secondary." + "description": "{var1} is missing a \"maxspeed\" tag and is tagged as motorway, trunk, primary, or secondary." }, "310": { "title": "Roundabout issue", @@ -1034,40 +1045,40 @@ }, "311": { "title": "Roundabout not closed loop", - "description": "This way is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout)." + "description": "{var1} is part of a roundabout but is not closed-loop. (Split carriageways approaching a roundabout should not be tagged as roundabout)." }, "312": { "title": "Roundabout wrong direction", - "description": "If this {var1} is in a country with {var2}-hand traffic then its orientation goes the wrong way around." + "description": "If {var1} is in a country with {var2} traffic then its orientation goes the wrong way around." }, "313": { "title": "Roundabout weakly connected", - "description": "This roundabout has only {var1} other road(s) connected. Roundabouts typically have 3 or more." + "description": "{var1} has only {var2} other road(s) connected. Roundabouts typically have 3 or more." }, "320": { "title": "Improper link connection", - "description": "This way is tagged as \"{var1}\" but doesn't have a connection to any other \"{var2}\" or \"{var3}\"." + "description": "{var1} is tagged as \"{var2}\" but doesn't have a connection to any other \"{var3}\" or \"{var4}\"." }, "350": { "title": "Improper bridge tag", - "description": "This bridge doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var1}." + "description": "{var1} doesn't have a tag in common with its surrounding ways that shows the purpose of this bridge. There should be one of these tags: {var2}." }, "360": { "title": "Missing local name tag", - "description": "It would be nice if this {var1} had a local name tag \"name:XX={var2}\" where XX shows the language of its common name \"{var2}\"." + "description": "It would be nice if {var1} had a local name tag \"name:XX={var2}\" where XX shows the language of its common name \"{var2}\"." }, "370": { "title": "Doubled places", - "description": "This node has tags in common with the surrounding way {var1} {var2} and seems to be redundant.", + "description": "{var1} has tags in common with the surrounding way {var2} {var3} and seems to be redundant.", "including_the_name": "(including the name {name})" }, "380": { "title": "Non-physical use of sport tag", - "description": "This way is tagged \"{var1}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\")." + "description": "{var1} is tagged \"{var2}\" but has no physical tag (e.g. \"leisure\", \"building\", \"amenity\", or \"highway\")." }, "390": { "title": "Missing tracktype", - "description": "This track doesn't have a \"tracktype\" tag." + "description": "{var1} doesn't have a \"tracktype\" tag." }, "400": { "title": "Geometry issue", @@ -1079,7 +1090,7 @@ }, "402": { "title": "Impossible angle", - "description": "This way bends in a very sharp angle here." + "description": "{var1} bends in a very sharp angle here." }, "410": { "title": "Website issue", diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index f25bd1494..4b5036c5f 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -109,8 +109,11 @@ function tokenReplacements(d) { capture = parseError(capture, idType); } else if (htmlRegex.test(capture)) { // escape any html in non-IDs capture = '\\' + capture + '\\'; - } else if (localizeStrings[capture]) { // some replacement strings can be localized - capture = t('QA.keepRight.error_parts.' + localizeStrings[capture]); + } else { + var compare = capture.toLowerCase(); + if (localizeStrings[compare]) { // some replacement strings can be localized + capture = t('QA.keepRight.error_parts.' + localizeStrings[compare]); + } } replacements['var' + i] = capture; @@ -121,9 +124,18 @@ function tokenReplacements(d) { function parseError(capture, idType) { + var compare = capture.toLowerCase(); + if (localizeStrings[compare]) { // some replacement strings can be localized + capture = t('QA.keepRight.error_parts.' + localizeStrings[compare]); + } switch (idType) { - // simple case just needs a linking span + // link a string like "this node" + case 'this': + capture = linkErrorObject(capture); + break; + + // link an entity ID case 'n': case 'w': case 'r': @@ -151,6 +163,10 @@ function parseError(capture, idType) { return capture; + function linkErrorObject(d) { + return '' + d + ''; + } + function linkEntity(d) { return '' + d + ''; } @@ -305,6 +321,28 @@ export default { // try to handle error type directly, fallback to parent error type. var whichType = errorTemplate ? errorType : parentErrorType; + // Rewrite a few of the errors at this point.. + // This is done to make them easier to linkify and translate. + switch (whichType) { + case '170': + props.description = 'This feature has a FIXME tag: ' + props.description; + break; + case '292': + case '293': + props.description = props.description.replace('A turn-', 'This turn-'); + break; + case '294': + case '295': + case '296': + case '297': + case '298': + props.description = 'This turn-restriction~' + props.description; + break; + case '300': + props.description = 'This highway is missing a maxspeed tag'; + break; + } + // - move markers slightly so it doesn't obscure the geometry, // - then move markers away from other coincident markers var coincident = false; diff --git a/modules/ui/keepRight_details.js b/modules/ui/keepRight_details.js index 38047a9ad..ee164ac58 100644 --- a/modules/ui/keepRight_details.js +++ b/modules/ui/keepRight_details.js @@ -6,7 +6,7 @@ import { import { dataEn } from '../../data'; import { modeSelect } from '../modes'; import { t } from '../util/locale'; -import { utilDisplayName, utilEntityOrMemberSelector } from '../util'; +import { utilDisplayName, utilEntityOrMemberSelector, utilEntityRoot } from '../util'; export function uiKeepRightDetails(context) { @@ -31,7 +31,8 @@ export function uiKeepRightDetails(context) { } else { detail = unknown; } - return detail.substr(0, 1).toUpperCase() + detail.substr(1); + + return detail; } @@ -65,10 +66,13 @@ export function uiKeepRightDetails(context) { .html(errorDetail); // If there are entity links in the error message.. - descriptionEnter.selectAll('.kr_error_entity_link') + descriptionEnter.selectAll('.kr_error_entity_link, .kr_error_object_link') .each(function() { var link = d3_select(this); - var entityID = this.innerText; + var isObjectLink = link.classed('kr_error_object_link'); + var entityID = isObjectLink ? + (utilEntityRoot(_error.object_type) + _error.object_id) + : this.textContent; var entity = context.hasEntity(entityID); // Add click handler @@ -87,15 +91,24 @@ export function uiKeepRightDetails(context) { if (!osmlayer.enabled()) { osmlayer.enabled(true); } + context.map().centerZoom(_error.loc, 20); - context.enter(modeSelect(context, [entityID])); + + if (entity) { + context.enter(modeSelect(context, [entityID])); + } else { + context.loadEntity(entityID, function() { + context.enter(modeSelect(context, [entityID])); + }); + } }); // Replace with friendly name if possible // (The entity may not yet be loaded into the graph) if (entity) { var name = utilDisplayName(entity); // try to use common name - if (!name) { + + if (!name && !isObjectLink) { var preset = context.presets().match(entity, context.graph()); name = preset && !preset.isFallback() && preset.name(); // fallback to preset name } From ae8dffa43ca32d2c7a80ecb8c060f74b1b2da1a2 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Jan 2019 21:35:13 -0500 Subject: [PATCH 102/114] Improve URL error messages 411, 412, 413 --- data/core.yaml | 6 +++--- data/keepRight.json | 15 +++++++++------ dist/locales/en.json | 6 +++--- modules/services/keepRight.js | 13 +++++++++++++ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/data/core.yaml b/data/core.yaml index 9f9d8b8e5..f7e7e49d8 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -891,11 +891,11 @@ en: title: 'Website issue' description: 'There is an unspecified issue with a contact website or URL.' 411: - description: 'The URL {var1} cannot be opened (HTTP status code {var2}).' + description: '{var1} may have an outdated URL: {var2} returned HTTP status code {var3}.' 412: - description: 'Possible domain squatting: The URL has suspicious text: "{var1}".' + description: '{var1} may have an outdated URL: {var2} contained suspicious text "{var3}".' 413: - description: 'Possible non-match. Content of the URL did not contain these keywords: ({var1}).' + description: '{var1} may have an outdated URL: {var2} did not contain keywords "{var3}".' streetside: tooltip: "Streetside photos from Microsoft" title: "Photo Overlay (Bing Streetside)" diff --git a/data/keepRight.json b/data/keepRight.json index 519a2f24d..52967eb7a 100644 --- a/data/keepRight.json +++ b/data/keepRight.json @@ -475,20 +475,23 @@ "411": { "title": "http error", "severity": "error", - "description": "The URL ($1) cannot be opened (HTTP status code $2)", - "regex": "href=(.+)>\\1\\) cannot be opened \\(HTTP status code (\\d+)\\)" + "description": "The URL ($1) cannot be opened (HTTP status code $2)", + "IDs": ["this", "url", ""], + "regex": "(this feature)~.+ href=([^>]+)>.+ code (\\d+)\\)" }, "412": { "title": "domain hijacking", "severity": "error", - "description": "Possible domain squatting: $1. Suspicious text is: \"$2\"", - "regex": "Possible domain squatting: \\1\\. Suspicious text is: "(.+)"" + "description": "Possible domain squatting: $1. Suspicious text is: \"$2\"", + "IDs": ["this", "url", ""], + "regex": "(this feature)~.+ href=([^>]+)>.+ is: "(.+)"" }, "413": { "title": "non-match", "severity": "error", - "description": "Content of the URL ($1) did not contain these keywords: ($2)", - "regex": "Content of the URL (\\1) did not contain these keywords: \\((.+)\\)" + "description": "Content of the URL ($1) did not contain these keywords: ($2)", + "IDs": ["this", "url", ""], + "regex": "(this feature)~.+ href=([^>]+)>.+ keywords: \\((.+)\\)" } } } diff --git a/dist/locales/en.json b/dist/locales/en.json index 846ba412d..0973332bd 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -1097,13 +1097,13 @@ "description": "There is an unspecified issue with a contact website or URL." }, "411": { - "description": "The URL {var1} cannot be opened (HTTP status code {var2})." + "description": "{var1} may have an outdated URL: {var2} returned HTTP status code {var3}." }, "412": { - "description": "Possible domain squatting: The URL has suspicious text: \"{var1}\"." + "description": "{var1} may have an outdated URL: {var2} contained suspicious text \"{var3}\"." }, "413": { - "description": "Possible non-match. Content of the URL did not contain these keywords: ({var1})." + "description": "{var1} may have an outdated URL: {var2} did not contain keywords \"{var3}\"." } } } diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 4b5036c5f..d0b3c3304 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -135,6 +135,10 @@ function parseError(capture, idType) { capture = linkErrorObject(capture); break; + case 'url': + capture = linkURL(capture); + break; + // link an entity ID case 'n': case 'w': @@ -171,6 +175,10 @@ function parseError(capture, idType) { return '' + d + ''; } + function linkURL(d) { + return '' + d + ''; + } + // arbitrary node list of form: #ID, #ID, #ID... function parse211(capture) { var newList = []; @@ -341,6 +349,11 @@ export default { case '300': props.description = 'This highway is missing a maxspeed tag'; break; + case '411': + case '412': + case '413': + props.description = 'This feature~' + props.description; + break; } // - move markers slightly so it doesn't obscure the geometry, From 623f96449eec5b9ecb68b7f3750012c59bcc2147 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Jan 2019 21:47:37 -0500 Subject: [PATCH 103/114] z-sort errors above warnings --- modules/services/keepRight.js | 2 ++ modules/svg/keepRight.js | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index d0b3c3304..8f141cf0e 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -328,6 +328,7 @@ export default { // try to handle error type directly, fallback to parent error type. var whichType = errorTemplate ? errorType : parentErrorType; + var whichTemplate = errorTypes[whichType]; // Rewrite a few of the errors at this point.. // This is done to make them easier to linkify and translate. @@ -376,6 +377,7 @@ export default { which_type: whichType, error_type: errorType, parent_error_type: parentErrorType, + severity: whichTemplate.severity || 'error', object_id: props.object_id, object_type: props.object_type, schema: props.schema, diff --git a/modules/svg/keepRight.js b/modules/svg/keepRight.js index a4ddd9e55..c4b8807ab 100644 --- a/modules/svg/keepRight.js +++ b/modules/svg/keepRight.js @@ -175,7 +175,11 @@ export function svgKeepRight(projection, context, dispatch) { function sortY(a, b) { - return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1]; + return (a.id === selectedID) ? 1 + : (b.id === selectedID) ? -1 + : (a.severity === 'error' && b.severity !== 'error') ? 1 + : (b.severity === 'error' && a.severity !== 'error') ? -1 + : b.loc[1] - a.loc[1]; } } From 5ebe0976fe4c00af42a423037a5b8a697f42eb2b Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 10 Jan 2019 23:27:55 -0500 Subject: [PATCH 104/114] Add `closed:keepright` changeset tag for closed issues The issues are written as `schema:error_id` and semicolon delimited e.g. `closed:keepright=56:102661142` closes this issue: https://www.keepright.at/report_map.php?schema=56&error=102661142 --- modules/services/keepRight.js | 31 +++++++++++++++++++++++++------ modules/ui/commit.js | 17 ++++++++++++++--- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/modules/services/keepRight.js b/modules/services/keepRight.js index 8f141cf0e..cf3b70ef9 100644 --- a/modules/services/keepRight.js +++ b/modules/services/keepRight.js @@ -281,7 +281,13 @@ export default { if (_krCache) { _forEach(_krCache.inflight, abortRequest); } - _krCache = { loaded: {}, inflight: {}, keepRight: {}, rtree: rbush() }; + _krCache = { + data: {}, + loaded: {}, + inflight: {}, + closed: {}, + rtree: rbush() + }; }, @@ -386,7 +392,7 @@ export default { d.replacements = tokenReplacements(d); - _krCache.keepRight[d.id] = d; + _krCache.data[d.id] = d; _krCache.rtree.insert(encodeErrorRtree(d)); }); @@ -418,8 +424,14 @@ export default { _krCache.inflight[d.id] = d3_request(url) .post(function(err) { delete _krCache.inflight[d.id]; - if (d.state === 'ignore' || d.state === 'ignore_t') { + + if (d.state === 'ignore') { // ignore permanently (false positive) that.removeError(d); + + } else if (d.state === 'ignore_t') { // ignore temporarily (error fixed) + that.removeError(d); + _krCache.closed[d.schema + ':' + d.error_id] = true; + } else { d = that.replaceError(d.update({ comment: d.newComment, @@ -449,7 +461,7 @@ export default { // get a single error from the cache getError: function(id) { - return _krCache.keepRight[id]; + return _krCache.data[id]; }, @@ -457,7 +469,7 @@ export default { replaceError: function(error) { if (!(error instanceof krError) || !error.id) return; - _krCache.keepRight[error.id] = error; + _krCache.data[error.id] = error; updateRtree(encodeErrorRtree(error), true); // true = replace return error; }, @@ -467,13 +479,20 @@ export default { removeError: function(error) { if (!(error instanceof krError) || !error.id) return; - delete _krCache.keepRight[error.id]; + delete _krCache.data[error.id]; updateRtree(encodeErrorRtree(error), false); // false = remove }, errorURL: function(error) { return _krUrlRoot + 'report_map.php?schema=' + error.schema + '&error=' + error.id; + }, + + + // Get an array of errors closed during this session. + // Used to populate `closed:keepright` changeset tag + getClosedIDs: function() { + return Object.keys(_krCache.closed).sort(); } }; diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 5d818a152..6ee11eaa2 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -8,6 +8,7 @@ import { select as d3_select } from 'd3-selection'; import { t } from '../util/locale'; import { osmChangeset } from '../osm'; +import { services } from '../services'; import { uiChangesetEditor } from './changeset_editor'; import { uiCommitChanges } from './commit_changes'; import { uiCommitWarnings } from './commit_warnings'; @@ -95,8 +96,18 @@ export function uiCommit(context) { tags = _clone(_changeset.tags); + // assign tags for imagery used var imageryUsed = context.history().imageryUsed().join(';').substr(0, 255); tags.imagery_used = imageryUsed || 'None'; + + // assign tags for closed issues and notes + if (services.keepRight) { + var krClosed = services.keepRight.getClosedIDs(); + if (krClosed.length) { + tags['closed:keepright'] = krClosed.join(';').substr(0, 255); + } + } + _changeset = _changeset.update({ tags: tags }); var header = selection.selectAll('.header') @@ -109,17 +120,17 @@ export function uiCommit(context) { headerTitle .append('div') .attr('class', 'header-block header-block-outer'); - + headerTitle .append('div') .attr('class', 'header-block') .append('h3') .text(t('commit.title')); - + headerTitle .append('div') .attr('class', 'header-block header-block-outer header-block-close') - .append('button') + .append('button') .attr('class', 'close') .on('click', function() { context.enter(modeBrowse(context)); }) .call(svgIcon('#iD-icon-close')); From d512b4b114eadd4aa821f8116073cc43d636181d Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 11 Jan 2019 12:18:51 +0100 Subject: [PATCH 105/114] Update 80_app.css Nicify grid for save area of notes and keepright. --- css/80_app.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/css/80_app.css b/css/80_app.css index 647d3158c..a8889987f 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2561,7 +2561,9 @@ input.key-trap { } .note-save, -.keepRight-save, +.keepRight-save { + padding-top: 20px; +} .kr_error-details, .kr_error-comment-container { padding: 10px; From 46dd90c5e53ee3e5f3f90a626fd0dadf5bc255f2 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" Date: Wed, 9 Jan 2019 07:06:12 +0000 Subject: [PATCH 106/114] chore(package): update rollup to version 1.1.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fb72db9a1..0e2e8996c 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "osm-community-index": "0.5.0", "phantomjs-prebuilt": "~2.1.11", "request": "^2.88.0", - "rollup": "~1.0.2", + "rollup": "~1.1.0", "rollup-plugin-commonjs": "^9.0.0", "rollup-plugin-includepaths": "~0.2.3", "rollup-plugin-json": "^3.0.0", From 55a92ca79d7c69f8cabb96adb3286c0cc7861110 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Jan 2019 10:14:55 -0500 Subject: [PATCH 107/114] Add `closed:note` changeset tag for closed OSM notes --- modules/services/osm.js | 18 ++++++++++++++++-- modules/ui/commit.js | 12 +++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/modules/services/osm.js b/modules/services/osm.js index d3e52cda5..9b5166859 100644 --- a/modules/services/osm.js +++ b/modules/services/osm.js @@ -47,7 +47,7 @@ var oauth = osmAuth({ var _blacklists = ['.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*']; var _tileCache = { loaded: {}, inflight: {}, seen: {} }; -var _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, rtree: rbush() }; +var _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() }; var _userCache = { toLoad: {}, user: {} }; var _changeset = {}; @@ -385,7 +385,7 @@ export default { if (_changeset.inflight) abortRequest(_changeset.inflight); _tileCache = { loaded: {}, inflight: {}, seen: {} }; - _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, rtree: rbush() }; + _noteCache = { loaded: {}, inflight: {}, inflightPost: {}, note: {}, closed: {}, rtree: rbush() }; _userCache = { toLoad: {}, user: {} }; _changeset = {}; @@ -956,6 +956,13 @@ export default { // we get the updated note back, remove from caches and reparse.. this.removeNote(note); + // update closed note cache - used to populate `closed:note` changeset tag + if (action === 'close') { + _noteCache.closed[note.id] = true; + } else if (action === 'reopen') { + delete _noteCache.closed[note.id]; + } + var options = { skipSeen: false }; return parseXML(xml, function(err, results) { if (err) { @@ -1118,6 +1125,13 @@ export default { _noteCache.note[note.id] = note; updateRtree(encodeNoteRtree(note), true); // true = replace return note; + }, + + + // Get an array of note IDs closed during this session. + // Used to populate `closed:note` changeset tag + getClosedIDs: function() { + return Object.keys(_noteCache.closed).sort(); } }; diff --git a/modules/ui/commit.js b/modules/ui/commit.js index 6ee11eaa2..46201e30c 100644 --- a/modules/ui/commit.js +++ b/modules/ui/commit.js @@ -64,6 +64,8 @@ export function uiCommit(context) { } var tags; + // Initialize changeset if one does not exist yet. + // Also pull values from local storage. if (!_changeset) { var detected = utilDetect(); tags = { @@ -82,13 +84,9 @@ export function uiCommit(context) { tags.hashtags = hashtags; } - // iD 2.8.1 could write a literal 'undefined' here.. see #5021 - // (old source values expire after 2 days, so 'undefined' checks can go away in v2.9) var source = context.storage('source'); - if (source && source !== 'undefined') { + if (source) { tags.source = source; - } else if (source === 'undefined') { - context.storage('source', null); } _changeset = new osmChangeset({ tags: tags }); @@ -101,6 +99,10 @@ export function uiCommit(context) { tags.imagery_used = imageryUsed || 'None'; // assign tags for closed issues and notes + var osmClosed = osm.getClosedIDs(); + if (osmClosed.length) { + tags['closed:note'] = osmClosed.join(';').substr(0, 255); + } if (services.keepRight) { var krClosed = services.keepRight.getClosedIDs(); if (krClosed.length) { From b575ee8500f9875815180bbfb2d35ec4e46348f0 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Jan 2019 13:56:16 -0500 Subject: [PATCH 108/114] Move formFields `klass` from an argument to a state variable (maybe needed in case render can get called different ways) --- modules/ui/form_fields.js | 32 +++++++++++++++++++------------- modules/ui/preset_editor.js | 5 +++-- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 08a7e2afb..10d16559f 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -7,25 +7,25 @@ import { utilGetSetValue, utilNoAuto } from '../util'; export function uiFormFields(context) { var moreCombo = uiCombobox(context, 'more-fields').minItems(1); + var _selection = d3_select(null); + var _fieldsArr = []; var _state = ''; - var _fieldsArr; + var _klass = ''; - function formFields(selection, klass) { - render(selection, klass); + function formFields(selection) { + _selection = selection + .call(render); } + formFields.tagsChanged = function() { + _selection + .call(render); + }; - formFields.tagsChanged = function() {}; - - function render(selection, klass) { - - formFields.tagsChanged = function() { - render(selection, klass); - }; + function render(selection) { var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); }); - var shown = allowedFields.filter(function(field) { return field.isShown(); }); var notShown = allowedFields.filter(function(field) { return !field.isShown(); }); @@ -34,7 +34,7 @@ export function uiFormFields(context) { container = container.enter() .append('div') - .attr('class', 'form-fields-container ' + (klass || '')) + .attr('class', 'form-fields-container ' + (_klass || '')) .merge(container); @@ -122,7 +122,7 @@ export function uiFormFields(context) { formFields.fieldsArr = function(val) { if (!arguments.length) return _fieldsArr; - _fieldsArr = val; + _fieldsArr = val || []; return formFields; }; @@ -132,6 +132,12 @@ export function uiFormFields(context) { return formFields; }; + formFields.klass = function(val) { + if (!arguments.length) return _klass; + _klass = val; + return formFields; + }; + return formFields; } diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js index 0c3d46ce6..75ea7e61a 100644 --- a/modules/ui/preset_editor.js +++ b/modules/ui/preset_editor.js @@ -87,8 +87,9 @@ export function uiPresetEditor(context) { selection .call(formFields .fieldsArr(_fieldsArr) - .state(_state), - 'inspector-inner fillL3'); + .state(_state) + .klass('inspector-inner fillL3') + ); selection.selectAll('.wrap-form-field input') From 09e574983fefad29fa7beda4db70247ab7fe0271 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Jan 2019 14:51:35 -0500 Subject: [PATCH 109/114] Don't call formFields.tagsChanged() when presetEditor.tags change (closes #5690) This change also makes sure to use the latest copy of the entity in field.isAllowed() to ensure the prerequisite field check works and fieldsArr is filtered properly for #5583 --- modules/ui/field.js | 34 +++++++++++++++++----------------- modules/ui/form_fields.js | 14 +------------- modules/ui/preset_editor.js | 1 - 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/modules/ui/field.js b/modules/ui/field.js index ccccb66eb..856b9e41a 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -231,38 +231,38 @@ export function uiField(context, presetField, entity, options) { } }; + // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown field.isShown = function() { return _show || isPresent(); }; + // An allowed field can appear in the UI or in the 'Add field' dropdown. // A non-allowed field is hidden from the user altogether field.isAllowed = function() { + if (!entity || isPresent()) return true; // a field with a value should always display - if (isPresent()) { - // always allow a field with a value to display - return true; - } + var latest = context.hasEntity(entity.id); // check the most current copy of the entity + if (!latest) return true; - var prerequisiteTag = field.prerequisiteTag; - if (prerequisiteTag && prerequisiteTag.key && field.entityID && context.hasEntity(field.entityID)) { - var value = context.entity(field.entityID).tags[prerequisiteTag.key]; - if (value) { - if (prerequisiteTag.valueNot) { - return prerequisiteTag.valueNot !== value; - } - if (prerequisiteTag.value) { - return prerequisiteTag.value === value; - } - return true; - } else { - return false; + var require = field.prerequisiteTag; + if (require && require.key) { + var value = latest.tags[require.key]; + if (!value) return false; + + if (require.valueNot) { + return require.valueNot !== value; } + if (require.value) { + return require.value === value; + } + return true; } return true; }; + field.focus = function() { if (field.impl) { field.impl.focus(); diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 10d16559f..a4a248174 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -7,24 +7,12 @@ import { utilGetSetValue, utilNoAuto } from '../util'; export function uiFormFields(context) { var moreCombo = uiCombobox(context, 'more-fields').minItems(1); - var _selection = d3_select(null); var _fieldsArr = []; var _state = ''; var _klass = ''; function formFields(selection) { - _selection = selection - .call(render); - } - - formFields.tagsChanged = function() { - _selection - .call(render); - }; - - - function render(selection) { var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); }); var shown = allowedFields.filter(function(field) { return field.isShown(); }); var notShown = allowedFields.filter(function(field) { return !field.isShown(); }); @@ -111,7 +99,7 @@ export function uiFormFields(context) { .on('accept', function (d) { var field = d.field; field.show(); - render(selection); + selection.call(formFields); // rerender if (field.type !== 'semiCombo' && field.type !== 'multiCombo') { field.focus(); } diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js index 75ea7e61a..2ccbabd07 100644 --- a/modules/ui/preset_editor.js +++ b/modules/ui/preset_editor.js @@ -121,7 +121,6 @@ export function uiPresetEditor(context) { presetEditor.tags = function(val) { if (!arguments.length) return _tags; _tags = val; - formFields.tagsChanged(); // Don't reset _fieldsArr here. return presetEditor; }; From 1f69661b91875e3f5a6904e3a82715b247fa8f23 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 11 Jan 2019 18:02:17 -0500 Subject: [PATCH 110/114] Add a preset "RV Dealership" for `shop=caravan` --- data/presets.yaml | 5 +++++ data/presets/presets.json | 1 + data/presets/presets/shop/caravan.json | 25 +++++++++++++++++++++++++ data/taginfo.json | 7 +++++++ dist/locales/en.json | 4 ++++ 5 files changed, 42 insertions(+) create mode 100644 data/presets/presets/shop/caravan.json diff --git a/data/presets.yaml b/data/presets.yaml index 8665ac920..ef4ce6615 100644 --- a/data/presets.yaml +++ b/data/presets.yaml @@ -5961,6 +5961,11 @@ en: name: Car Repair Shop # 'terms: auto,garage,service' terms: '' + shop/caravan: + # shop=caravan + name: RV Dealership + # 'terms: auto' + terms: '' shop/carpet: # shop=carpet name: Carpet Store diff --git a/data/presets/presets.json b/data/presets/presets.json index 7ed15f534..67fa6891b 100644 --- a/data/presets/presets.json +++ b/data/presets/presets.json @@ -858,6 +858,7 @@ "shop/car_parts": {"icon": "maki-car", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "car_parts"}, "name": "Car Parts Store"}, "shop/car_repair": {"icon": "maki-car-repair", "fields": ["name", "operator", "address", "building_area", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto", "garage", "service"], "tags": {"shop": "car_repair"}, "name": "Car Repair Shop"}, "shop/car": {"icon": "maki-car", "fields": ["name", "brand", "operator", "address", "building_area", "second_hand", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "car"}, "name": "Car Dealership"}, + "shop/caravan": {"icon": "maki-car", "fields": ["name", "brand", "operator", "address", "building_area", "second_hand", "service/vehicle", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["auto"], "tags": {"shop": "caravan"}, "name": "RV Dealership"}, "shop/carpet": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["rug"], "tags": {"shop": "carpet"}, "name": "Carpet Store"}, "shop/catalogue": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "tags": {"shop": "catalogue"}, "name": "Catalog Shop"}, "shop/charity": {"icon": "maki-shop", "fields": ["name", "operator", "address", "building_area", "second_hand", "opening_hours", "payment_multi"], "geometry": ["point", "area"], "terms": ["thrift", "op shop", "nonprofit"], "tags": {"shop": "charity"}, "name": "Charity Store"}, diff --git a/data/presets/presets/shop/caravan.json b/data/presets/presets/shop/caravan.json new file mode 100644 index 000000000..4912aa4fa --- /dev/null +++ b/data/presets/presets/shop/caravan.json @@ -0,0 +1,25 @@ +{ + "icon": "maki-car", + "fields": [ + "name", + "brand", + "operator", + "address", + "building_area", + "second_hand", + "service/vehicle", + "opening_hours", + "payment_multi" + ], + "geometry": [ + "point", + "area" + ], + "terms": [ + "auto" + ], + "tags": { + "shop": "caravan" + }, + "name": "RV Dealership" +} diff --git a/data/taginfo.json b/data/taginfo.json index 3ebc49bfd..3b8ff842c 100644 --- a/data/taginfo.json +++ b/data/taginfo.json @@ -5561,6 +5561,13 @@ "object_types": ["node", "area"], "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true" }, + { + "key": "shop", + "value": "caravan", + "description": "🄿 RV Dealership", + "object_types": ["node", "area"], + "icon_url": "https://raw.githubusercontent.com/mapbox/maki/master/icons/car-15.svg?sanitize=true" + }, { "key": "shop", "value": "carpet", diff --git a/dist/locales/en.json b/dist/locales/en.json index 0973332bd..7c08de65e 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -6986,6 +6986,10 @@ "name": "Car Dealership", "terms": "auto" }, + "shop/caravan": { + "name": "RV Dealership", + "terms": "auto" + }, "shop/carpet": { "name": "Carpet Store", "terms": "rug" From 36a6596897c7380ccfeb205ebe9c9ab444910d62 Mon Sep 17 00:00:00 2001 From: Iman Date: Sat, 12 Jan 2019 13:11:26 +0330 Subject: [PATCH 111/114] margin-right for keyhints --- css/80_app.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index 811c0fc85..b15ec1438 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -4487,6 +4487,10 @@ svg.mouseclick use.right { margin-left: 5px; } +[dir='rtl'] .tooltip-inner .keyhint { + margin-left: 0; + margin-right: 5px; +} /* dark tooltips for sidebar / panels */ .map-pane .tooltip.top .tooltip-arrow, From 7b53d0aa5af567d273738064f38c8e0190e7b28f Mon Sep 17 00:00:00 2001 From: iriman Date: Sun, 13 Jan 2019 19:43:32 +0330 Subject: [PATCH 112/114] [RTL] pick appropriate class for `.field-help-title > h2` --- modules/ui/field_help.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/ui/field_help.js b/modules/ui/field_help.js index 6289180dd..fcc54f42d 100644 --- a/modules/ui/field_help.js +++ b/modules/ui/field_help.js @@ -4,7 +4,7 @@ import { } from 'd3-selection'; import marked from 'marked'; -import { t } from '../util/locale'; +import { t, textDirection } from '../util/locale'; import { svgIcon } from '../svg'; import { icon } from './intro/helper'; @@ -197,7 +197,7 @@ export function uiFieldHelp(context, fieldName) { titleEnter .append('h2') - .attr('class', 'fl') + .attr('class', ((textDirection === 'rtl') ? 'fr' : 'fl')) .text(t('help.field.' + fieldName + '.title')); titleEnter From a8fb9fc62238e0689fbde33d22c685a6eef8fe12 Mon Sep 17 00:00:00 2001 From: iriman Date: Sun, 13 Jan 2019 20:11:54 +0330 Subject: [PATCH 113/114] [RTL] right padding for `.member-entity-name` --- css/80_app.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/css/80_app.css b/css/80_app.css index b15ec1438..ae4c2c8ab 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -2275,6 +2275,12 @@ div.combobox { padding-left: 10px; } +[dir='rtl'] .raw-member-editor .member-row .member-entity-name, +[dir='rtl'] .raw-membership-editor .member-row .member-entity-name { + padding-left:0; + padding-right: 10px; +} + .form-field-input-member > input.member-role { border-radius: 0 0 0 4px; } From bc9a64058fec92beb6e4bf62b2be607cd3aab1a9 Mon Sep 17 00:00:00 2001 From: iriman Date: Mon, 14 Jan 2019 15:50:31 +0330 Subject: [PATCH 114/114] [RTL] place edit menu under #id-container to inherit rtl --- modules/ui/edit_menu.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ui/edit_menu.js b/modules/ui/edit_menu.js index 8c99a45b2..f74272964 100644 --- a/modules/ui/edit_menu.js +++ b/modules/ui/edit_menu.js @@ -104,7 +104,7 @@ export function uiEditMenu(context, operations) { .attr('transform', function () { return 'translate(' + [2 * p, 5] + ')'; }) .attr('xlink:href', function (d) { return '#iD-operation-' + d.id; }); - tooltip = d3_select(document.body) + tooltip = d3_select('#id-container') .append('div') .attr('class', 'tooltip-inner edit-menu-tooltip');