From 72c51bc28350d65ae2f037f2f76146a945dbe073 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 14 Dec 2016 17:21:32 -0500 Subject: [PATCH] Add getSmallestSurroundingRectangle --- modules/actions/reflect.js | 97 ++++++++++++++++++++++++++++++----- modules/operations/reflect.js | 8 +-- 2 files changed, 87 insertions(+), 18 deletions(-) diff --git a/modules/actions/reflect.js b/modules/actions/reflect.js index 394948c35..aa02b4dd2 100644 --- a/modules/actions/reflect.js +++ b/modules/actions/reflect.js @@ -1,26 +1,95 @@ import _ from 'lodash'; -/* Flip the provided way horizontally -Only operates on "area" ways -*/ +import { + polygonHull as d3polygonHull, + polygonCentroid as d3polygonCentroid +} from 'd3'; -export function actionReflect(wayId) { +import { geoExtent } from '../geo/extent'; - return function (graph) { - const targetWay = graph.entity(wayId); - // If the way is not an area, we will not process it +/* Flip the provided way horizontally. Only operates on "area" ways */ +export function actionReflect(wayId, projection) { + + function rotatePolygon(polygon, angle, centroid) { + return polygon.map(function(point) { + var radial = [point[0] - centroid[0], point[1] - centroid[1]]; + return [ + radial[0] * Math.cos(angle) - radial[1] * Math.sin(angle) + centroid[0], + radial[0] * Math.sin(angle) + radial[1] * Math.cos(angle) + centroid[1] + ]; + }); + } + + // 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, way) { + var nodes = _.uniq(graph.childNodes(way)), + points = nodes.map(function(n) { return projection(n.loc); }), + hull = d3polygonHull(points), + centroid = d3polygonCentroid(hull), + minArea = Infinity, + ssrExtent = [], + ssrAngle = 0, + c1 = hull[0]; + + for (var i = 0; i < hull.length - 1; i++) { + var c2 = hull[i + 1], + angle = Math.atan2(c2[1] - c1[1], c2[0] - c1[0]), + poly = rotatePolygon(hull, -angle, centroid), + extent = poly.reduce(function(extent, point) { + return extent.extend(geoExtent(point)); + }, geoExtent()), + area = extent.area(); + + if (area < minArea) { + minArea = area; + ssrExtent = extent; + ssrAngle = angle; + } + c1 = c2; + } + + return { + poly: rotatePolygon(ssrExtent.polygon(), ssrAngle, centroid), + angle: ssrAngle + }; + } + + + return function (graph) { + var targetWay = graph.entity(wayId); if (!targetWay.isArea()) { - // return input graph without changes return graph; } + + +// lol hacky debug + var ssr = getSmallestSurroundingRectangle(graph, targetWay); + var nodes = targetWay.nodes; + for (var d = 0; d < nodes.length - 1; d++) { + var node = graph.entity(nodes[d]); + if (d < 4) { + node = node.move(projection.invert(ssr.poly[d])); + graph = graph.replace(node); + } else { + graph = graph.remove(node); + } + } + graph = graph.replace(targetWay.update( + { nodes: [nodes[0], nodes[1], nodes[2], nodes[3], nodes[0]] }) + ); + + return graph; +// end debug + // Get the bounding rectangle of the area - const boundingRect = targetWay.extent(graph).rectangle(); + var boundingRect = targetWay.extent(graph).rectangle(); // rectangle returned as [ lon (x) top left, lat (y) top left, lon (x) bottom right, lat (y) bottom right] // Obtain the left and right lonlat's - const left = boundingRect[0]; - const right = boundingRect[2]; + var left = boundingRect[0]; + var right = boundingRect[2]; // Determine the mid-point that we will flip on - const midPoint = left + ((right - left) / 2); + var midPoint = left + ((right - left) / 2); // Obtain all of the nodes on the way, iterate over them to translate then aggreate up return _(targetWay.nodes) @@ -31,7 +100,7 @@ export function actionReflect(wayId) { .uniqBy(function (node) { return node.id; }) // Get distance from midPoint and produce a translated node .map(function (node) { - const delta = node.loc[0] - midPoint; + var delta = node.loc[0] - midPoint; return node.move([node.loc[0]-(2*delta), node.loc[1]]); }) // Chain together consecutive updates to the graph for each updated node and return @@ -40,4 +109,4 @@ export function actionReflect(wayId) { }, graph); }; -} \ No newline at end of file +} diff --git a/modules/operations/reflect.js b/modules/operations/reflect.js index 42b267df2..3ef5ed60f 100644 --- a/modules/operations/reflect.js +++ b/modules/operations/reflect.js @@ -2,10 +2,10 @@ import { t } from '../util/locale'; import { actionReflect } from '../actions/index'; export function operationReflect(selectedIDs, context) { - const entityId = selectedIDs[0]; - const entity = context.entity(entityId); - const extent = entity.extent(context.graph()); - const action = actionReflect(entityId); + var entityId = selectedIDs[0]; + var entity = context.entity(entityId); + var extent = entity.extent(context.graph()); + var action = actionReflect(entityId, context.projection); var operation = function() { context.perform(