Skip to content

Commit

Permalink
Add Downgrade operation to remove most tags from features but retain …
Browse files Browse the repository at this point in the history
…address and building tags instead of immediate deletion
  • Loading branch information
quincylvania committed Apr 2, 2019
1 parent d5a2ca1 commit 8779e1a
Show file tree
Hide file tree
Showing 6 changed files with 188 additions and 3 deletions.
17 changes: 17 additions & 0 deletions data/core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
23 changes: 23 additions & 0 deletions dist/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
},
Expand Down
10 changes: 7 additions & 3 deletions modules/modes/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
135 changes: 135 additions & 0 deletions modules/operations/downgrade.js
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions modules/operations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
5 changes: 5 additions & 0 deletions svg/iD-sprite/operations/operation-downgrade.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8779e1a

Please sign in to comment.