Skip to content

Commit

Permalink
Add MinimumAreaRectangle, a bounding rectangle algorithm.
Browse files Browse the repository at this point in the history
Port of locationtech/jts#977
This adds a MinimumAreaRectangle class implementing the
standard "rotating-calipers` algorithm for computing a Minimum-Area Rectangle.

MinimumDiameter.getMinimumRectangle was previously used
for this, but it does not always compute the Minimum-Area
Rectangle. It is kept available, for backwards compatibility.
Also, it computes the Minimum-Width Rectangle, which may be useful.

The CAPI GEOSMinimumRotatedRectangle is now bound to
MinimumAreaRectangle rather than MinimumDiameter.getMinimumRectangle
  • Loading branch information
pramsey committed May 26, 2023
1 parent bfe64bd commit 2efd5ed
Show file tree
Hide file tree
Showing 15 changed files with 1,148 additions and 42 deletions.
6 changes: 4 additions & 2 deletions capi/geos_ts_c.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <geos/algorithm/BoundaryNodeRule.h>
#include <geos/algorithm/MinimumBoundingCircle.h>
#include <geos/algorithm/MinimumDiameter.h>
#include <geos/algorithm/MinimumAreaRectangle.h>
#include <geos/algorithm/Orientation.h>
#include <geos/algorithm/construct/MaximumInscribedCircle.h>
#include <geos/algorithm/construct/LargestEmptyCircle.h>
Expand Down Expand Up @@ -1323,9 +1324,10 @@ extern "C" {
Geometry*
GEOSMinimumRotatedRectangle_r(GEOSContextHandle_t extHandle, const Geometry* g)
{
using geos::algorithm::MinimumAreaRectangle;

return execute(extHandle, [&]() {
geos::algorithm::MinimumDiameter m(g);
auto g3 = m.getMinimumRectangle();
auto g3 = MinimumAreaRectangle::getMinimumRectangle(g);
g3->setSRID(g->getSRID());
return g3.release();
});
Expand Down
32 changes: 20 additions & 12 deletions include/geos/algorithm/Distance.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,11 @@ class GEOS_DLL Distance {
* another point of the line (must be different to A)
*/
// formerly distanceLineLine
static double segmentToSegment(const geom::CoordinateXY& A,
const geom::CoordinateXY& B,
const geom::CoordinateXY& C,
const geom::CoordinateXY& D);
static double segmentToSegment(
const geom::CoordinateXY& A,
const geom::CoordinateXY& B,
const geom::CoordinateXY& C,
const geom::CoordinateXY& D);

/**
* Computes the distance from a point to a sequence of line segments.
Expand All @@ -63,8 +64,9 @@ class GEOS_DLL Distance {
* a sequence of contiguous line segments defined by their vertices
* @return the minimum distance between the point and the line segments
*/
static double pointToSegmentString(const geom::CoordinateXY& p,
const geom::CoordinateSequence* seq);
static double pointToSegmentString(
const geom::CoordinateXY& p,
const geom::CoordinateSequence* seq);

/**
* Computes the distance from a point p to a line segment AB
Expand All @@ -80,9 +82,10 @@ class GEOS_DLL Distance {
* @return the distance from p to line segment AB
*/
// formerly distancePointLine
static double pointToSegment(const geom::CoordinateXY& p,
const geom::CoordinateXY& A,
const geom::CoordinateXY& B);
static double pointToSegment(
const geom::CoordinateXY& p,
const geom::CoordinateXY& A,
const geom::CoordinateXY& B);

/**
* Computes the perpendicular distance from a point p to the (infinite) line
Expand All @@ -97,10 +100,15 @@ class GEOS_DLL Distance {
* @return the distance from p to line AB
*/
// formerly distancePointLinePerpendicular
static double pointToLinePerpendicular(const geom::CoordinateXY& p,
const geom::CoordinateXY& A,
const geom::CoordinateXY& B);
static double pointToLinePerpendicular(
const geom::CoordinateXY& p,
const geom::CoordinateXY& A,
const geom::CoordinateXY& B);

static double pointToLinePerpendicularSigned(
const geom::CoordinateXY& p,
const geom::CoordinateXY& A,
const geom::CoordinateXY& B);
};

} // namespace geos::algorithm
Expand Down
159 changes: 159 additions & 0 deletions include/geos/algorithm/MinimumAreaRectangle.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2023 Paul Ramsey <pramsey@cleverelephant.ca>
*
* This is free software; you can redistribute and/or modify it under
* the terms of the GNU Lesser General Public Licence as published
* by the Free Software Foundation.
* See the COPYING file for more information.
*
**********************************************************************/

#pragma once

#include <geos/export.h>

#include <vector>
#include <memory>

// Forward declarations
namespace geos {
namespace geom {
class CoordinateSequence;
class CoordinateXY;
class Geometry;
class GeometryFactory;
class LineSegment;
class LineString;
class Polygon;
}
}

using geos::geom::CoordinateSequence;
using geos::geom::CoordinateXY;
using geos::geom::Geometry;
using geos::geom::GeometryFactory;
using geos::geom::LineSegment;
using geos::geom::LineString;
using geos::geom::Polygon;


namespace geos {
namespace algorithm { // geos::algorithm


/**
* Computes the minimum-area rectangle enclosing a Geometry.
* Unlike the Envelope, the rectangle may not be axis-parallel.
*
* The first step in the algorithm is computing the convex hull of the Geometry.
* If the input Geometry is known to be convex, a hint can be supplied to
* avoid this computation.
*
* In degenerate cases the minimum enclosing geometry
* may be a LineString or a Point.
*
* The minimum-area enclosing rectangle does not necessarily
* have the minimum possible width.
* Use MinimumDiameter to compute this.
*
* @see MinimumDiameter
* @see ConvexHull
*
*/
class GEOS_DLL MinimumAreaRectangle {

private:

// Members
const Geometry* m_inputGeom;
bool m_isConvex;

// Methods
std::unique_ptr<Geometry> getMinimumRectangle();

std::unique_ptr<Geometry> computeConvex(const Geometry* convexGeom);

/**
* Computes the minimum-area rectangle for a convex ring of Coordinate.
*
* This algorithm uses the "dual rotating calipers" technique.
* Performance is linear in the number of segments.
*
* @param ring the convex ring to scan
*/
std::unique_ptr<Polygon> computeConvexRing(const CoordinateSequence* ring);

std::size_t findFurthestVertex(
const CoordinateSequence* pts,
const LineSegment& baseSeg,
std::size_t startIndex,
int orient);

bool isFurtherOrEqual(double d1, double d2, int orient);

static double orientedDistance(
const LineSegment& seg,
const CoordinateXY& p,
int orient);

static std::size_t getNextIndex(
const CoordinateSequence* ring,
std::size_t index);

/**
* Creates a line of maximum extent from the provided vertices
* @param pts the vertices
* @param factory the geometry factory
* @return the line of maximum extent
*/
static std::unique_ptr<LineString> computeMaximumLine(
const CoordinateSequence* pts,
const GeometryFactory* factory);


public:

/**
* Compute a minimum-area rectangle for a given Geometry.
*
* @param inputGeom a Geometry
*/
MinimumAreaRectangle(const Geometry* inputGeom)
: m_inputGeom(inputGeom)
, m_isConvex(false)
{};

/**
* Compute a minimum rectangle for a Geometry,
* with a hint if the geometry is convex
* (e.g. a convex Polygon or LinearRing,
* or a two-point LineString, or a Point).
*
* @param inputGeom a Geometry which is convex
* @param isConvex true if the input geometry is convex
*/
MinimumAreaRectangle(const Geometry* inputGeom, bool isConvex)
: m_inputGeom(inputGeom)
, m_isConvex(isConvex)
{};

/**
* Gets the minimum-area rectangular Polygon which encloses the input geometry.
* If the convex hull of the input is degenerate (a line or point)
* a LineString or Point is returned.
*
* @param geom the geometry
* @return the minimum rectangle enclosing the geometry
*/
static std::unique_ptr<Geometry> getMinimumRectangle(const Geometry* geom);

};


} // namespace geos::algorithm
} // namespace geos

30 changes: 17 additions & 13 deletions include/geos/algorithm/MinimumDiameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2023 Paul Ramsey <pramsey@cleverelephant.ca>
* Copyright (C) 2005-2006 Refractions Research Inc.
* Copyright (C) 2001-2002 Vivid Solutions Inc.
*
Expand All @@ -11,10 +12,6 @@
* by the Free Software Foundation.
* See the COPYING file for more information.
*
**********************************************************************
*
* Last port: algorithm/MinimumDiameter.java r966
*
**********************************************************************/

#pragma once
Expand Down Expand Up @@ -53,12 +50,16 @@ namespace algorithm { // geos::algorithm
*
* This class can also be used to compute a line segment representing
* the minimum diameter, the supporting line segment of the minimum diameter,
* and a minimum rectangle enclosing the input geometry.
* and a minimum-width rectangle of the input geometry.
* This rectangle will have width equal to the minimum diameter, and have
* one side parallel to the supporting segment.
*
* @see ConvexHull
* In degenerate cases the rectangle may be a LineString or a Point.
* (Note that this may not be the enclosing rectangle with minimum area;
* use MinimumAreaRectangle to compute this.)
*
* @see ConvexHull
* @see MinimumAreaRectangle
*/
class GEOS_DLL MinimumDiameter {
private:
Expand All @@ -75,7 +76,7 @@ class GEOS_DLL MinimumDiameter {
void computeWidthConvex(const geom::Geometry* geom);

/**
* Compute the width information for a ring of {@link geom::Coordinate}s.
* Compute the width information for a ring of Coordinate.
* Leaves the width information in the instance variables.
*
* @param pts
Expand Down Expand Up @@ -127,7 +128,7 @@ class GEOS_DLL MinimumDiameter {
double getLength();

/** \brief
* Gets the {@link geom::Coordinate} forming one end of the minimum diameter.
* Gets the geom::Coordinate forming one end of the minimum diameter.
*
* @return a coordinate forming one end of the minimum diameter
*/
Expand All @@ -148,23 +149,26 @@ class GEOS_DLL MinimumDiameter {
std::unique_ptr<geom::LineString> getDiameter();

/** \brief
* Gets the minimum rectangular Polygon which encloses the input geometry.
* Gets the rectangular Polygon which encloses the input geometry
* and is based on the minimum diameter supporting segment.
*
* The rectangle has width equal to the minimum diameter, and a longer
* length. If the convex hill of the input is degenerate (a line or point)
* a LineString or Point is returned.
* The minimum rectangle can be used as an extremely generalized
* representation for the given geometry.
* This is not necessarily the rectangle with minimum area.
* Use MinimumAreaRectangle to compute this.
*
* @return the minimum rectangle enclosing the input (or a line or point if degenerate)
* @return the the minimum-width rectangle enclosing the geometry
* @see MinimumAreaRectangle
*/
std::unique_ptr<geom::Geometry> getMinimumRectangle();

/** \brief
* Gets the minimum rectangle enclosing a geometry.
*
* @param geom the geometry
* @return the minimum rectangle enclosing the geometry
* @return a rectangle enclosing the input (or a line or point if degenerate)
* @see MinimumAreaRectangle
*/
static std::unique_ptr<geom::Geometry> getMinimumRectangle(geom::Geometry* geom);

Expand Down

0 comments on commit 2efd5ed

Please sign in to comment.