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(); }