From b3cffd78333e38aba150057678b50280df84104d Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Wed, 25 Jul 2018 11:21:15 -0400 Subject: [PATCH 01/60] 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 02/60] 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 03/60] 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 04/60] 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 05/60] 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 06/60] 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 07/60] 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 08/60] 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 09/60] 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 10/60] 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 11/60] 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 12/60] 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 13/60] 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 14/60] 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 15/60] 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 16/60] 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 17/60] 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 18/60] 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 19/60] 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 20/60] 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 21/60] 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 22/60] 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 23/60] 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 24/60] 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 25/60] 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 26/60] 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 27/60] 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 28/60] 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 29/60] 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 30/60] 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 31/60] 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 32/60] 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 33/60] 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 34/60] 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 35/60] 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 36/60] 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 7f669d292f12d98be1fe029e31bf2a0597440b3d Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 20 Dec 2018 14:40:14 -0500 Subject: [PATCH 37/60] 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 38/60] 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 39/60] 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 40/60] 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 41/60] 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 3785ffb15478b7524c59e1ae0ecb6731f3581df3 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 22 Dec 2018 12:12:28 -0500 Subject: [PATCH 42/60] 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 192b4da9e6cec23f3a3457c81e3e503cfe85e630 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Sat, 29 Dec 2018 17:30:35 -0500 Subject: [PATCH 43/60] 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 44/60] 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 2ec02f369f02626cd94c0a47d9ebb698c8fca431 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 2 Jan 2019 11:46:13 -0500 Subject: [PATCH 45/60] 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 46/60] 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 47/60] 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 48/60] 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 49/60] 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 50/60] 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 51/60] 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 52/60] 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 53/60] 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 54/60] 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 55/60] 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 56/60] 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 cd9203975d7ba15a567ac0d2a8bc9cba07aae330 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Fri, 4 Jan 2019 15:48:39 -0500 Subject: [PATCH 57/60] 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 58/60] 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 59/60] 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 60/60] 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;