Convert Osmsoe service to ES6 syntax

This commit is contained in:
SilentSpike
2019-12-30 19:07:12 +00:00
parent 896ed762e0
commit 12206c68e6
2 changed files with 218 additions and 231 deletions
+216 -229
View File
@@ -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 '<a class="error_entity_link">' + d + '</a>';
return `<a class="error_entity_link">${d}</a>`;
}
// 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);
}
};
// 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;
}
};
+2 -2
View File
@@ -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);
});