Add Downgrade operation to remove most tags from features but retain address and building tags instead of immediate deletion

This commit is contained in:
Quincy Morgan
2019-04-02 11:09:08 -04:00
parent d5a2ca131d
commit 8779e1a6dc
6 changed files with 188 additions and 3 deletions
+17
View File
@@ -149,6 +149,23 @@ en:
has_wikidata_tag:
single: This feature can't be deleted because it has a Wikidata tag.
multiple: These features can't be deleted because some have Wikidata tags.
downgrade:
title: Downgrade
description:
building_address: Remove all non-address and non-building tags.
building: Remove all non-building tags.
address: Remove all non-address tags.
annotation:
building:
single: Downgraded a feature to a basic building.
multiple: "Downgraded {n} features to basic buildings."
address:
single: Downgraded a feature to an address.
multiple: "Downgraded {n} features to addresses."
multiple: "Downgraded {n} features."
has_wikidata_tag:
single: This feature can't be downgraded because it has a Wikidata tag.
multiple: These features can't be downgraded because some have Wikidata tags.
add_member:
annotation: Added a member to a relation.
delete_member:
+23
View File
@@ -191,6 +191,29 @@
"multiple": "These features can't be deleted because some have Wikidata tags."
}
},
"downgrade": {
"title": "Downgrade",
"description": {
"building_address": "Remove all non-address and non-building tags.",
"building": "Remove all non-building tags.",
"address": "Remove all non-address tags."
},
"annotation": {
"building": {
"single": "Downgraded a feature to a basic building.",
"multiple": "Downgraded {n} features to basic buildings."
},
"address": {
"single": "Downgraded a feature to an address.",
"multiple": "Downgraded {n} features to addresses."
},
"multiple": "Downgraded {n} features."
},
"has_wikidata_tag": {
"single": "This feature can't be downgraded because it has a Wikidata tag.",
"multiple": "These features can't be downgraded because some have Wikidata tags."
}
},
"add_member": {
"annotation": "Added a member to a relation."
},
+7 -3
View File
@@ -241,15 +241,19 @@ export function modeSelect(context, selectedIDs) {
var operations = Object.values(Operations)
.map(function(o) { return o(selectedIDs, context); })
.filter(function(o) { return o.available() && o.id !== 'delete'; });
.filter(function(o) { return o.available() && o.id !== 'delete' && o.id !== 'downgrade'; });
var downgradeOperation = Operations.operationDowngrade(selectedIDs, context);
// don't allow delete if downgrade is available
var lastOperation = downgradeOperation.available() ? downgradeOperation : Operations.operationDelete(selectedIDs, context);
// deprecation warning - Radial Menu to be removed in iD v3
var isRadialMenu = context.storage('edit-menu-style') === 'radial';
if (isRadialMenu) {
operations = operations.slice(0,7);
operations.unshift(Operations.operationDelete(selectedIDs, context));
operations.unshift(lastOperation);
} else {
operations.push(Operations.operationDelete(selectedIDs, context));
operations.push(lastOperation);
}
operations.forEach(function(operation) {
+135
View File
@@ -0,0 +1,135 @@
import { actionChangeTags } from '../actions';
import { behaviorOperation } from '../behavior';
import { modeSelect } from '../modes';
import { t } from '../util/locale';
import { uiCmd } from '../ui';
export function operationDowngrade(selectedIDs, context) {
var affectedFeatureCount = 0;
var downgradeType;
setDowngradeTypeForEntityIDs();
var multi = affectedFeatureCount === 1 ? 'single' : 'multiple';
function setDowngradeTypeForEntityIDs() {
for (var i in selectedIDs) {
var entityID = selectedIDs[i];
var type = downgradeTypeForEntityID(entityID);
if (type) {
affectedFeatureCount += 1;
if (downgradeType && type !== downgradeType) {
downgradeType = 'building_address';
} else {
downgradeType = type;
}
}
}
}
function downgradeTypeForEntityID(entityID) {
var graph = context.graph();
var entity = graph.entity(entityID);
var preset = context.presets().match(entity, graph);
if (preset.isFallback()) return null;
if (entity.type === 'node' &&
preset.id !== 'address' &&
Object.keys(entity.tags).some(function(key) {
return key.match(/^addr:.{1,}/);
})) {
return 'address';
}
if (entity.geometry(graph) === 'area' &&
entity.tags.building &&
!preset.tags.building) {
return 'building';
}
return null;
}
var buildingKeysToKeep = ['architect', 'building', 'height', 'layer', 'source', 'type', 'wheelchair'];
var addressKeysToKeep = ['source'];
var operation = function () {
context.perform(function(graph) {
for (var i in selectedIDs) {
var entityID = selectedIDs[i];
var type = downgradeTypeForEntityID(entityID);
if (!type) continue;
var tags = Object.assign({}, graph.entity(entityID).tags); // shallow copy
for (var key in tags) {
if (type === 'address' && addressKeysToKeep.indexOf(key) !== -1) continue;
if (type === 'building') {
if (buildingKeysToKeep.indexOf(key) !== -1 ||
key.match(/^building:.{1,}/) ||
key.match(/^roof:.{1,}/)) continue;
}
// keep address tags for buildings too
if (key.match(/^addr:.{1,}/)) continue;
delete tags[key];
}
graph = actionChangeTags(entityID, tags)(graph);
}
return graph;
}, operation.annotation());
// refresh the select mode to enable the delete operation
context.enter(modeSelect(context, selectedIDs));
};
operation.available = function () {
return downgradeType;
};
operation.disabled = function () {
var reason;
if (selectedIDs.some(hasWikidataTag)) {
reason = 'has_wikidata_tag';
}
function hasWikidataTag(id) {
var entity = context.entity(id);
return entity.tags.wikidata && entity.tags.wikidata.trim().length > 0;
}
return reason;
};
operation.tooltip = function () {
var disable = operation.disabled();
return disable ?
t('operations.downgrade.' + disable + '.' + multi) :
t('operations.downgrade.description.' + downgradeType);
};
operation.annotation = function () {
var suffix;
if (downgradeType === 'building_address') {
suffix = 'multiple';
} else {
suffix = downgradeType + '.' + multi;
}
return t('operations.downgrade.annotation.' + suffix, { n: affectedFeatureCount});
};
operation.id = 'downgrade';
operation.keys = [uiCmd('⌘⌫'), uiCmd('⌘⌦'), uiCmd('⌦')];
operation.title = t('operations.downgrade.title');
operation.behavior = behaviorOperation(context).which(operation);
return operation;
}
+1
View File
@@ -2,6 +2,7 @@ export { operationCircularize } from './circularize';
export { operationContinue } from './continue';
export { operationDelete } from './delete';
export { operationDisconnect } from './disconnect';
export { operationDowngrade } from './downgrade';
export { operationMerge } from './merge';
export { operationMove } from './move';
export { operationOrthogonalize } from './orthogonalize';
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" width="20" height="20" viewBox="0 0 20 20">
<path d="M10,14.999 C10.453,15.014 10.695,14.905 10.949,14.663 C11.203,14.42 16.199,8.808 16.949,7.971 C17.398,7.47 17.338,6.716 16.813,6.286 C16.289,5.857 15.5,5.915 15.051,6.416 C11.367,10.524 10.683,11.236 10,11.999 C5.699,7.202 5.699,7.252 4.949,6.416 C4.5,5.915 3.711,5.857 3.187,6.286 C2.662,6.716 2.602,7.47 3.051,7.971 C8.301,13.826 8.766,14.342 9.051,14.663 C9.312,14.889 9.547,14.983 10,14.999 z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 731 B