mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
Enable the Extract operation for multiple selected extractable features (close #7600)
Allow extracting vertices that are `via` or `location_hint` relation members
This commit is contained in:
+10
-7
@@ -407,21 +407,24 @@ en:
|
||||
description:
|
||||
vertex:
|
||||
single: Extract this point from its parent lines/areas.
|
||||
multiple: Extract these points from their parent features.
|
||||
line:
|
||||
single: Extract a point from this line.
|
||||
multiple: Extract points from these lines.
|
||||
area:
|
||||
single: Extract a point from this area.
|
||||
multiple: Extract points from these areas.
|
||||
feature:
|
||||
multiple: Extract points from these features.
|
||||
annotation:
|
||||
single: Extracted a point.
|
||||
multiple: "Extracted {n} points."
|
||||
too_large:
|
||||
area:
|
||||
single: A point can't be extracted from this area because not enough of it is currently visible.
|
||||
restriction:
|
||||
vertex:
|
||||
single: "This point can't be extracted because it would damage a \"{relation}\" relation."
|
||||
single: A point can't be extracted because not enough of this feature is visible.
|
||||
multiple: Points can't be extracted because not enough of these features are visible.
|
||||
connected_to_hidden:
|
||||
vertex:
|
||||
single: This point can't be extracted because it is connected to a hidden feature.
|
||||
single: This point can't be extracted because it is connected to a hidden feature.
|
||||
multiple: Points can't be extracted from these features because some are connected to hidden features.
|
||||
restriction:
|
||||
controls:
|
||||
distance: Distance
|
||||
|
||||
Vendored
+15
-15
@@ -531,32 +531,32 @@
|
||||
"key": "E",
|
||||
"description": {
|
||||
"vertex": {
|
||||
"single": "Extract this point from its parent lines/areas."
|
||||
"single": "Extract this point from its parent lines/areas.",
|
||||
"multiple": "Extract these points from their parent features."
|
||||
},
|
||||
"line": {
|
||||
"single": "Extract a point from this line."
|
||||
"single": "Extract a point from this line.",
|
||||
"multiple": "Extract points from these lines."
|
||||
},
|
||||
"area": {
|
||||
"single": "Extract a point from this area."
|
||||
"single": "Extract a point from this area.",
|
||||
"multiple": "Extract points from these areas."
|
||||
},
|
||||
"feature": {
|
||||
"multiple": "Extract points from these features."
|
||||
}
|
||||
},
|
||||
"annotation": {
|
||||
"single": "Extracted a point."
|
||||
"single": "Extracted a point.",
|
||||
"multiple": "Extracted {n} points."
|
||||
},
|
||||
"too_large": {
|
||||
"area": {
|
||||
"single": "A point can't be extracted from this area because not enough of it is currently visible."
|
||||
}
|
||||
},
|
||||
"restriction": {
|
||||
"vertex": {
|
||||
"single": "This point can't be extracted because it would damage a \"{relation}\" relation."
|
||||
}
|
||||
"single": "A point can't be extracted because not enough of this feature is visible.",
|
||||
"multiple": "Points can't be extracted because not enough of these features are visible."
|
||||
},
|
||||
"connected_to_hidden": {
|
||||
"vertex": {
|
||||
"single": "This point can't be extracted because it is connected to a hidden feature."
|
||||
}
|
||||
"single": "This point can't be extracted because it is connected to a hidden feature.",
|
||||
"multiple": "Points can't be extracted from these features because some are connected to hidden features."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -99,27 +99,5 @@ export function actionExtract(entityID) {
|
||||
return extractedNodeID;
|
||||
};
|
||||
|
||||
action.disabled = function(graph) {
|
||||
var entity = graph.entity(entityID);
|
||||
|
||||
if (entity.type === 'node') {
|
||||
var parentRels = graph.parentRelations(entity);
|
||||
for (var i = 0; i < parentRels.length; i++) {
|
||||
var relation = parentRels[i];
|
||||
if (!relation.hasFromViaTo()) continue;
|
||||
|
||||
for (var j = 0; j < relation.members.length; j++) {
|
||||
var m = relation.members[j];
|
||||
if (m.id === entityID && (m.role === 'via' || m.role === 'location_hint')) {
|
||||
return 'restriction';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
@@ -3,47 +3,65 @@ import { behaviorOperation } from '../behavior/operation';
|
||||
import { modeSelect } from '../modes/select';
|
||||
import { t } from '../core/localizer';
|
||||
import { presetManager } from '../presets';
|
||||
import { utilArrayUniq } from '../util/array';
|
||||
|
||||
export function operationExtract(context, selectedIDs) {
|
||||
var entityID = selectedIDs.length && selectedIDs[0];
|
||||
var action = actionExtract(entityID);
|
||||
|
||||
var geometry = entityID && context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
|
||||
var extent = (geometry === 'area' || geometry === 'line') && context.graph().entity(entityID).extent(context.graph());
|
||||
var _amount = selectedIDs.length === 1 ? 'single' : 'multiple';
|
||||
var _geometries = utilArrayUniq(selectedIDs.map(function(entityID) {
|
||||
return context.graph().hasEntity(entityID) && context.graph().geometry(entityID);
|
||||
}).filter(Boolean));
|
||||
var _geometryID = _geometries.length === 1 ? _geometries[0] : 'feature';
|
||||
|
||||
var _extent;
|
||||
var _actions = selectedIDs.map(function(entityID) {
|
||||
var graph = context.graph();
|
||||
var entity = graph.hasEntity(entityID);
|
||||
if (!entity || !entity.hasInterestingTags()) return;
|
||||
|
||||
if (entity.type === 'node' && graph.parentWays(entity).length === 0) return;
|
||||
|
||||
var geometry = graph.geometry(entityID);
|
||||
if (geometry === 'area' || geometry === 'line') {
|
||||
var preset = presetManager.match(entity, graph);
|
||||
// only allow extraction from ways/multipolygons if the preset supports points
|
||||
if (preset.geometry.indexOf('point') === -1) return;
|
||||
}
|
||||
|
||||
_extent = _extent ? _extent.extend(entity.extent(graph)) : entity.extent(graph);
|
||||
|
||||
return actionExtract(entityID);
|
||||
}).filter(Boolean);
|
||||
|
||||
|
||||
var operation = function () {
|
||||
context.perform(action, operation.annotation()); // do the extract
|
||||
context.enter(modeSelect(context, [action.getExtractedNodeID()]));
|
||||
var combinedAction = function(graph) {
|
||||
_actions.forEach(function(action) {
|
||||
graph = action(graph);
|
||||
});
|
||||
return graph;
|
||||
};
|
||||
context.perform(combinedAction, operation.annotation()); // do the extract
|
||||
|
||||
var extractedNodeIDs = _actions.map(function(action) {
|
||||
return action.getExtractedNodeID();
|
||||
});
|
||||
context.enter(modeSelect(context, extractedNodeIDs));
|
||||
};
|
||||
|
||||
|
||||
operation.available = function () {
|
||||
if (selectedIDs.length !== 1) return false;
|
||||
|
||||
var graph = context.graph();
|
||||
var entity = graph.hasEntity(entityID);
|
||||
if (!entity) return false;
|
||||
|
||||
if (!entity.hasInterestingTags()) return false;
|
||||
|
||||
if (geometry === 'area' || geometry === 'line') {
|
||||
var preset = presetManager.match(entity, graph);
|
||||
// only allow extraction from ways/multipolygons if the preset supports points
|
||||
return preset.geometry.indexOf('point') !== -1;
|
||||
}
|
||||
|
||||
return entity.type === 'node' && graph.parentWays(entity).length > 0;
|
||||
return _actions.length && selectedIDs.length === _actions.length;
|
||||
};
|
||||
|
||||
|
||||
operation.disabled = function () {
|
||||
var reason = action.disabled(context.graph());
|
||||
if (reason) {
|
||||
return reason;
|
||||
} else if (geometry === 'vertex' && selectedIDs.some(context.hasHiddenConnections)) {
|
||||
|
||||
if (selectedIDs.some(function(entityID) {
|
||||
return context.graph().geometry(entityID) === 'vertex' && context.hasHiddenConnections(entityID);
|
||||
})) {
|
||||
return 'connected_to_hidden';
|
||||
} else if (extent && extent.area() && extent.percentContainedIn(context.map().extent()) < 0.8) {
|
||||
} else if (_extent && _extent.area() && _extent.percentContainedIn(context.map().extent()) < 0.8) {
|
||||
return 'too_large';
|
||||
}
|
||||
|
||||
@@ -54,16 +72,15 @@ export function operationExtract(context, selectedIDs) {
|
||||
operation.tooltip = function () {
|
||||
var disableReason = operation.disabled();
|
||||
if (disableReason) {
|
||||
return t('operations.extract.' + disableReason + '.' + geometry + '.single',
|
||||
{ relation: presetManager.item('type/restriction').name() });
|
||||
return t('operations.extract.' + disableReason + '.' + _amount);
|
||||
} else {
|
||||
return t('operations.extract.description.' + geometry + '.single');
|
||||
return t('operations.extract.description.' + _geometryID + '.' + _amount);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
operation.annotation = function () {
|
||||
return t('operations.extract.annotation.single');
|
||||
return t('operations.extract.annotation.' + _amount, { n: selectedIDs.length });
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -201,8 +201,7 @@ export function validationMismatchedGeometry() {
|
||||
var entityId = this.entityIds[0];
|
||||
|
||||
var extractOnClick = null;
|
||||
if (!context.hasHiddenConnections(entityId) &&
|
||||
!actionExtract(entityId).disabled(context.graph())) {
|
||||
if (!context.hasHiddenConnections(entityId)) {
|
||||
|
||||
extractOnClick = function(context) {
|
||||
var entityId = this.issue.entityIds[0];
|
||||
|
||||
@@ -41,11 +41,6 @@ describe('iD.operationExtract', function () {
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for two selected ids', function () {
|
||||
var result = iD.operationExtract(fakeContext, ['a', 'b']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('is not available for unknown selected id', function () {
|
||||
var result = iD.operationExtract(fakeContext, ['z']).available();
|
||||
expect(result).to.be.not.ok;
|
||||
@@ -85,6 +80,11 @@ describe('iD.operationExtract', function () {
|
||||
var result = iD.operationExtract(fakeContext, ['b']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
|
||||
it('is available for two selected nodes with tags and parent ways', function () {
|
||||
var result = iD.operationExtract(fakeContext, ['a', 'b']).available();
|
||||
expect(result).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@ describe('iD.operationExtract', function () {
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('returns not-enabled for via node in restriction', function () {
|
||||
it('returns enabled for via node in restriction', function () {
|
||||
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
|
||||
// from & to roles are only appropriate for Ways
|
||||
graph = iD.coreGraph([
|
||||
@@ -134,10 +134,10 @@ describe('iD.operationExtract', function () {
|
||||
})
|
||||
]);
|
||||
var result = iD.operationExtract(fakeContext, ['d']).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
|
||||
it('returns not-enabled for location_hint node in restriction', function () {
|
||||
it('returns enabled for location_hint node in restriction', function () {
|
||||
// https://wiki.openstreetmap.org/wiki/Relation:restriction indicates that
|
||||
// from & to roles are only appropriate for Ways
|
||||
graph = iD.coreGraph([
|
||||
@@ -160,7 +160,7 @@ describe('iD.operationExtract', function () {
|
||||
})
|
||||
]);
|
||||
var result = iD.operationExtract(fakeContext, ['d']).disabled();
|
||||
expect(result).to.eql('restriction');
|
||||
expect(result).to.be.not.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user