From 75cff00a2a4fa599b8fcb45bd0661cc8cf93d2fa Mon Sep 17 00:00:00 2001 From: Thomas Hervey Date: Sat, 28 Jul 2018 13:55:33 -0400 Subject: [PATCH] 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) }); });