mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 01:02:58 +00:00
Load issue fixes dynamically instead of cacheing them (close #7037)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { geoExtent } from '../../geo';
|
||||
import { t } from '../../util/locale';
|
||||
|
||||
export function validationIssue(attrs) {
|
||||
this.type = attrs.type; // required - name of rule that created the issue (e.g. 'missing_tag')
|
||||
@@ -9,7 +10,7 @@ export function validationIssue(attrs) {
|
||||
this.entityIds = attrs.entityIds; // optional - array of IDs of entities involved in the issue
|
||||
this.loc = attrs.loc; // optional - [lon, lat] to zoom in on to see the issue
|
||||
this.data = attrs.data; // optional - object containing extra data for the fixes
|
||||
this.fixes = attrs.fixes || []; // optional - array of validationIssueFix objects
|
||||
this.dynamicFixes = attrs.dynamicFixes;// optional - function(context) returning fixes
|
||||
this.hash = attrs.hash; // optional - string to further differentiate the issue
|
||||
|
||||
this.id = generateID.apply(this); // generated - see below
|
||||
@@ -50,25 +51,41 @@ export function validationIssue(attrs) {
|
||||
return null;
|
||||
};
|
||||
|
||||
this.fixes = function(context) {
|
||||
var fixes = this.dynamicFixes ? this.dynamicFixes(context) : [];
|
||||
var issue = this;
|
||||
|
||||
if (this.fixes) { // add a reference in the fixes to the issue for use in fix actions
|
||||
for (var i = 0; i < this.fixes.length; i++) {
|
||||
var fix = this.fixes[i];
|
||||
fix.issue = this;
|
||||
if (fix.autoArgs) {
|
||||
this.autoFix = fix;
|
||||
}
|
||||
if (issue.severity === 'warning') {
|
||||
// allow ignoring any issue that's not an error
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.ignore_issue.title'),
|
||||
icon: 'iD-icon-close',
|
||||
onClick: function() {
|
||||
context.validator().ignoreIssue(this.issue.id);
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
fixes.forEach(function(fix) {
|
||||
fix.id = fix.title;
|
||||
// add a reference to the issue for use in actions
|
||||
fix.issue = issue;
|
||||
if (fix.autoArgs) {
|
||||
issue.autoFix = fix;
|
||||
}
|
||||
});
|
||||
return fixes;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
export function validationIssueFix(attrs) {
|
||||
this.title = attrs.title; // Required
|
||||
this.onClick = attrs.onClick; // Required
|
||||
this.onClick = attrs.onClick; // Optional - the function to run to apply the fix
|
||||
this.icon = attrs.icon; // Optional - shows 'iD-icon-wrench' if not set
|
||||
this.entityIds = attrs.entityIds || []; // Optional - Used for hover-higlighting.
|
||||
this.entityIds = attrs.entityIds || []; // Optional - used for hover-higlighting.
|
||||
this.autoArgs = attrs.autoArgs; // Optional - pass [actions, annotation] arglist if this fix can automatically run
|
||||
|
||||
this.issue = null; // Generated link - added by ValidationIssue constructor
|
||||
this.issue = null; // Generated link - added by validationIssue
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import { geoExtent } from '../geo/extent';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { utilArrayGroupBy, utilRebind } from '../util';
|
||||
import { t } from '../util/locale';
|
||||
import { validationIssueFix } from './validation/models';
|
||||
import * as Validations from '../validations/index';
|
||||
|
||||
|
||||
@@ -91,19 +90,7 @@ export function coreValidator(context) {
|
||||
buildings.forEach(function(entity) {
|
||||
var detected = checkUnsquareWay(entity, graph);
|
||||
if (detected.length !== 1) return;
|
||||
|
||||
var issue = detected[0];
|
||||
var ignoreFix = new validationIssueFix({
|
||||
title: t('issues.fix.ignore_issue.title'),
|
||||
icon: 'iD-icon-close',
|
||||
onClick: function() {
|
||||
ignoreIssue(this.issue.id);
|
||||
}
|
||||
});
|
||||
ignoreFix.type = 'ignore';
|
||||
ignoreFix.issue = issue;
|
||||
issue.fixes.push(ignoreFix);
|
||||
|
||||
if (!cache.issuesByEntityID[entity.id]) {
|
||||
cache.issuesByEntityID[entity.id] = new Set();
|
||||
}
|
||||
@@ -275,9 +262,9 @@ export function coreValidator(context) {
|
||||
};
|
||||
|
||||
|
||||
function ignoreIssue(id) {
|
||||
validator.ignoreIssue = function(id) {
|
||||
_ignoredIssueIDs[id] = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
@@ -296,21 +283,6 @@ export function coreValidator(context) {
|
||||
}
|
||||
|
||||
var detected = fn(entity, graph);
|
||||
detected.forEach(function(issue) {
|
||||
var hasIgnoreFix = issue.fixes && issue.fixes.length && issue.fixes[issue.fixes.length - 1].type === 'ignore';
|
||||
if (issue.severity === 'warning' && !hasIgnoreFix) {
|
||||
var ignoreFix = new validationIssueFix({
|
||||
title: t('issues.fix.ignore_issue.title'),
|
||||
icon: 'iD-icon-close',
|
||||
onClick: function() {
|
||||
ignoreIssue(this.issue.id);
|
||||
}
|
||||
});
|
||||
ignoreFix.type = 'ignore';
|
||||
ignoreFix.issue = issue;
|
||||
issue.fixes.push(ignoreFix);
|
||||
}
|
||||
});
|
||||
entityIssues = entityIssues.concat(detected);
|
||||
}
|
||||
|
||||
|
||||
@@ -197,16 +197,17 @@ export function uiEntityIssues(context) {
|
||||
var fixLists = containers.selectAll('.issue-fix-list');
|
||||
|
||||
var fixes = fixLists.selectAll('.issue-fix-item')
|
||||
.data(function(d) { return d.fixes ? d.fixes : []; });
|
||||
.data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
|
||||
|
||||
fixes.exit()
|
||||
.remove();
|
||||
|
||||
var fixesEnter = fixes.enter()
|
||||
.append('li')
|
||||
.attr('class', function(d) {
|
||||
return 'issue-fix-item ' + (d.onClick ? 'actionable' : '');
|
||||
})
|
||||
.attr('class', 'issue-fix-item')
|
||||
.on('click', function(d) {
|
||||
// not all fixes are actionable
|
||||
if (!d.onClick) return;
|
||||
if (!d3_select(this).classed('actionable') || !d.onClick) return;
|
||||
|
||||
// Don't run another fix for this issue within a second of running one
|
||||
// (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
|
||||
@@ -250,6 +251,11 @@ export function uiEntityIssues(context) {
|
||||
.append('span')
|
||||
.attr('class', 'fix-message')
|
||||
.text(function(d) { return d.title; });
|
||||
|
||||
fixesEnter.merge(fixes)
|
||||
.classed('actionable', function(d) {
|
||||
return d.onClick;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,9 +42,40 @@ export function validationAlmostJunction(context) {
|
||||
|
||||
var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
|
||||
extendableNodeInfos.forEach(function(extendableNodeInfo) {
|
||||
var node = extendableNodeInfo.node;
|
||||
var edgeHighway = graph.entity(extendableNodeInfo.wid);
|
||||
issues.push(new validationIssue({
|
||||
type: type,
|
||||
subtype: 'highway-highway',
|
||||
severity: 'warning',
|
||||
message: function(context) {
|
||||
var entity1 = context.hasEntity(this.entityIds[0]);
|
||||
if (this.entityIds[0] === this.entityIds[2]) {
|
||||
return entity1 ? t('issues.almost_junction.self.message', {
|
||||
feature: utilDisplayLabel(entity1, context)
|
||||
}) : '';
|
||||
} else {
|
||||
var entity2 = context.hasEntity(this.entityIds[2]);
|
||||
return (entity1 && entity2) ? t('issues.almost_junction.message', {
|
||||
feature: utilDisplayLabel(entity1, context),
|
||||
feature2: utilDisplayLabel(entity2, context)
|
||||
}) : '';
|
||||
}
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [entity.id, extendableNodeInfo.node.id, extendableNodeInfo.wid],
|
||||
loc: extendableNodeInfo.node.loc,
|
||||
hash: JSON.stringify(extendableNodeInfo.node.loc),
|
||||
data: {
|
||||
edge: extendableNodeInfo.edge,
|
||||
cross_loc: extendableNodeInfo.cross_loc
|
||||
},
|
||||
dynamicFixes: makeFixes
|
||||
}));
|
||||
});
|
||||
|
||||
return issues;
|
||||
|
||||
|
||||
function makeFixes(context) {
|
||||
var fixes = [new validationIssueFix({
|
||||
icon: 'iD-icon-abutment',
|
||||
title: t('issues.fix.connect_features.title'),
|
||||
@@ -73,7 +104,8 @@ export function validationAlmostJunction(context) {
|
||||
}
|
||||
})];
|
||||
|
||||
if (Object.keys(node.tags).length === 0) {
|
||||
var node = context.hasEntity(this.entityIds[1]);
|
||||
if (node && Object.keys(node.tags).length === 0) {
|
||||
// node has no tags, suggest noexit fix
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'maki-barrier',
|
||||
@@ -88,37 +120,8 @@ export function validationAlmostJunction(context) {
|
||||
}));
|
||||
}
|
||||
|
||||
issues.push(new validationIssue({
|
||||
type: type,
|
||||
subtype: 'highway-highway',
|
||||
severity: 'warning',
|
||||
message: function(context) {
|
||||
var entity1 = context.hasEntity(this.entityIds[0]);
|
||||
if (this.entityIds[0] === this.entityIds[2]) {
|
||||
return entity1 ? t('issues.almost_junction.self.message', {
|
||||
feature: utilDisplayLabel(entity1, context)
|
||||
}) : '';
|
||||
} else {
|
||||
var entity2 = context.hasEntity(this.entityIds[2]);
|
||||
return (entity1 && entity2) ? t('issues.almost_junction.message', {
|
||||
feature: utilDisplayLabel(entity1, context),
|
||||
feature2: utilDisplayLabel(entity2, context)
|
||||
}) : '';
|
||||
}
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [entity.id, node.id, edgeHighway.id],
|
||||
loc: extendableNodeInfo.node.loc,
|
||||
hash: JSON.stringify(extendableNodeInfo.node.loc),
|
||||
data: {
|
||||
edge: extendableNodeInfo.edge,
|
||||
cross_loc: extendableNodeInfo.cross_loc
|
||||
},
|
||||
fixes: fixes
|
||||
}));
|
||||
});
|
||||
|
||||
return issues;
|
||||
return fixes;
|
||||
}
|
||||
|
||||
|
||||
function showReference(selection) {
|
||||
|
||||
@@ -162,16 +162,18 @@ export function validationCloseNodes(context) {
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [node.id, nearby.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-disconnect',
|
||||
title: t('issues.fix.move_points_apart.title')
|
||||
}),
|
||||
new validationIssueFix({
|
||||
icon: 'iD-icon-layers',
|
||||
title: t('issues.fix.use_different_layers_or_levels.title')
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-disconnect',
|
||||
title: t('issues.fix.move_points_apart.title')
|
||||
}),
|
||||
new validationIssueFix({
|
||||
icon: 'iD-icon-layers',
|
||||
title: t('issues.fix.use_different_layers_or_levels.title')
|
||||
})
|
||||
];
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,44 +359,6 @@ export function validationCrossingWays(context) {
|
||||
crossingTypeID += '_connectable';
|
||||
}
|
||||
|
||||
var fixes = [];
|
||||
if (connectionTags) {
|
||||
fixes.push(makeConnectWaysFix(connectionTags));
|
||||
}
|
||||
|
||||
var useFixIcon = 'iD-icon-layers';
|
||||
var useFixID;
|
||||
if (isCrossingIndoors) {
|
||||
useFixID = 'use_different_levels';
|
||||
} else if (isCrossingTunnels || isCrossingBridges) {
|
||||
useFixID = 'use_different_layers';
|
||||
// don't recommend bridges for waterways even though they're okay
|
||||
} else if ((allowsBridge(featureType1) && featureType1 !== 'waterway') ||
|
||||
(allowsBridge(featureType2) && featureType2 !== 'waterway')) {
|
||||
useFixID = 'use_bridge_or_tunnel';
|
||||
useFixIcon = 'maki-bridge';
|
||||
} else if (allowsTunnel(featureType1) || allowsTunnel(featureType2)) {
|
||||
useFixID = 'use_tunnel';
|
||||
} else {
|
||||
useFixID = 'use_different_layers';
|
||||
}
|
||||
if (useFixID === 'use_different_layers' ||
|
||||
featureType1 === 'building' ||
|
||||
featureType2 === 'building') {
|
||||
fixes.push(makeChangeLayerFix('higher'));
|
||||
fixes.push(makeChangeLayerFix('lower'));
|
||||
}
|
||||
if (useFixID !== 'use_different_layers') {
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: useFixIcon,
|
||||
title: t('issues.fix.' + useFixID + '.title')
|
||||
}));
|
||||
}
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-move',
|
||||
title: t('issues.fix.reposition_features.title')
|
||||
}));
|
||||
|
||||
return new validationIssue({
|
||||
type: type,
|
||||
subtype: subtype,
|
||||
@@ -427,7 +389,48 @@ export function validationCrossingWays(context) {
|
||||
// ensure the correct connection tags are added in the fix
|
||||
JSON.stringify(connectionTags),
|
||||
loc: crossing.crossPoint,
|
||||
fixes: fixes
|
||||
dynamicFixes: function() {
|
||||
var fixes = [];
|
||||
|
||||
if (connectionTags) {
|
||||
fixes.push(makeConnectWaysFix(connectionTags));
|
||||
}
|
||||
|
||||
var useFixIcon = 'iD-icon-layers';
|
||||
var useFixID;
|
||||
if (isCrossingIndoors) {
|
||||
useFixID = 'use_different_levels';
|
||||
} else if (isCrossingTunnels || isCrossingBridges) {
|
||||
useFixID = 'use_different_layers';
|
||||
// don't recommend bridges for waterways even though they're okay
|
||||
} else if ((allowsBridge(featureType1) && featureType1 !== 'waterway') ||
|
||||
(allowsBridge(featureType2) && featureType2 !== 'waterway')) {
|
||||
useFixID = 'use_bridge_or_tunnel';
|
||||
useFixIcon = 'maki-bridge';
|
||||
} else if (allowsTunnel(featureType1) || allowsTunnel(featureType2)) {
|
||||
useFixID = 'use_tunnel';
|
||||
} else {
|
||||
useFixID = 'use_different_layers';
|
||||
}
|
||||
if (useFixID === 'use_different_layers' ||
|
||||
featureType1 === 'building' ||
|
||||
featureType2 === 'building') {
|
||||
fixes.push(makeChangeLayerFix('higher'));
|
||||
fixes.push(makeChangeLayerFix('lower'));
|
||||
}
|
||||
if (useFixID !== 'use_different_layers') {
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: useFixIcon,
|
||||
title: t('issues.fix.' + useFixID + '.title')
|
||||
}));
|
||||
}
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-move',
|
||||
title: t('issues.fix.reposition_features.title')
|
||||
}));
|
||||
|
||||
return fixes;
|
||||
}
|
||||
});
|
||||
|
||||
function showReference(selection) {
|
||||
|
||||
@@ -18,44 +18,6 @@ export function validationDisconnectedWay() {
|
||||
var routingIslandWays = routingIslandForEntity(entity);
|
||||
if (!routingIslandWays) return [];
|
||||
|
||||
var fixes = [];
|
||||
|
||||
var isSingle = routingIslandWays.size === 1;
|
||||
|
||||
if (isSingle) {
|
||||
|
||||
if (entity.type === 'way' && !entity.isClosed()) {
|
||||
|
||||
var startFix = makeContinueDrawingFixIfAllowed(entity.first(), 'start');
|
||||
if (startFix) fixes.push(startFix);
|
||||
|
||||
var endFix = makeContinueDrawingFixIfAllowed(entity.last(), 'end');
|
||||
if (endFix) fixes.push(endFix);
|
||||
}
|
||||
if (!fixes.length) {
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_feature.title')
|
||||
}));
|
||||
}
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.delete_feature.title'),
|
||||
entityIds: [entity.id],
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_features.title')
|
||||
}));
|
||||
}
|
||||
|
||||
return [new validationIssue({
|
||||
type: type,
|
||||
subtype: 'highway',
|
||||
@@ -69,10 +31,54 @@ export function validationDisconnectedWay() {
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
|
||||
fixes: fixes
|
||||
dynamicFixes: makeFixes
|
||||
})];
|
||||
|
||||
|
||||
function makeFixes(context) {
|
||||
|
||||
var fixes = [];
|
||||
|
||||
var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
|
||||
|
||||
if (singleEntity) {
|
||||
|
||||
if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
|
||||
|
||||
var startFix = makeContinueDrawingFixIfAllowed(singleEntity.first(), 'start');
|
||||
if (startFix) fixes.push(startFix);
|
||||
|
||||
var endFix = makeContinueDrawingFixIfAllowed(singleEntity.last(), 'end');
|
||||
if (endFix) fixes.push(endFix);
|
||||
}
|
||||
if (!fixes.length) {
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_feature.title')
|
||||
}));
|
||||
}
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.delete_feature.title'),
|
||||
entityIds: [singleEntity.id],
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_features.title')
|
||||
}));
|
||||
}
|
||||
|
||||
return fixes;
|
||||
}
|
||||
|
||||
|
||||
function showReference(selection) {
|
||||
selection.selectAll('.issue-reference')
|
||||
.data([0])
|
||||
|
||||
@@ -27,11 +27,13 @@ export function validationHelpRequest(context) {
|
||||
var entity = context.hasEntity(this.entityIds[0]);
|
||||
return entity ? t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context) }) : '';
|
||||
},
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.address_the_concern.title')
|
||||
})
|
||||
],
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.address_the_concern.title')
|
||||
})
|
||||
];
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [entity.id]
|
||||
})];
|
||||
|
||||
@@ -144,35 +144,6 @@ export function validationImpossibleOneway() {
|
||||
if (connectedEndpointsOkay) return [];
|
||||
}
|
||||
|
||||
var fixes = [];
|
||||
|
||||
if (attachedOneways.length) {
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-reverse',
|
||||
title: t('issues.fix.reverse_feature.title'),
|
||||
entityIds: [way.id],
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
context.perform(actionReverse(id), t('operations.reverse.annotation'));
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (node.tags.noexit !== 'yes') {
|
||||
var useLeftContinue = (isFirst && textDirection === 'ltr') ||
|
||||
(!isFirst && textDirection === 'rtl');
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
|
||||
title: t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
|
||||
onClick: function(context) {
|
||||
var entityID = this.issue.entityIds[0];
|
||||
var vertexID = this.issue.entityIds[1];
|
||||
var way = context.entity(entityID);
|
||||
var vertex = context.entity(vertexID);
|
||||
continueDrawing(way, vertex, context);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
var placement = isFirst ? 'start' : 'end',
|
||||
messageID = wayType + '.',
|
||||
referenceID = wayType + '.';
|
||||
@@ -197,7 +168,39 @@ export function validationImpossibleOneway() {
|
||||
},
|
||||
reference: getReference(referenceID),
|
||||
entityIds: [way.id, node.id],
|
||||
fixes: fixes,
|
||||
dynamicFixes: function() {
|
||||
|
||||
var fixes = [];
|
||||
|
||||
if (attachedOneways.length) {
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-reverse',
|
||||
title: t('issues.fix.reverse_feature.title'),
|
||||
entityIds: [way.id],
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
context.perform(actionReverse(id), t('operations.reverse.annotation'));
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (node.tags.noexit !== 'yes') {
|
||||
var useLeftContinue = (isFirst && textDirection === 'ltr') ||
|
||||
(!isFirst && textDirection === 'rtl');
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
|
||||
title: t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
|
||||
onClick: function(context) {
|
||||
var entityID = this.issue.entityIds[0];
|
||||
var vertexID = this.issue.entityIds[1];
|
||||
var way = context.entity(entityID);
|
||||
var vertex = context.entity(vertexID);
|
||||
continueDrawing(way, vertex, context);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
return fixes;
|
||||
},
|
||||
loc: node.loc
|
||||
})];
|
||||
|
||||
|
||||
@@ -40,11 +40,13 @@ export function validationIncompatibleSource() {
|
||||
},
|
||||
reference: getReference(invalidSource.id),
|
||||
entityIds: [entity.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.remove_proprietary_data.title')
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.remove_proprietary_data.title')
|
||||
})
|
||||
];
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -72,37 +72,11 @@ export function validationMismatchedGeometry(context) {
|
||||
}
|
||||
}
|
||||
|
||||
function lineTaggedAsAreaIssue(entity, graph) {
|
||||
function lineTaggedAsAreaIssue(entity) {
|
||||
|
||||
var tagSuggestingArea = tagSuggestingLineIsArea(entity);
|
||||
if (!tagSuggestingArea) return null;
|
||||
|
||||
var fixes = [];
|
||||
|
||||
var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, graph);
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_endpoints.title'),
|
||||
onClick: connectEndsOnClick
|
||||
}));
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_tag.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
for (var key in tagSuggestingArea) {
|
||||
delete tags[key];
|
||||
}
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.remove_tag.annotation')
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
return new validationIssue({
|
||||
type: type,
|
||||
subtype: 'area_as_line',
|
||||
@@ -116,10 +90,38 @@ export function validationMismatchedGeometry(context) {
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [entity.id],
|
||||
hash: JSON.stringify(tagSuggestingArea) +
|
||||
// avoid stale "connect endpoints" fix
|
||||
(typeof connectEndsOnClick),
|
||||
fixes: fixes
|
||||
hash: JSON.stringify(tagSuggestingArea),
|
||||
dynamicFixes: function(context) {
|
||||
|
||||
var fixes = [];
|
||||
|
||||
var entity = context.entity(this.entityIds[0]);
|
||||
var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
title: t('issues.fix.connect_endpoints.title'),
|
||||
onClick: connectEndsOnClick
|
||||
}));
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_tag.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
for (var key in tagSuggestingArea) {
|
||||
delete tags[key];
|
||||
}
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.remove_tag.annotation')
|
||||
);
|
||||
}
|
||||
}));
|
||||
|
||||
return fixes;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -206,13 +208,15 @@ export function validationMismatchedGeometry(context) {
|
||||
.text(t('issues.point_as_vertex.reference'));
|
||||
},
|
||||
entityIds: [entity.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-extract',
|
||||
title: t('issues.fix.extract_point.title'),
|
||||
onClick: extractOnClick
|
||||
})
|
||||
],
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-extract',
|
||||
title: t('issues.fix.extract_point.title'),
|
||||
onClick: extractOnClick
|
||||
})
|
||||
];
|
||||
},
|
||||
hash: typeof extractOnClick, // avoid stale extraction fix
|
||||
});
|
||||
}
|
||||
@@ -223,7 +227,7 @@ export function validationMismatchedGeometry(context) {
|
||||
var validation = function checkMismatchedGeometry(entity, graph) {
|
||||
var issues = [
|
||||
vertexTaggedAsPointIssue(entity, graph),
|
||||
lineTaggedAsAreaIssue(entity, graph)
|
||||
lineTaggedAsAreaIssue(entity)
|
||||
];
|
||||
return issues.filter(Boolean);
|
||||
};
|
||||
|
||||
@@ -55,20 +55,22 @@ export function validationMissingRole() {
|
||||
member: member
|
||||
},
|
||||
hash: member.index.toString(),
|
||||
fixes: [
|
||||
makeAddRoleFix('inner'),
|
||||
makeAddRoleFix('outer'),
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_from_relation.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(
|
||||
actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
|
||||
t('operations.delete_member.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
makeAddRoleFix('inner'),
|
||||
makeAddRoleFix('outer'),
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_from_relation.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(
|
||||
actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
|
||||
t('operations.delete_member.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -62,37 +62,11 @@ export function validationMissingTag() {
|
||||
|
||||
if (!subtype) return [];
|
||||
|
||||
var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
|
||||
|
||||
var fixes = [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-icon-search',
|
||||
title: t('issues.fix.' + selectFixType + '.title'),
|
||||
onClick: function(context) {
|
||||
context.ui().sidebar.showPresetList();
|
||||
}
|
||||
})
|
||||
];
|
||||
|
||||
// can always delete if the user created it in the first place..
|
||||
var canDelete = (entity.version === undefined || entity.v !== undefined);
|
||||
fixes.push(
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.delete_feature.title'),
|
||||
onClick: function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
|
||||
var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
|
||||
|
||||
// can always delete if the user created it in the first place..
|
||||
var canDelete = (entity.version === undefined || entity.v !== undefined);
|
||||
var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
|
||||
|
||||
return [new validationIssue({
|
||||
@@ -107,7 +81,44 @@ export function validationMissingTag() {
|
||||
},
|
||||
reference: showReference,
|
||||
entityIds: [entity.id],
|
||||
fixes: fixes
|
||||
dynamicFixes: function(context) {
|
||||
|
||||
var fixes = [];
|
||||
|
||||
var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
|
||||
|
||||
fixes.push(new validationIssueFix({
|
||||
icon: 'iD-icon-search',
|
||||
title: t('issues.fix.' + selectFixType + '.title'),
|
||||
onClick: function(context) {
|
||||
context.ui().sidebar.showPresetList();
|
||||
}
|
||||
}));
|
||||
|
||||
var deleteOnClick;
|
||||
|
||||
var id = this.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
if (!operation.disabled()) {
|
||||
deleteOnClick = function(context) {
|
||||
var id = this.issue.entityIds[0];
|
||||
var operation = operationDelete([id], context);
|
||||
if (!operation.disabled()) {
|
||||
operation();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fixes.push(
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.delete_feature.title'),
|
||||
onClick: deleteOnClick
|
||||
})
|
||||
);
|
||||
|
||||
return fixes;
|
||||
}
|
||||
})];
|
||||
|
||||
|
||||
|
||||
@@ -144,15 +144,17 @@ export function validationOutdatedTags(context) {
|
||||
reference: showReference,
|
||||
entityIds: [entity.id],
|
||||
hash: JSON.stringify(tagDiff),
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
autoArgs: autoArgs,
|
||||
title: t('issues.fix.upgrade_tags.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.upgrade_tags.annotation'));
|
||||
}
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
autoArgs: autoArgs,
|
||||
title: t('issues.fix.upgrade_tags.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.upgrade_tags.annotation'));
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
})];
|
||||
|
||||
|
||||
@@ -245,15 +247,17 @@ export function validationOutdatedTags(context) {
|
||||
message: showMessage,
|
||||
reference: showReference,
|
||||
entityIds: [outerWay.id, multipolygon.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
autoArgs: [doUpgrade, t('issues.fix.move_tags.annotation')],
|
||||
title: t('issues.fix.move_tags.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.move_tags.annotation'));
|
||||
}
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
autoArgs: [doUpgrade, t('issues.fix.move_tags.annotation')],
|
||||
title: t('issues.fix.move_tags.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.move_tags.annotation'));
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
})];
|
||||
|
||||
|
||||
|
||||
@@ -63,15 +63,17 @@ export function validationPrivateData() {
|
||||
message: showMessage,
|
||||
reference: showReference,
|
||||
entityIds: [entity.id],
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.' + fixID + '.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.upgrade_tags.annotation'));
|
||||
}
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.' + fixID + '.title'),
|
||||
onClick: function(context) {
|
||||
context.perform(doUpgrade, t('issues.fix.upgrade_tags.annotation'));
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
})];
|
||||
|
||||
|
||||
|
||||
@@ -111,22 +111,24 @@ export function validationSuspiciousName() {
|
||||
reference: showReference,
|
||||
entityIds: [entityId],
|
||||
hash: nameKey + '=' + incorrectName,
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_the_name.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
delete tags[nameKey];
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.remove_mistaken_name.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-delete',
|
||||
title: t('issues.fix.remove_the_name.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
delete tags[nameKey];
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.remove_mistaken_name.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
function showReference(selection) {
|
||||
|
||||
@@ -80,38 +80,40 @@ export function validationUnsquareWay(context) {
|
||||
reference: showReference,
|
||||
entityIds: [entity.id],
|
||||
hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
|
||||
fixes: [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-orthogonalize',
|
||||
title: t('issues.fix.square_feature.title'),
|
||||
autoArgs: autoArgs,
|
||||
onClick: function(context, completionHandler) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
// use same degree threshold as for detection
|
||||
context.perform(
|
||||
actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
|
||||
t('operations.orthogonalize.annotation.area')
|
||||
);
|
||||
// run after the squaring transition (currently 150ms)
|
||||
window.setTimeout(function() { completionHandler(); }, 175);
|
||||
}
|
||||
}),
|
||||
/*
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.tag_as_unsquare.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
tags.nonsquare = 'yes';
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.tag_as_unsquare.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
*/
|
||||
]
|
||||
dynamicFixes: function() {
|
||||
return [
|
||||
new validationIssueFix({
|
||||
icon: 'iD-operation-orthogonalize',
|
||||
title: t('issues.fix.square_feature.title'),
|
||||
autoArgs: autoArgs,
|
||||
onClick: function(context, completionHandler) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
// use same degree threshold as for detection
|
||||
context.perform(
|
||||
actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
|
||||
t('operations.orthogonalize.annotation.area')
|
||||
);
|
||||
// run after the squaring transition (currently 150ms)
|
||||
window.setTimeout(function() { completionHandler(); }, 175);
|
||||
}
|
||||
}),
|
||||
/*
|
||||
new validationIssueFix({
|
||||
title: t('issues.fix.tag_as_unsquare.title'),
|
||||
onClick: function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
var entity = context.entity(entityId);
|
||||
var tags = Object.assign({}, entity.tags); // shallow copy
|
||||
tags.nonsquare = 'yes';
|
||||
context.perform(
|
||||
actionChangeTags(entityId, tags),
|
||||
t('issues.fix.tag_as_unsquare.annotation')
|
||||
);
|
||||
}
|
||||
})
|
||||
*/
|
||||
];
|
||||
}
|
||||
})];
|
||||
|
||||
function showReference(selection) {
|
||||
|
||||
@@ -166,8 +166,8 @@ describe('iD.validations.almost_junction', function () {
|
||||
expect(issue.data.cross_loc[0]).to.eql(22.42356);
|
||||
expect(issue.data.cross_loc[1]).to.eql(0);
|
||||
|
||||
expect(issue.fixes).to.have.lengthOf(2);
|
||||
issue.fixes[0].onClick(context);
|
||||
expect(issue.fixes(context)).to.have.lengthOf(3);
|
||||
issue.fixes(context)[0].onClick(context);
|
||||
issues = validate();
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
@@ -196,8 +196,8 @@ describe('iD.validations.almost_junction', function () {
|
||||
expect(issue.data.cross_loc[0]).to.eql(22.42356);
|
||||
expect(issue.data.cross_loc[1]).to.eql(0);
|
||||
|
||||
expect(issue.fixes).to.have.lengthOf(2);
|
||||
issue.fixes[1].onClick(context);
|
||||
expect(issue.fixes(context)).to.have.lengthOf(3);
|
||||
issue.fixes(context)[1].onClick(context);
|
||||
issues = validate();
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user