-
Notifications
You must be signed in to change notification settings - Fork 371
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix RelateOp for empty geometry and closed linear geometry
Ports the RelateOp class from JTS and then applies the fixes in locationtech/jts#671 Fixes https://trac.osgeo.org/geos/ticket/1096
- Loading branch information
Showing
10 changed files
with
704 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
/********************************************************************** | ||
* | ||
* GEOS - Geometry Engine Open Source | ||
* http://geos.osgeo.org | ||
* | ||
* Copyright (C) 2022 ISciences LLC | ||
* | ||
* 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. | ||
* | ||
********************************************************************** | ||
* | ||
* Last port: operation/BoundaryOp.java fd5aebb | ||
* | ||
**********************************************************************/ | ||
|
||
#pragma once | ||
|
||
#include <geos/algorithm/BoundaryNodeRule.h> | ||
#include <geos/geom/Geometry.h> | ||
#include <map> | ||
|
||
namespace geos { | ||
namespace geom { | ||
class LineString; | ||
class MultiLineString; | ||
} | ||
} | ||
|
||
namespace geos { | ||
namespace operation { | ||
|
||
/** | ||
* Computes the boundary of a Geometry. | ||
* Allows specifying the BoundaryNodeRule to be used. | ||
* This operation will always return a Geometry of the appropriate | ||
* dimension for the boundary (even if the input geometry is empty). | ||
* The boundary of zero-dimensional geometries (Points) is | ||
* always the empty GeometryCollection. | ||
* | ||
* @author Martin Davis | ||
* @version 1.7 | ||
*/ | ||
class GEOS_DLL BoundaryOp { | ||
|
||
public: | ||
/** | ||
* Creates a new instance for the given geometry. | ||
* | ||
* @param geom the input geometry | ||
*/ | ||
BoundaryOp(const geom::Geometry& geom); | ||
|
||
/** | ||
* Creates a new instance for the given geometry. | ||
* | ||
* @param geom the input geometry | ||
* @param bnRule the Boundary Node Rule to use | ||
*/ | ||
BoundaryOp(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& bnRule); | ||
|
||
/** | ||
* Computes a geometry representing the boundary of a geometry. | ||
* | ||
* @param g the input geometry | ||
* @return the computed boundary | ||
*/ | ||
static std::unique_ptr<geom::Geometry> getBoundary(const geom::Geometry& g); | ||
|
||
/** | ||
* Computes a geometry representing the boundary of a geometry, | ||
* using an explicit BoundaryNodeRule. | ||
* | ||
* @param g the input geometry | ||
* @param bnRule the Boundary Node Rule to use | ||
* @return the computed boundary | ||
*/ | ||
static std::unique_ptr<geom::Geometry> getBoundary(const geom::Geometry& g, const algorithm::BoundaryNodeRule& bnRule); | ||
|
||
/** | ||
* Tests if a geometry has a boundary (it is non-empty). | ||
* The semantics are: | ||
* <ul> | ||
* <li>Empty geometries do not have boundaries. | ||
* <li>Points do not have boundaries. | ||
* <li>For linear geometries the existence of the boundary | ||
* is determined by the BoundaryNodeRule. | ||
* <li>Non-empty polygons always have a boundary. | ||
* </ul> | ||
* | ||
* @param geom the geometry providing the boundary | ||
* @param boundaryNodeRule the Boundary Node Rule to use | ||
* @return true if the boundary exists | ||
*/ | ||
static bool hasBoundary(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& boundaryNodeRule); | ||
|
||
/** | ||
* Gets the computed boundary. | ||
* | ||
* @return the boundary geometry | ||
*/ | ||
std::unique_ptr<geom::Geometry> getBoundary(); | ||
|
||
private: | ||
const geom::Geometry& m_geom; | ||
const geom::GeometryFactory& m_geomFact; | ||
const algorithm::BoundaryNodeRule& m_bnRule; | ||
|
||
std::unique_ptr<geom::Geometry> boundaryMultiLineString(const geom::MultiLineString& mLine); | ||
|
||
std::vector<geom::Coordinate> computeBoundaryCoordinates(const geom::MultiLineString& mLine); | ||
|
||
std::unique_ptr<geom::Geometry> boundaryLineString(const geom::LineString& line); | ||
}; | ||
|
||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/********************************************************************** | ||
* | ||
* GEOS - Geometry Engine Open Source | ||
* http://geos.osgeo.org | ||
* | ||
* Copyright (C) 2022 ISciences LLC | ||
* | ||
* 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. | ||
* | ||
********************************************************************** | ||
* | ||
* Last port: operation/BoundaryOp.java fd5aebb | ||
* | ||
**********************************************************************/ | ||
|
||
#include <geos/operation/BoundaryOp.h> | ||
#include <geos/algorithm/BoundaryNodeRule.h> | ||
#include <geos/geom/Geometry.h> | ||
#include <geos/geom/GeometryFactory.h> | ||
#include <geos/geom/LineString.h> | ||
#include <geos/geom/MultiLineString.h> | ||
#include <map> | ||
|
||
using geos::geom::Coordinate; | ||
using geos::geom::Dimension; | ||
using geos::geom::Geometry; | ||
using geos::geom::LineString; | ||
using geos::geom::MultiLineString; | ||
using geos::geom::Point; | ||
using geos::algorithm::BoundaryNodeRule; | ||
|
||
namespace geos { | ||
namespace operation { | ||
|
||
BoundaryOp::BoundaryOp(const Geometry& geom) : | ||
m_geom(geom), | ||
m_geomFact(*geom.getFactory()), | ||
m_bnRule(BoundaryNodeRule::getBoundaryRuleMod2()) | ||
{} | ||
|
||
BoundaryOp::BoundaryOp(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& bnRule) : | ||
m_geom(geom), | ||
m_geomFact(*geom.getFactory()), | ||
m_bnRule(bnRule) | ||
{} | ||
|
||
std::unique_ptr<geom::Geometry> | ||
BoundaryOp::getBoundary() | ||
{ | ||
if (auto ls = dynamic_cast<const LineString*>(&m_geom)) { | ||
return boundaryLineString(*ls); | ||
} | ||
|
||
if (auto mls = dynamic_cast<const MultiLineString*>(&m_geom)) { | ||
return boundaryMultiLineString(*mls); | ||
} | ||
|
||
return m_geom.getBoundary(); | ||
} | ||
|
||
std::unique_ptr<geom::Geometry> | ||
BoundaryOp::getBoundary(const geom::Geometry& g) | ||
{ | ||
BoundaryOp bop(g); | ||
return bop.getBoundary(); | ||
} | ||
|
||
std::unique_ptr<geom::Geometry> | ||
BoundaryOp::getBoundary(const geom::Geometry& g, const algorithm::BoundaryNodeRule& bnRule) | ||
{ | ||
BoundaryOp bop(g, bnRule); | ||
return bop.getBoundary(); | ||
} | ||
|
||
bool | ||
BoundaryOp::hasBoundary(const geom::Geometry& geom, const algorithm::BoundaryNodeRule& boundaryNodeRule) | ||
{ | ||
// Note that this does not handle geometry collections with a non-empty linear element | ||
if (geom.isEmpty()) { | ||
return false; | ||
} | ||
|
||
switch (geom.getDimension()) { | ||
case Dimension::P: return false; | ||
/** | ||
* Linear geometries might have an empty boundary due to boundary node rule. | ||
*/ | ||
case Dimension::L: | ||
{ | ||
|
||
auto boundary = getBoundary(geom, boundaryNodeRule); | ||
return !boundary->isEmpty(); | ||
} | ||
default: | ||
return true; | ||
} | ||
} | ||
|
||
std::unique_ptr<Geometry> | ||
BoundaryOp::boundaryLineString(const geom::LineString& line) | ||
{ | ||
if (m_geom.isEmpty()) { | ||
return m_geomFact.createMultiPoint(); | ||
} | ||
|
||
if (line.isClosed()) { | ||
// check whether endpoints of valence 2 are on the boundary or not | ||
bool closedEndpointOnBoundary = m_bnRule.isInBoundary(2); | ||
if (closedEndpointOnBoundary) { | ||
return line.getStartPoint(); | ||
} | ||
else { | ||
return m_geomFact.createMultiPoint(); | ||
} | ||
} | ||
|
||
std::vector<std::unique_ptr<Point>> pts(2); | ||
pts[0] = line.getStartPoint(); | ||
pts[1] = line.getEndPoint(); | ||
|
||
return m_geomFact.createMultiPoint(std::move(pts)); | ||
} | ||
|
||
std::unique_ptr<Geometry> | ||
BoundaryOp::boundaryMultiLineString(const geom::MultiLineString& mLine) | ||
{ | ||
if (m_geom.isEmpty()) { | ||
return m_geomFact.createMultiPoint(); | ||
} | ||
|
||
auto bdyPts = computeBoundaryCoordinates(mLine); | ||
|
||
// return Point or MultiPoint | ||
if (bdyPts.size() == 1) { | ||
return std::unique_ptr<Geometry>(m_geomFact.createPoint(bdyPts[0])); | ||
} | ||
// this handles 0 points case as well | ||
return m_geomFact.createMultiPoint(std::move(bdyPts)); | ||
} | ||
|
||
std::vector<geom::Coordinate> | ||
BoundaryOp::computeBoundaryCoordinates(const geom::MultiLineString& mLine) | ||
{ | ||
std::vector<Coordinate> bdyPts; | ||
std::map<Coordinate, int> endpointMap; | ||
|
||
for (std::size_t i = 0; i < mLine.getNumGeometries(); i++) { | ||
const LineString* line = mLine.getGeometryN(i); | ||
|
||
if (line->getNumPoints() == 0) { | ||
continue; | ||
} | ||
|
||
endpointMap[line->getCoordinateN(0)]++; | ||
endpointMap[line->getCoordinateN(line->getNumPoints() - 1)]++; | ||
} | ||
|
||
for (const auto& entry: endpointMap) { | ||
auto valence = entry.second; | ||
if (m_bnRule.isInBoundary(valence)) { | ||
bdyPts.push_back(entry.first); | ||
} | ||
} | ||
|
||
return bdyPts; | ||
} | ||
|
||
|
||
} | ||
} |
Oops, something went wrong.