Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions data/core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,9 @@ en:
same_value:
message: "{feature}: {tag1} and {tag2} have same value"
reference: 'The tags "{tag1}" and "{tag2}" have the same value. Only one tag should be set.'
lifecycle_prefix_conflict:
message: "{feature} has both {tag1} and {tag2}"
reference: 'The tags "{tag1}" and "{tag2}" contradict each other. Only one tag should be set.'
old_multipolygon:
message: "{multipolygon} has misplaced tags"
reference: "Multipolygons should be tagged on their relation, not their outer way."
Expand Down
12 changes: 12 additions & 0 deletions modules/osm/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,15 @@ export const osmUrlKeys = new Set([
'website:menu',
'post_office:website',
]);

export const osmLifecycleConflictPrefixes = new Set([
'demolished',
'removed',
'razed',
'destroyed',
'obliterated',
'dismantled',
'disused',
'abandoned',
'was',
]);
1 change: 1 addition & 0 deletions modules/validations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { validationOutdatedTags } from './outdated_tags';
export { validationPrivateData } from './private_data';
export { validationSuspiciousName } from './suspicious_name';
export { validationUnsquareWay } from './unsquare_way';
export { validationLifecyclePrefixConflict} from './lifecycle_prefix_conflict';
76 changes: 76 additions & 0 deletions modules/validations/lifecycle_prefix_conflict.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { actionChangeTags } from '../actions/change_tags';
import { t } from '../core/localizer';
import { utilDisplayLabel } from '../util/utilDisplayLabel';
import { validationIssue, validationIssueFix } from '../core/validation';
import { osmLifecycleConflictPrefixes } from '../osm/tags';

export function validationLifecyclePrefixConflict() {
const type = 'lifecycle_prefix_conflict';

const validation = function checkLifecyclePrefixConflict(entity) {
const pairsFound = [];

Object.keys(entity.tags).forEach((key) => {
const colonIndex = key.indexOf(':');
if (colonIndex === -1) return;

const prefix = key.slice(0, colonIndex);
const baseKey = key.slice(colonIndex + 1);

if (!osmLifecycleConflictPrefixes.has(prefix)) return;
if (!(baseKey in entity.tags)) return;

pairsFound.push([key, baseKey]);
});

return pairsFound.map((pair) => {
return new validationIssue({
type: type,
severity: 'error',
message: function(context) {
const entity = context.hasEntity(this.entityIds[0]);
return entity ? t.append('issues.lifecycle_prefix_conflict.message', {
feature: utilDisplayLabel(entity, context.graph()),
tag1: pair[0],
tag2: pair[1]
}) : '';
},
reference: (selection) => showReference(selection, pair),
entityIds: [entity.id],
dynamicFixes: () => pair.slice(0, 2).map((tagToRemove) => createIssueFix(tagToRemove))
});
});

function createIssueFix(tagToRemove) {
return new validationIssueFix({
icon: 'iD-operation-delete',
title: t.append('issues.fix.remove_named_tag.title', { tag: tagToRemove }),
onClick: function(context) {
const entityId = this.issue.entityIds[0];
const entity = context.entity(entityId);
const tags = Object.assign({}, entity.tags);
delete tags[tagToRemove];
context.perform(
actionChangeTags(entityId, tags),
t('issues.fix.remove_named_tag.annotation', { tag: tagToRemove })
);
}
});
}

function showReference(selection, pair) {
selection.selectAll('.issue-reference')
.data([0])
.enter()
.append('div')
.attr('class', 'issue-reference')
.call(t.append('issues.lifecycle_prefix_conflict.reference', {
tag1: pair[0],
tag2: pair[1]
}));
}
};

validation.type = type;
return validation;
}