Refactor things out of util with limited use, move more to service

This commit is contained in:
Bryan Housel
2018-12-21 11:56:55 -05:00
parent 4eb0e4b3a4
commit f7150004c0
11 changed files with 216 additions and 291 deletions
+11 -15
View File
@@ -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:
+11 -15
View File
@@ -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": {
+182 -6
View File
@@ -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 '<span><a class="kr_error_description-id">' + d + '</a></span>'; }
// 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));
});
+10 -4
View File
@@ -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);
}
}
+2 -9
View File
@@ -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 }); });
}
-44
View File
@@ -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);
-2
View File
@@ -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';
-3
View File
@@ -1,3 +0,0 @@
export { errorTypes } from './errorSchema.json';
export { parseError } from './parse_error';
export { parseErrorDescriptions, clickLink } from './keepRight_error';
-80
View File
@@ -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);
}
-113
View File
@@ -1,113 +0,0 @@
export function parseError(group, idType) {
function fillPlaceholder(d) { return '<span><a class="kr_error_description-id">' + d + '</a></span>'; }
// 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;
}