mirror of
https://github.com/FoggedLens/iD.git
synced 2026-02-13 01:02:58 +00:00
disable Move and Rotate operations if area < 80% contained in the viewport
see #542. Also included: 1. DRY up code for "% contained in" extent testing. 2. If action.disabled() returns a better reason, show that instead of the too_large one.
This commit is contained in:
@@ -120,6 +120,7 @@ en:
|
||||
area: Moved an area.
|
||||
multiple: Moved multiple objects.
|
||||
incomplete_relation: This feature can't be moved because it hasn't been fully downloaded.
|
||||
too_large: This can't be moved because not enough of it is currently visible.
|
||||
rotate:
|
||||
title: Rotate
|
||||
description: Rotate this object around its center point.
|
||||
@@ -127,6 +128,7 @@ en:
|
||||
annotation:
|
||||
line: Rotated a line.
|
||||
area: Rotated an area.
|
||||
too_large: This can't be rotated because not enough of it is currently visible.
|
||||
reverse:
|
||||
title: Reverse
|
||||
description: Make this line go in the opposite direction.
|
||||
|
||||
6
dist/locales/en.json
vendored
6
dist/locales/en.json
vendored
@@ -150,7 +150,8 @@
|
||||
"area": "Moved an area.",
|
||||
"multiple": "Moved multiple objects."
|
||||
},
|
||||
"incomplete_relation": "This feature can't be moved because it hasn't been fully downloaded."
|
||||
"incomplete_relation": "This feature can't be moved because it hasn't been fully downloaded.",
|
||||
"too_large": "This can't be moved because not enough of it is currently visible."
|
||||
},
|
||||
"rotate": {
|
||||
"title": "Rotate",
|
||||
@@ -159,7 +160,8 @@
|
||||
"annotation": {
|
||||
"line": "Rotated a line.",
|
||||
"area": "Rotated an area."
|
||||
}
|
||||
},
|
||||
"too_large": "This can't be rotated because not enough of it is currently visible."
|
||||
},
|
||||
"reverse": {
|
||||
"title": "Reverse",
|
||||
|
||||
@@ -57,6 +57,18 @@ _.extend(iD.geo.Extent.prototype, {
|
||||
Math.min(obj[1][1], this[1][1])]);
|
||||
},
|
||||
|
||||
percentContainedIn: function(obj) {
|
||||
if (!(obj instanceof iD.geo.Extent)) obj = new iD.geo.Extent(obj);
|
||||
var a1 = this.intersection(obj).area(),
|
||||
a2 = this.area();
|
||||
|
||||
if (a1 === Infinity || a2 === Infinity || a1 === 0 || a2 === 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return a1 / a2;
|
||||
}
|
||||
},
|
||||
|
||||
padByMeters: function(meters) {
|
||||
var dLat = iD.geo.metersToLat(meters),
|
||||
dLon = iD.geo.metersToLon(meters, this.center()[1]);
|
||||
@@ -68,4 +80,5 @@ _.extend(iD.geo.Extent.prototype, {
|
||||
toParam: function() {
|
||||
return [this[0][0], this[0][1], this[1][0], this[1][1]].join(',');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
iD.operations.Circularize = function(selectedIDs, context) {
|
||||
var entityId = selectedIDs[0],
|
||||
entity = context.entity(entityId),
|
||||
extent = entity.extent(context.graph()),
|
||||
geometry = context.geometry(entityId),
|
||||
action = iD.actions.Circularize(entityId, context.projection);
|
||||
|
||||
@@ -9,24 +11,17 @@ iD.operations.Circularize = function(selectedIDs, context) {
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var entity = context.entity(entityId);
|
||||
return selectedIDs.length === 1 &&
|
||||
entity.type === 'way' &&
|
||||
_.uniq(entity.nodes).length > 1;
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
var way = context.entity(entityId),
|
||||
wayExtent = way.extent(context.graph()),
|
||||
mapExtent = context.extent(),
|
||||
intersection = mapExtent.intersection(wayExtent),
|
||||
pctVisible = intersection.area() / wayExtent.area();
|
||||
|
||||
if (pctVisible < 0.8) {
|
||||
return 'too_large';
|
||||
} else {
|
||||
return action.disabled(context.graph());
|
||||
var reason;
|
||||
if (extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
reason = 'too_large';
|
||||
}
|
||||
return action.disabled(context.graph()) || reason;
|
||||
};
|
||||
|
||||
operation.tooltip = function() {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
iD.operations.Move = function(selectedIDs, context) {
|
||||
var extent = selectedIDs.reduce(function(extent, id) {
|
||||
return extent.extend(context.entity(id).extent(context.graph()));
|
||||
}, iD.geo.Extent());
|
||||
|
||||
var operation = function() {
|
||||
context.enter(iD.modes.Move(context, selectedIDs));
|
||||
};
|
||||
@@ -9,8 +13,11 @@ iD.operations.Move = function(selectedIDs, context) {
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
return iD.actions.Move(selectedIDs)
|
||||
.disabled(context.graph());
|
||||
var reason;
|
||||
if (extent.area() && extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
reason = 'too_large';
|
||||
}
|
||||
return iD.actions.Move(selectedIDs).disabled(context.graph()) || reason;
|
||||
};
|
||||
|
||||
operation.tooltip = function() {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
iD.operations.Orthogonalize = function(selectedIDs, context) {
|
||||
var entityId = selectedIDs[0],
|
||||
entity = context.entity(entityId),
|
||||
extent = entity.extent(context.graph()),
|
||||
geometry = context.geometry(entityId),
|
||||
action = iD.actions.Orthogonalize(entityId, context.projection);
|
||||
|
||||
function operation() {
|
||||
var operation = function() {
|
||||
var annotation = t('operations.orthogonalize.annotation.' + geometry);
|
||||
context.perform(action, annotation);
|
||||
}
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var entity = context.entity(entityId);
|
||||
return selectedIDs.length === 1 &&
|
||||
entity.type === 'way' &&
|
||||
entity.isClosed() &&
|
||||
@@ -17,17 +18,11 @@ iD.operations.Orthogonalize = function(selectedIDs, context) {
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
var way = context.entity(entityId),
|
||||
wayExtent = way.extent(context.graph()),
|
||||
mapExtent = context.extent(),
|
||||
intersection = mapExtent.intersection(wayExtent),
|
||||
pctVisible = intersection.area() / wayExtent.area();
|
||||
|
||||
if (pctVisible < 0.8) {
|
||||
return 'too_large';
|
||||
} else {
|
||||
return action.disabled(context.graph());
|
||||
var reason;
|
||||
if (extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
reason = 'too_large';
|
||||
}
|
||||
return action.disabled(context.graph()) || reason;
|
||||
};
|
||||
|
||||
operation.tooltip = function() {
|
||||
|
||||
@@ -1,31 +1,37 @@
|
||||
iD.operations.Rotate = function(selectedIDs, context) {
|
||||
var entityId = selectedIDs[0];
|
||||
var entityId = selectedIDs[0],
|
||||
entity = context.entity(entityId),
|
||||
extent = entity.extent(context.graph()),
|
||||
geometry = context.geometry(entityId);
|
||||
|
||||
var operation = function() {
|
||||
context.enter(iD.modes.RotateWay(context, entityId));
|
||||
};
|
||||
|
||||
operation.available = function() {
|
||||
var graph = context.graph(),
|
||||
entity = graph.entity(entityId);
|
||||
|
||||
if (selectedIDs.length !== 1 ||
|
||||
entity.type !== 'way')
|
||||
if (selectedIDs.length !== 1 || entity.type !== 'way')
|
||||
return false;
|
||||
if (context.geometry(entityId) === 'area')
|
||||
if (geometry === 'area')
|
||||
return true;
|
||||
if (entity.isClosed() &&
|
||||
graph.parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
|
||||
context.graph().parentRelations(entity).some(function(r) { return r.isMultipolygon(); }))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
operation.disabled = function() {
|
||||
return false;
|
||||
if (extent.percentContainedIn(context.extent()) < 0.8) {
|
||||
return 'too_large';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
operation.tooltip = function() {
|
||||
return t('operations.rotate.description');
|
||||
var disable = operation.disabled();
|
||||
return disable ?
|
||||
t('operations.rotate.' + disable) :
|
||||
t('operations.rotate.description');
|
||||
};
|
||||
|
||||
operation.id = 'rotate';
|
||||
|
||||
@@ -158,4 +158,29 @@ describe("iD.geo.Extent", function () {
|
||||
expect(b.intersection(a)).to.eql(iD.geo.Extent([1, 1], [2, 2]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("#percentContainedIn", function () {
|
||||
it("returns a 0 if self does not intersect other", function () {
|
||||
var a = iD.geo.Extent([0, 0], [1, 1]),
|
||||
b = iD.geo.Extent([0, 3], [4, 1]);
|
||||
expect(a.percentContainedIn(b)).to.eql(0);
|
||||
expect(b.percentContainedIn(a)).to.eql(0);
|
||||
});
|
||||
|
||||
it("returns the percent contained of self with other (1)", function () {
|
||||
var a = iD.geo.Extent([0, 0], [2, 1]),
|
||||
b = iD.geo.Extent([1, 0], [3, 1]);
|
||||
expect(a.percentContainedIn(b)).to.eql(0.5);
|
||||
expect(b.percentContainedIn(a)).to.eql(0.5);
|
||||
});
|
||||
|
||||
it("returns the percent contained of self with other (2)", function () {
|
||||
var a = iD.geo.Extent([0, 0], [4, 1]),
|
||||
b = iD.geo.Extent([3, 0], [4, 2]);
|
||||
expect(a.percentContainedIn(b)).to.eql(0.25);
|
||||
expect(b.percentContainedIn(a)).to.eql(0.5);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user