diff --git a/modules/services/osmose.js b/modules/services/osmose.js
index 46f498ecf..d42994e5a 100644
--- a/modules/services/osmose.js
+++ b/modules/services/osmose.js
@@ -9,280 +9,267 @@ import { qaError } from '../osm';
import { utilRebind, utilTiler, utilQsString } from '../util';
import { services } from '../../data/qa_errors.json';
-var tiler = utilTiler();
-var dispatch = d3_dispatch('loaded');
+const tiler = utilTiler();
+const dispatch = d3_dispatch('loaded');
+const _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/';
+const _erZoom = 14;
-var _erCache;
-var _erZoom = 14;
-
-var _osmoseUrlRoot = 'https://osmose.openstreetmap.fr/';
+// This gets reassigned if reset
+let _erCache;
function abortRequest(controller) {
- if (controller) {
- controller.abort();
- }
+ if (controller) {
+ controller.abort();
+ }
}
function abortUnwantedRequests(cache, tiles) {
- Object.keys(cache.inflightTile).forEach(function(k) {
- var wanted = tiles.find(function(tile) { return k === tile.id; });
- if (!wanted) {
- abortRequest(cache.inflightTile[k]);
- delete cache.inflightTile[k];
- }
- });
+ Object.keys(cache.inflightTile).forEach(k => {
+ let wanted = tiles.find(tile => k === tile.id);
+ if (!wanted) {
+ abortRequest(cache.inflightTile[k]);
+ delete cache.inflightTile[k];
+ }
+ });
}
-
function encodeErrorRtree(d) {
- return { minX: d.loc[0], minY: d.loc[1], maxX: d.loc[0], maxY: d.loc[1], data: 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) {
- _erCache.rtree.remove(item, function isEql(a, b) {
- return a.data.id === b.data.id;
- });
+ _erCache.rtree.remove(item, (a, b) => a.data.id === b.data.id);
- if (replace) {
- _erCache.rtree.insert(item);
- }
+ if (replace) {
+ _erCache.rtree.insert(item);
+ }
}
function linkEntity(d) {
- return '' + d + '';
+ return `${d}`;
}
// Errors shouldn't obscure eachother
-function preventCoincident(loc, bumpUp) {
- var coincident = false;
- do {
- // first time, move marker up. after that, move marker right.
- var delta = coincident ? [0.00001, 0] : (bumpUp ? [0, 0.00001] : [0, 0]);
- loc = geoVecAdd(loc, delta);
- var bbox = geoExtent(loc).bbox();
- coincident = _erCache.rtree.search(bbox).length;
- } while (coincident);
+function preventCoincident(loc) {
+ let coincident = false;
+ do {
+ // first time, move marker up. after that, move marker right.
+ let delta = coincident ? [0.00001, 0] : [0, 0.00001];
+ loc = geoVecAdd(loc, delta);
+ let bbox = geoExtent(loc).bbox();
+ coincident = _erCache.rtree.search(bbox).length;
+ } while (coincident);
- return loc;
+ return loc;
}
export default {
- init: function() {
- if (!_erCache) {
- this.reset();
- }
+ init() {
+ if (!_erCache) {
+ this.reset();
+ }
- this.event = utilRebind(this, dispatch, 'on');
- },
+ this.event = utilRebind(this, dispatch, 'on');
+ },
- reset: function() {
- if (_erCache) {
- Object.values(_erCache.inflightTile).forEach(abortRequest);
- }
- _erCache = {
- data: {},
- loadedTile: {},
- inflightTile: {},
- inflightPost: {},
- closed: {},
- rtree: new RBush()
- };
- },
+ reset() {
+ if (_erCache) {
+ Object.values(_erCache.inflightTile).forEach(abortRequest);
+ }
+ _erCache = {
+ data: {},
+ loadedTile: {},
+ inflightTile: {},
+ inflightPost: {},
+ closed: {},
+ rtree: new RBush()
+ };
+ },
- loadErrors: function(projection) {
- var params = {
- item: services.osmose.items.join() // only interested in certain errors
- };
+ loadErrors(projection) {
+ let params = {
+ // Tiles return a maximum # of errors
+ // So we want to filter our request for only types iD supports
+ item: services.osmose.items.join()
+ };
- // determine the needed tiles to cover the view
- var tiles = tiler
- .zoomExtent([_erZoom, _erZoom])
- .getTiles(projection);
+ // determine the needed tiles to cover the view
+ let tiles = tiler
+ .zoomExtent([_erZoom, _erZoom])
+ .getTiles(projection);
- // abort inflight requests that are no longer needed
- abortUnwantedRequests(_erCache, tiles);
+ // abort inflight requests that are no longer needed
+ abortUnwantedRequests(_erCache, tiles);
- // issue new requests..
- tiles.forEach(function(tile) {
- if (_erCache.loadedTile[tile.id] || _erCache.inflightTile[tile.id]) return;
+ // issue new requests..
+ tiles.forEach(tile => {
+ if (_erCache.loadedTile[tile.id] || _erCache.inflightTile[tile.id]) return;
- var lang = 'en'; // todo: may want to use provided translations
- var path = [tile.xyz[2], tile.xyz[0], tile.xyz[1]].join('/');
- var url = _osmoseUrlRoot + lang + '/map/issues/' + path + '.json?' + utilQsString(params);
+ let lang = 'en'; // todo: may want to use provided translations
+ let [ x, y, z ] = tile.xyz;
+ let url = _osmoseUrlRoot + `${lang}/map/issues/${z}/${x}/${y}.json?` + utilQsString(params);
- var controller = new AbortController();
- _erCache.inflightTile[tile.id] = controller;
+ let controller = new AbortController();
+ _erCache.inflightTile[tile.id] = controller;
- d3_json(url, { signal: controller.signal })
- .then(function(data) {
- delete _erCache.inflightTile[tile.id];
- _erCache.loadedTile[tile.id] = true;
+ d3_json(url, { signal: controller.signal })
+ .then(data => {
+ delete _erCache.inflightTile[tile.id];
+ _erCache.loadedTile[tile.id] = true;
- if (data.features) {
- data.features.forEach(function(issue) {
- var loc = issue.geometry.coordinates; // lon, lat
- var props = issue.properties;
- // Item is the type of error, w/ class tells us the sub-type
- var type = [props.item, props.class].join('-');
+ if (data.features) {
+ data.features.forEach(issue => {
+ const { item, class: error_class, issue_id: identifier } = issue.properties;
+ // Item is the type of error, w/ class tells us the sub-type
+ const error_type = [item, error_class].join('-');
- // Filter out unsupported error types (some are too specific or advanced)
- if (type in services.osmose.errorTypes) {
- loc = preventCoincident(loc, true);
+ // Filter out unsupported error types (some are too specific or advanced)
+ if (error_type in services.osmose.errorTypes) {
+ let loc = issue.geometry.coordinates; // lon, lat
+ loc = preventCoincident(loc);
- var d = new qaError({
- // Info required for every error
- loc: loc,
- service: 'osmose',
- error_type: type,
- // Extra details needed for this service
- identifier: props.issue_id, // needed to query and update the error
- item: props.item, // category of the issue for styling
- class: props.class
- });
-
- // Special handling for some error types
- // Setting elems here prevents UI error detail requests
- switch (d.item) {
- case 8300:
- case 8360:
- mapillaryError(d);
- break;
- }
-
- _erCache.data[d.id] = d;
- _erCache.rtree.insert(encodeErrorRtree(d));
- }
- });
- }
-
- dispatch.call('loaded');
- })
- .catch(function() {
- delete _erCache.inflightTile[tile.id];
- _erCache.loadedTile[tile.id] = true;
- });
- });
-
- function mapillaryError(d) {
- // Parts only exists for these error types
- var parts = dataEn.QA.osmose.error_types[d.item].parts;
- d.replacements = [parts[d.class]];
- d.elems = [];
- }
- },
-
- loadErrorDetail: function(d, callback) {
- // Error details only need to be fetched once
- if (d.elems !== undefined) {
- if (callback) callback(null, d);
- return;
- }
-
- var url = _osmoseUrlRoot + 'en/api/0.3beta/issue/' + d.identifier;
-
- var that = this;
- d3_json(url)
- .then(function(data) {
- // Associated elements used for highlighting
- // Assign directly for immediate use in the callback
- d.elems = data.elems.map(function(e) {
- return e.type.substring(0,1) + e.id;
- });
-
- // Element links used in the error description
- d.replacements = d.elems.map(function(i) {
- return linkEntity(i);
+ let d = new qaError({
+ // Info required for every error
+ loc,
+ service: 'osmose',
+ error_type,
+ // Extra details needed for this service
+ identifier, // needed to query and update the error
+ item // category of the issue for styling
});
// Special handling for some error types
+ // Setting elems here prevents UI error detail requests
switch (d.item) {
- case 3040:
- d.replacements.push(/Bad value for (.+)/i
- .exec(data.subtitle)[1]
- );
- break;
+ case 8300:
+ case 8360: {
+ // Parts only exists for these error types
+ let { parts } = dataEn.QA.osmose.error_types[d.item];
+ d.replacements = [parts[error_class]];
+ d.elems = [];
+ break;
+ }
}
- that.replaceError(d);
- if (callback) callback(null, d);
- })
- .catch(function(err) {
- if (callback) callback(err.message);
+ _erCache.data[d.id] = d;
+ _erCache.rtree.insert(encodeErrorRtree(d));
+ }
});
- },
+ }
- postUpdate: function(d, callback) {
- if (_erCache.inflightPost[d.id]) {
- return callback({ message: 'Error update already inflight', status: -2 }, d);
+ dispatch.call('loaded');
+ })
+ .catch(() => {
+ delete _erCache.inflightTile[tile.id];
+ _erCache.loadedTile[tile.id] = true;
+ });
+ });
+ },
+
+ loadErrorDetail(d, callback) {
+ // Error details only need to be fetched once
+ if (d.elems !== undefined) {
+ if (callback) callback(null, d);
+ return;
+ }
+
+ let url = _osmoseUrlRoot + `en/api/0.3beta/issue/${d.identifier}`;
+
+ d3_json(url)
+ .then(data => {
+ // Associated elements used for highlighting
+ // Assign directly for immediate use in the callback
+ d.elems = data.elems.map(e => e.type.substring(0,1) + e.id);
+
+ // Element links used in the error description
+ d.replacements = d.elems.map(linkEntity);
+
+ // Special handling for some error types
+ switch (d.item) {
+ case 3040: {
+ let [, key_value] = /Bad value for (.+)/i.exec(data.subtitle);
+ d.replacements.push(key_value);
+ break;
+ }
}
- var that = this;
+ this.replaceError(d);
+ if (callback) callback(null, d);
+ })
+ .catch(err => {
+ if (callback) callback(err.message);
+ });
+ },
- // UI sets the status to either '/done' or '/false'
- var url = _osmoseUrlRoot + 'en/api/0.3beta/issue/' + d.identifier + d.newStatus;
-
- var controller = new AbortController();
- _erCache.inflightPost[d.id] = controller;
-
- fetch(url, { signal: controller.signal })
- .then(function() {
- delete _erCache.inflightPost[d.id];
-
- that.removeError(d);
- if (d.newStatus === '/done') {
- // No error identifier, so we give a count of each category
- if (!(d.item in _erCache.closed)) {
- _erCache.closed[d.item] = 0;
- }
- _erCache.closed[d.item] += 1;
- }
- if (callback) callback(null, d);
- })
- .catch(function(err) {
- delete _erCache.inflightPost[d.id];
- if (callback) callback(err.message);
- });
- },
-
-
- // 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();
-
- return _erCache.rtree.search(bbox).map(function(d) {
- return d.data;
- });
- },
-
- // get a single error from the cache
- getError: function(id) {
- return _erCache.data[id];
- },
-
- // replace a single error in the cache
- replaceError: function(error) {
- if (!(error instanceof qaError) || !error.id) return;
-
- _erCache.data[error.id] = error;
- updateRtree(encodeErrorRtree(error), true); // true = replace
- return error;
- },
-
- // remove a single error from the cache
- removeError: function(error) {
- if (!(error instanceof qaError) || !error.id) return;
-
- delete _erCache.data[error.id];
- updateRtree(encodeErrorRtree(error), false); // false = remove
- },
-
- // Used to populate `closed:osmose:*` changeset tags
- getClosedCounts: function() {
- return _erCache.closed;
+ postUpdate(d, callback) {
+ if (_erCache.inflightPost[d.id]) {
+ return callback({ message: 'Error update already inflight', status: -2 }, d);
}
-};
\ No newline at end of file
+
+ // UI sets the status to either 'done' or 'false'
+ let url = _osmoseUrlRoot + `en/api/0.3beta/issue/${d.identifier}/${d.newStatus}`;
+
+ let controller = new AbortController();
+ _erCache.inflightPost[d.id] = controller;
+
+ fetch(url, { signal: controller.signal })
+ .then(() => {
+ delete _erCache.inflightPost[d.id];
+
+ this.removeError(d);
+ if (d.newStatus === 'done') {
+ // No error identifier, so we give a count of each category
+ if (!(d.item in _erCache.closed)) {
+ _erCache.closed[d.item] = 0;
+ }
+ _erCache.closed[d.item] += 1;
+ }
+ if (callback) callback(null, d);
+ })
+ .catch(err => {
+ delete _erCache.inflightPost[d.id];
+ if (callback) callback(err.message);
+ });
+ },
+
+
+ // get all cached errors covering the viewport
+ getErrors(projection) {
+ let viewport = projection.clipExtent();
+ let min = [viewport[0][0], viewport[1][1]];
+ let max = [viewport[1][0], viewport[0][1]];
+ let bbox = geoExtent(projection.invert(min), projection.invert(max)).bbox();
+
+ return _erCache.rtree.search(bbox).map(d => {
+ return d.data;
+ });
+ },
+
+ // get a single error from the cache
+ getError(id) {
+ return _erCache.data[id];
+ },
+
+ // replace a single error in the cache
+ replaceError(error) {
+ if (!(error instanceof qaError) || !error.id) return;
+
+ _erCache.data[error.id] = error;
+ updateRtree(encodeErrorRtree(error), true); // true = replace
+ return error;
+ },
+
+ // remove a single error from the cache
+ removeError(error) {
+ if (!(error instanceof qaError) || !error.id) return;
+
+ delete _erCache.data[error.id];
+ updateRtree(encodeErrorRtree(error), false); // false = remove
+ },
+
+ // Used to populate `closed:osmose:*` changeset tags
+ getClosedCounts() {
+ return _erCache.closed;
+ }
+};
diff --git a/modules/ui/osmose_editor.js b/modules/ui/osmose_editor.js
index 38cbc42fa..4571eeb56 100644
--- a/modules/ui/osmose_editor.js
+++ b/modules/ui/osmose_editor.js
@@ -136,7 +136,7 @@ export function uiOsmoseEditor(context) {
this.blur(); // avoid keeping focus on the button - #4641
var errorService = services.osmose;
if (errorService) {
- d.newStatus = '/done';
+ d.newStatus = 'done';
errorService.postUpdate(d, function(err, error) {
dispatch.call('change', error);
});
@@ -151,7 +151,7 @@ export function uiOsmoseEditor(context) {
this.blur(); // avoid keeping focus on the button - #4641
var errorService = services.osmose;
if (errorService) {
- d.newStatus = '/false';
+ d.newStatus = 'false';
errorService.postUpdate(d, function(err, error) {
dispatch.call('change', error);
});