diff --git a/data/core.yaml b/data/core.yaml
index d4e3aed3b..0522b3d21 100644
--- a/data/core.yaml
+++ b/data/core.yaml
@@ -119,12 +119,16 @@ en:
not_downloaded: This can't be made square because parts of it have not yet been downloaded.
straighten:
title: Straighten
- description: Straighten this line.
+ description:
+ points: Straighten these points.
+ line: Straighten this line.
key: S
- annotation: Straightened a line.
+ annotation:
+ points: Straightened several points.
+ line: Straightened a line.
too_bendy: This can't be straightened because it bends too much.
- connected_to_hidden: This line can't be straightened because it is connected to a hidden feature.
- not_downloaded: This line can't be straightened because parts of it have not yet been downloaded.
+ connected_to_hidden: This can't be straightened because it is connected to a hidden feature.
+ not_downloaded: This can't be straightened because parts of it have not yet been downloaded.
delete:
title: Delete
description:
diff --git a/dist/locales/en.json b/dist/locales/en.json
index ee0177ffb..5ed75e435 100644
--- a/dist/locales/en.json
+++ b/dist/locales/en.json
@@ -152,12 +152,18 @@
},
"straighten": {
"title": "Straighten",
- "description": "Straighten this line.",
+ "description": {
+ "points": "Straighten these points.",
+ "line": "Straighten this line."
+ },
"key": "S",
- "annotation": "Straightened a line.",
+ "annotation": {
+ "points": "Straightened several points.",
+ "line": "Straightened a line."
+ },
"too_bendy": "This can't be straightened because it bends too much.",
- "connected_to_hidden": "This line can't be straightened because it is connected to a hidden feature.",
- "not_downloaded": "This line can't be straightened because parts of it have not yet been downloaded."
+ "connected_to_hidden": "This can't be straightened because it is connected to a hidden feature.",
+ "not_downloaded": "This can't be straightened because parts of it have not yet been downloaded."
},
"delete": {
"title": "Delete",
diff --git a/modules/actions/index.js b/modules/actions/index.js
index b56fbbb09..dfdd1c7e6 100644
--- a/modules/actions/index.js
+++ b/modules/actions/index.js
@@ -31,7 +31,8 @@ export { actionReverse } from './reverse';
export { actionRevert } from './revert';
export { actionRotate } from './rotate';
export { actionSplit } from './split';
-export { actionStraighten } from './straighten';
+export { actionStraightenNodes } from './straighten_nodes';
+export { actionStraightenWay } from './straighten_way';
export { actionUnrestrictTurn } from './unrestrict_turn';
export { actionReflect } from './reflect.js';
export { actionUpgradeTags } from './upgrade_tags';
diff --git a/modules/actions/reflect.js b/modules/actions/reflect.js
index 5df28d557..16c35669b 100644
--- a/modules/actions/reflect.js
+++ b/modules/actions/reflect.js
@@ -1,50 +1,10 @@
-import {
- polygonHull as d3_polygonHull,
- polygonCentroid as d3_polygonCentroid
-} from 'd3-polygon';
-
-import { geoExtent, geoRotate, geoVecInterp, geoVecLength } from '../geo';
+import { geoGetSmallestSurroundingRectangle, geoVecInterp, geoVecLength } from '../geo';
import { utilGetAllNodes } from '../util';
/* Reflect the given area around its axis of symmetry */
export function actionReflect(reflectIds, projection) {
- var useLongAxis = true;
-
-
- // http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
- // http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
- function getSmallestSurroundingRectangle(graph, nodes) {
- var points = nodes.map(function(n) { return projection(n.loc); });
- var hull = d3_polygonHull(points);
- var centroid = d3_polygonCentroid(hull);
- var minArea = Infinity;
- var ssrExtent = [];
- var ssrAngle = 0;
- var c1 = hull[0];
-
- for (var i = 0; i <= hull.length - 1; i++) {
- var c2 = (i === hull.length - 1) ? hull[0] : hull[i + 1];
- var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
- var poly = geoRotate(hull, -angle, centroid);
- var extent = poly.reduce(function(extent, point) {
- return extent.extend(geoExtent(point));
- }, geoExtent());
-
- var area = extent.area();
- if (area < minArea) {
- minArea = area;
- ssrExtent = extent;
- ssrAngle = angle;
- }
- c1 = c2;
- }
-
- return {
- poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
- angle: ssrAngle
- };
- }
+ var _useLongAxis = true;
var action = function(graph, t) {
@@ -52,7 +12,8 @@ export function actionReflect(reflectIds, projection) {
t = Math.min(Math.max(+t, 0), 1);
var nodes = utilGetAllNodes(reflectIds, graph);
- var ssr = getSmallestSurroundingRectangle(graph, nodes);
+ var points = nodes.map(function(n) { return projection(n.loc); });
+ var ssr = geoGetSmallestSurroundingRectangle(points);
// Choose line pq = axis of symmetry.
// The shape's surrounding rectangle has 2 axes of symmetry.
@@ -64,7 +25,7 @@ export function actionReflect(reflectIds, projection) {
var p, q;
var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
- if ((useLongAxis && isLong) || (!useLongAxis && !isLong)) {
+ if ((_useLongAxis && isLong) || (!_useLongAxis && !isLong)) {
p = p1;
q = q1;
} else {
@@ -95,8 +56,8 @@ export function actionReflect(reflectIds, projection) {
action.useLongAxis = function(val) {
- if (!arguments.length) return useLongAxis;
- useLongAxis = val;
+ if (!arguments.length) return _useLongAxis;
+ _useLongAxis = val;
return action;
};
diff --git a/modules/actions/straighten_nodes.js b/modules/actions/straighten_nodes.js
new file mode 100644
index 000000000..45e4c5615
--- /dev/null
+++ b/modules/actions/straighten_nodes.js
@@ -0,0 +1,61 @@
+import { geoGetSmallestSurroundingRectangle, geoVecDot, geoVecLength, geoVecInterp } from '../geo';
+
+
+/* Align nodes along their common axis */
+export function actionStraightenNodes(nodeIDs, projection) {
+
+ function positionAlongWay(a, o, b) {
+ return geoVecDot(a, b, o) / geoVecDot(b, b, o);
+ }
+
+
+ var action = function(graph, t) {
+ if (t === null || !isFinite(t)) t = 1;
+ t = Math.min(Math.max(+t, 0), 1);
+
+ var nodes = nodeIDs.map(function(id) { return graph.entity(id); });
+ var points = nodes.map(function(n) { return projection(n.loc); });
+ var ssr = geoGetSmallestSurroundingRectangle(points);
+
+ // Choose line pq = axis of symmetry.
+ // The shape's surrounding rectangle has 2 axes of symmetry.
+ // Snap points to the long axis
+ var p1 = [(ssr.poly[0][0] + ssr.poly[1][0]) / 2, (ssr.poly[0][1] + ssr.poly[1][1]) / 2 ];
+ var q1 = [(ssr.poly[2][0] + ssr.poly[3][0]) / 2, (ssr.poly[2][1] + ssr.poly[3][1]) / 2 ];
+ var p2 = [(ssr.poly[3][0] + ssr.poly[4][0]) / 2, (ssr.poly[3][1] + ssr.poly[4][1]) / 2 ];
+ var q2 = [(ssr.poly[1][0] + ssr.poly[2][0]) / 2, (ssr.poly[1][1] + ssr.poly[2][1]) / 2 ];
+ var p, q;
+
+ var isLong = (geoVecLength(p1, q1) > geoVecLength(p2, q2));
+ if (isLong) {
+ p = p1;
+ q = q1;
+ } else {
+ p = p2;
+ q = q2;
+ }
+
+ // Move points onto line pq
+ for (var i = 0; i < points.length; i++) {
+ var node = nodes[i];
+ var point = points[i];
+ var u = positionAlongWay(point, p, q);
+ var point2 = geoVecInterp(p, q, u);
+ var loc2 = projection.invert(point2);
+ graph = graph.replace(node.move(geoVecInterp(node.loc, loc2, t)));
+ }
+
+ return graph;
+ };
+
+
+ action.disabled = function() {
+ return false;
+ };
+
+
+ action.transitionable = true;
+
+
+ return action;
+}
diff --git a/modules/actions/straighten.js b/modules/actions/straighten_way.js
similarity index 98%
rename from modules/actions/straighten.js
rename to modules/actions/straighten_way.js
index f4237d213..b317d8606 100644
--- a/modules/actions/straighten.js
+++ b/modules/actions/straighten_way.js
@@ -6,7 +6,7 @@ import { utilArrayDifference } from '../util';
/*
* Based on https://github.com/openstreetmap/potlatch2/net/systemeD/potlatch2/tools/Straighten.as
*/
-export function actionStraighten(selectedIDs, projection) {
+export function actionStraightenWay(selectedIDs, projection) {
function positionAlongWay(a, o, b) {
return geoVecDot(a, b, o) / geoVecDot(b, b, o);
diff --git a/modules/geo/geom.js b/modules/geo/geom.js
index 7c5fb789f..ec5ccb576 100644
--- a/modules/geo/geom.js
+++ b/modules/geo/geom.js
@@ -1,11 +1,13 @@
import {
- geoVecAngle,
- geoVecCross,
- geoVecDot,
- geoVecEqual,
- geoVecInterp,
- geoVecLength,
- geoVecSubtract
+ polygonHull as d3_polygonHull,
+ polygonCentroid as d3_polygonCentroid
+} from 'd3-polygon';
+
+import { geoExtent } from './extent.js';
+
+import {
+ geoVecAngle, geoVecCross, geoVecDot, geoVecEqual,
+ geoVecInterp, geoVecLength, geoVecSubtract
} from './vector.js';
@@ -15,11 +17,13 @@ export function geoAngle(a, b, projection) {
return geoVecAngle(projection(a.loc), projection(b.loc));
}
+
export function geoEdgeEqual(a, b) {
return (a[0] === b[0] && a[1] === b[1]) ||
(a[0] === b[1] && a[1] === b[0]);
}
+
// Rotate all points counterclockwise around a pivot point by given angle
export function geoRotate(points, angle, around) {
return points.map(function(point) {
@@ -272,6 +276,40 @@ export function geoPolygonIntersectsPolygon(outer, inner, checkSegments) {
}
+// http://gis.stackexchange.com/questions/22895/finding-minimum-area-rectangle-for-given-points
+// http://gis.stackexchange.com/questions/3739/generalisation-strategies-for-building-outlines/3756#3756
+export function geoGetSmallestSurroundingRectangle(points) {
+ var hull = d3_polygonHull(points);
+ var centroid = d3_polygonCentroid(hull);
+ var minArea = Infinity;
+ var ssrExtent = [];
+ var ssrAngle = 0;
+ var c1 = hull[0];
+
+ for (var i = 0; i <= hull.length - 1; i++) {
+ var c2 = (i === hull.length - 1) ? hull[0] : hull[i + 1];
+ var angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]);
+ var poly = geoRotate(hull, -angle, centroid);
+ var extent = poly.reduce(function(extent, point) {
+ return extent.extend(geoExtent(point));
+ }, geoExtent());
+
+ var area = extent.area();
+ if (area < minArea) {
+ minArea = area;
+ ssrExtent = extent;
+ ssrAngle = angle;
+ }
+ c1 = c2;
+ }
+
+ return {
+ poly: geoRotate(ssrExtent.polygon(), ssrAngle, centroid),
+ angle: ssrAngle
+ };
+}
+
+
export function geoPathLength(path) {
var length = 0;
for (var i = 0; i < path.length - 1; i++) {
diff --git a/modules/geo/index.js b/modules/geo/index.js
index 90c0b99ae..4d62211aa 100644
--- a/modules/geo/index.js
+++ b/modules/geo/index.js
@@ -14,6 +14,7 @@ export { geoZoomToScale } from './geo.js';
export { geoAngle } from './geom.js';
export { geoChooseEdge } from './geom.js';
export { geoEdgeEqual } from './geom.js';
+export { geoGetSmallestSurroundingRectangle } from './geom.js';
export { geoHasLineIntersections } from './geom.js';
export { geoHasSelfIntersections } from './geom.js';
export { geoRotate } from './geom.js';
diff --git a/modules/operations/straighten.js b/modules/operations/straighten.js
index 046e93a28..3aa0ea772 100644
--- a/modules/operations/straighten.js
+++ b/modules/operations/straighten.js
@@ -1,65 +1,81 @@
import { t } from '../util/locale';
-import { actionStraighten } from '../actions/index';
+import { actionStraightenNodes, actionStraightenWay } from '../actions/index';
import { behaviorOperation } from '../behavior/index';
import { utilArrayDifference, utilGetAllNodes } from '../util/index';
export function operationStraighten(selectedIDs, context) {
- var action = actionStraighten(selectedIDs, context.projection);
var wayIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'w'; });
- var nodes = utilGetAllNodes(wayIDs, context.graph());
+ var nodeIDs = selectedIDs.filter(function(id) { return id.charAt(0) === 'n'; });
+
+ var nodes = utilGetAllNodes(selectedIDs, context.graph());
var coords = nodes.map(function(n) { return n.loc; });
+ var action = chooseAction();
+ var geometry;
var _disabled;
+ function chooseAction() {
+ // straighten selected nodes
+ if (wayIDs.length === 0 && nodeIDs.length > 2) {
+ geometry = 'points';
+ return actionStraightenNodes(nodeIDs, context.projection);
+
+ // straighten selected ways (possibly between range of 2 selected nodes)
+ } else if (wayIDs.length > 0 && (nodeIDs.length === 0 || nodeIDs.length === 2)) {
+ var startNodeIDs = [];
+ var endNodeIDs = [];
+
+ for (var i = 0; i < selectedIDs.length; i++) {
+ var entity = context.entity(selectedIDs[i]);
+ if (entity.type === 'node') {
+ continue;
+ } else if (entity.type !== 'way' || entity.isClosed()) {
+ return false; // exit early, can't straighten these
+ }
+
+ startNodeIDs.push(entity.first());
+ endNodeIDs.push(entity.last());
+ }
+
+ // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
+ startNodeIDs = startNodeIDs.filter(function(n) {
+ return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
+ });
+ endNodeIDs = endNodeIDs.filter(function(n) {
+ return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
+ });
+
+ // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
+ if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
+ utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return false;
+
+ // Ensure path contains at least 3 unique nodes
+ var wayNodeIDs = utilGetAllNodes(wayIDs, context.graph())
+ .map(function(node) { return node.id; });
+ if (wayNodeIDs.length <= 2) return false;
+
+ // If range of 2 selected nodes is supplied, ensure nodes lie on the selected path
+ if (nodeIDs.length === 2 && (
+ wayNodeIDs.indexOf(nodeIDs[0]) === -1 || wayNodeIDs.indexOf(nodeIDs[1]) === -1
+ )) return false;
+
+ geometry = 'line';
+ return actionStraightenWay(selectedIDs, context.projection);
+ }
+
+ return false;
+ }
+
+
function operation() {
+ if (!action) return;
context.perform(action, operation.annotation());
}
operation.available = function() {
- var nodeIDs = nodes.map(function(node) { return node.id; });
- var startNodeIDs = [];
- var endNodeIDs = [];
- var selectedNodeIDs = [];
-
- for (var i = 0; i < selectedIDs.length; i++) {
- var entity = context.entity(selectedIDs[i]);
- if (entity.type === 'node') {
- selectedNodeIDs.push(entity.id);
- continue;
- } else if (entity.type !== 'way' || entity.isClosed()) {
- return false; // exit early, can't straighten these
- }
-
- startNodeIDs.push(entity.first());
- endNodeIDs.push(entity.last());
- }
-
- // Remove duplicate end/startNodeIDs (duplicate nodes cannot be at the line end)
- startNodeIDs = startNodeIDs.filter(function(n) {
- return startNodeIDs.indexOf(n) === startNodeIDs.lastIndexOf(n);
- });
- endNodeIDs = endNodeIDs.filter(function(n) {
- return endNodeIDs.indexOf(n) === endNodeIDs.lastIndexOf(n);
- });
-
- // Return false if line is only 2 nodes long
- if (nodeIDs.length <= 2) return false;
-
- // Return false unless exactly 0 or 2 specific start/end nodes are selected
- if (!(selectedNodeIDs.length === 0 || selectedNodeIDs.length === 2)) return false;
-
- // Ensure all ways are connected (i.e. only 2 unique endpoints/startpoints)
- if (utilArrayDifference(startNodeIDs, endNodeIDs).length +
- utilArrayDifference(endNodeIDs, startNodeIDs).length !== 2) return false;
-
- // Ensure both start/end selected nodes lie on the selected path
- if (selectedNodeIDs.length === 2 && (
- nodeIDs.indexOf(selectedNodeIDs[0]) === -1 || nodeIDs.indexOf(selectedNodeIDs[1]) === -1
- )) return false;
-
- return true;
+ return Boolean(action);
};
@@ -96,12 +112,12 @@ export function operationStraighten(selectedIDs, context) {
var disable = operation.disabled();
return disable ?
t('operations.straighten.' + disable) :
- t('operations.straighten.description');
+ t('operations.straighten.description.' + geometry);
};
operation.annotation = function() {
- return t('operations.straighten.annotation');
+ return t('operations.straighten.annotation.' + geometry);
};
diff --git a/test/index.html b/test/index.html
index 4dbafa952..827d7cea7 100644
--- a/test/index.html
+++ b/test/index.html
@@ -59,7 +59,8 @@
-
+
+
diff --git a/test/spec/actions/straighten_nodes.js b/test/spec/actions/straighten_nodes.js
new file mode 100644
index 000000000..96a2412a0
--- /dev/null
+++ b/test/spec/actions/straighten_nodes.js
@@ -0,0 +1,88 @@
+describe('iD.actionStraightenNodes', function () {
+ var projection = function (l) { return l; };
+ projection.invert = projection;
+
+ it('straightens points', function() {
+ var graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, -1] }),
+ iD.osmNode({ id: 'b', loc: [5, 1], tags: { foo: 'bar' } }),
+ iD.osmNode({ id: 'c', loc: [10, -1] }), // untagged
+ iD.osmNode({ id: 'd', loc: [15, 1] })
+ ]);
+
+ graph = iD.actionStraightenNodes(['a','b','c','d'], projection)(graph);
+ expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('b').loc[0]).to.be.closeTo(5, 1e-6);
+ expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('c').loc[0]).to.be.closeTo(10, 1e-6); // doesn't delete untagged
+ expect(graph.entity('c').loc[1]).to.be.closeTo(0, 1e-6); // doesn't delete untagged
+ expect(graph.entity('d').loc[0]).to.be.closeTo(15, 1e-6);
+ expect(graph.entity('d').loc[1]).to.be.closeTo(0, 1e-6);
+ });
+
+
+ describe('transitions', function () {
+ it('is transitionable', function() {
+ expect(iD.actionStraightenNodes().transitionable).to.be.true;
+ });
+
+ it('straighten at t = 0', function() {
+ var graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, -1] }),
+ iD.osmNode({ id: 'b', loc: [5, 1], tags: { foo: 'bar' } }),
+ iD.osmNode({ id: 'c', loc: [10, -1] }), // untagged
+ iD.osmNode({ id: 'd', loc: [15, 1] })
+ ]);
+
+ graph = iD.actionStraightenNodes(['a','b','c','d'], projection)(graph, 0);
+ expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('a').loc[1]).to.be.closeTo(-1, 1e-6);
+ expect(graph.entity('b').loc[0]).to.be.closeTo(5, 1e-6);
+ expect(graph.entity('b').loc[1]).to.be.closeTo(1, 1e-6);
+ expect(graph.entity('c').loc[0]).to.be.closeTo(10, 1e-6); // doesn't delete untagged
+ expect(graph.entity('c').loc[1]).to.be.closeTo(-1, 1e-6); // doesn't delete untagged
+ expect(graph.entity('d').loc[0]).to.be.closeTo(15, 1e-6);
+ expect(graph.entity('d').loc[1]).to.be.closeTo(1, 1e-6);
+ });
+
+ it('straighten at t = 0.5', function() {
+ var graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, -1] }),
+ iD.osmNode({ id: 'b', loc: [5, 1], tags: { foo: 'bar' } }),
+ iD.osmNode({ id: 'c', loc: [10, -1] }), // untagged
+ iD.osmNode({ id: 'd', loc: [15, 1] })
+ ]);
+
+ graph = iD.actionStraightenNodes(['a','b','c','d'], projection)(graph, 0.5);
+ expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('a').loc[1]).to.be.closeTo(-0.5, 1e-6);
+ expect(graph.entity('b').loc[0]).to.be.closeTo(5, 1e-6);
+ expect(graph.entity('b').loc[1]).to.be.closeTo(0.5, 1e-6);
+ expect(graph.entity('c').loc[0]).to.be.closeTo(10, 1e-6); // doesn't delete untagged
+ expect(graph.entity('c').loc[1]).to.be.closeTo(-0.5, 1e-6); // doesn't delete untagged
+ expect(graph.entity('d').loc[0]).to.be.closeTo(15, 1e-6);
+ expect(graph.entity('d').loc[1]).to.be.closeTo(0.5, 1e-6);
+ });
+
+ it('straighten at t = 1', function() {
+ var graph = iD.coreGraph([
+ iD.osmNode({ id: 'a', loc: [0, -1] }),
+ iD.osmNode({ id: 'b', loc: [5, 1], tags: { foo: 'bar' } }),
+ iD.osmNode({ id: 'c', loc: [10, -1] }), // untagged
+ iD.osmNode({ id: 'd', loc: [15, 1] })
+ ]);
+
+ graph = iD.actionStraightenNodes(['a','b','c','d'], projection)(graph, 1);
+ expect(graph.entity('a').loc[0]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('a').loc[1]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('b').loc[0]).to.be.closeTo(5, 1e-6);
+ expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
+ expect(graph.entity('c').loc[0]).to.be.closeTo(10, 1e-6); // doesn't delete untagged
+ expect(graph.entity('c').loc[1]).to.be.closeTo(0, 1e-6); // doesn't delete untagged
+ expect(graph.entity('d').loc[0]).to.be.closeTo(15, 1e-6);
+ expect(graph.entity('d').loc[1]).to.be.closeTo(0, 1e-6);
+ });
+ });
+
+});
diff --git a/test/spec/actions/straighten.js b/test/spec/actions/straighten_way.js
similarity index 88%
rename from test/spec/actions/straighten.js
rename to test/spec/actions/straighten_way.js
index 17bb0dbff..544121530 100644
--- a/test/spec/actions/straighten.js
+++ b/test/spec/actions/straighten_way.js
@@ -1,4 +1,4 @@
-describe('iD.actionStraighten', function () {
+describe('iD.actionStraightenWay', function () {
var projection = d3.geoMercator();
describe('#disabled', function () {
@@ -10,7 +10,7 @@ describe('iD.actionStraighten', function () {
iD.osmNode({id: 'd', loc: [3, 0]}),
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- expect(iD.actionStraighten(['-'], projection).disabled(graph)).not.to.be.ok;
+ expect(iD.actionStraightenWay(['-'], projection).disabled(graph)).not.to.be.ok;
});
it('returns \'too_bendy\' for ways with internal nodes far off centerline', function () {
@@ -21,7 +21,7 @@ describe('iD.actionStraighten', function () {
iD.osmNode({id: 'd', loc: [3, 0]}),
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- expect(iD.actionStraighten(['-'], projection).disabled(graph)).to.equal('too_bendy');
+ expect(iD.actionStraightenWay(['-'], projection).disabled(graph)).to.equal('too_bendy');
});
it('returns \'too_bendy\' for ways with coincident start/end nodes', function () {
@@ -32,7 +32,7 @@ describe('iD.actionStraighten', function () {
iD.osmNode({id: 'd', loc: [0, 0]}),
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- expect(iD.actionStraighten(['-'], projection).disabled(graph)).to.equal('too_bendy');
+ expect(iD.actionStraightenWay(['-'], projection).disabled(graph)).to.equal('too_bendy');
});
});
@@ -45,7 +45,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph);
+ graph = iD.actionStraightenWay(['-'], projection)(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'c']);
expect(graph.hasEntity('b')).to.eq(undefined);
});
@@ -58,7 +58,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '-', nodes: ['a', 'b', 'c']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph);
+ graph = iD.actionStraightenWay(['-'], projection)(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
@@ -73,7 +73,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '=', nodes: ['b']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph);
+ graph = iD.actionStraightenWay(['-'], projection)(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c']);
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
@@ -94,7 +94,7 @@ describe('iD.actionStraighten', function () {
iD.Way({id: '--', nodes: ['d', 'e', 'f', 'g', 'h']})
]);
- graph = iD.actionStraighten(['-', '--'], projection)(graph);
+ graph = iD.actionStraightenWay(['-', '--'], projection)(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd']);
expect(graph.entity('--').nodes).to.eql(['d', 'f', 'h']);
expect(graph.entity('f').loc[0]).to.be.closeTo(5, 1e-6);
@@ -117,7 +117,7 @@ describe('iD.actionStraighten', function () {
iD.Way({id: '--', nodes: ['h', 'g', 'f', 'e', 'd']})
]);
- graph = iD.actionStraighten(['-', '--'], projection)(graph);
+ graph = iD.actionStraightenWay(['-', '--'], projection)(graph);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd']);
expect(graph.entity('--').nodes).to.eql(['h', 'f', 'd']);
expect(graph.entity('f').loc[0]).to.be.closeTo(5, 1e-6);
@@ -127,7 +127,7 @@ describe('iD.actionStraighten', function () {
describe('transitions', function () {
it('is transitionable', function() {
- expect(iD.actionStraighten().transitionable).to.be.true;
+ expect(iD.actionStraightenWay().transitionable).to.be.true;
});
it('straighten at t = 0', function() {
@@ -139,7 +139,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph, 0);
+ graph = iD.actionStraightenWay(['-'], projection)(graph, 0);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd']);
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
expect(graph.entity('b').loc[1]).to.be.closeTo(0.01, 1e-6);
@@ -156,7 +156,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph, 0.5);
+ graph = iD.actionStraightenWay(['-'], projection)(graph, 0.5);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'c', 'd']);
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
expect(graph.entity('b').loc[1]).to.be.closeTo(0.005, 1e-6);
@@ -173,7 +173,7 @@ describe('iD.actionStraighten', function () {
iD.osmWay({id: '-', nodes: ['a', 'b', 'c', 'd']})
]);
- graph = iD.actionStraighten(['-'], projection)(graph, 1);
+ graph = iD.actionStraightenWay(['-'], projection)(graph, 1);
expect(graph.entity('-').nodes).to.eql(['a', 'b', 'd']);
expect(graph.entity('b').loc[0]).to.be.closeTo(1, 1e-6);
expect(graph.entity('b').loc[1]).to.be.closeTo(0, 1e-6);
diff --git a/test/spec/geo/geom.js b/test/spec/geo/geom.js
index e93a3b53b..dabe3e852 100644
--- a/test/spec/geo/geom.js
+++ b/test/spec/geo/geom.js
@@ -446,6 +446,19 @@ describe('iD.geo - geometry', function() {
});
});
+ describe('geoGetSmallestSurroundingRectangle', function() {
+ it('calculates a smallest surrounding rectangle', function() {
+ // +----b---------d
+ // | |
+ // a---------c----+
+ var points = [[0, -1], [5, 1], [10, -1], [15, 1]];
+ var ssr = iD.geoGetSmallestSurroundingRectangle(points);
+ expect(ssr.poly).to.eql([[0, -1], [0, 1], [15, 1], [15, -1], [0, -1]]);
+ expect(ssr.angle).to.eql(0);
+ });
+
+ });
+
describe('geoPathLength', function() {
it('calculates a simple path length', function() {
var path = [[0, 0], [0, 1], [3, 5]];