operation/overlayng/Edge.h
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2020 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/geom/Coordinate.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/Dimension.h>
#include <geos/operation/overlayng/OverlayEdge.h>
#include <geos/operation/overlayng/OverlayLabel.h>
#include <geos/operation/overlayng/EdgeSourceInfo.h>
#include <geos/util/GEOSException.h>
#include <geos/export.h>
#include <memory>
namespace geos { // geos.
namespace operation { // geos.operation
namespace overlayng { // geos.operation.overlayng
class GEOS_DLL Edge {
using Location = geos::geom::Location;
private:
// Members
int aDim = OverlayLabel::DIM_UNKNOWN;
int aDepthDelta = 0;
bool aIsHole = false;
int bDim = OverlayLabel::DIM_UNKNOWN;
int bDepthDelta = 0;
bool bIsHole = false;
std::unique_ptr<geom::CoordinateSequence> pts;
// Methods
static void initLabel(OverlayLabel& lbl, uint8_t geomIndex, int dim, int depthDelta, bool p_isHole);
static int labelDim(int dim, int depthDelta)
{
if (dim == geom::Dimension::False)
return OverlayLabel::DIM_NOT_PART;
if (dim == geom::Dimension::L)
return OverlayLabel::DIM_LINE;
// assert: dim is A
bool isCollapse = (depthDelta == 0);
if (isCollapse)
return OverlayLabel::DIM_COLLAPSE;
return OverlayLabel::DIM_BOUNDARY;
};
bool isHole(int index) const
{
if (index == 0)
return aIsHole;
return bIsHole;
};
bool isBoundary(int geomIndex) const
{
if (geomIndex == 0)
return aDim == OverlayLabel::DIM_BOUNDARY;
return bDim == OverlayLabel::DIM_BOUNDARY;
};
bool isShell(int geomIndex) const
{
if (geomIndex == 0) {
return (aDim == OverlayLabel::DIM_BOUNDARY && ! aIsHole);
}
return (bDim == OverlayLabel::DIM_BOUNDARY && ! bIsHole);
};
static Location locationRight(int depthDelta)
{
int sgn = delSign(depthDelta);
switch (sgn) {
case 0: return Location::NONE;
case 1: return Location::INTERIOR;
case -1: return Location::EXTERIOR;
}
return Location::NONE;
};
static Location locationLeft(int depthDelta)
{
// TODO: is it always safe to ignore larger depth deltas?
int sgn = delSign(depthDelta);
switch (sgn) {
case 0: return Location::NONE;
case 1: return Location::EXTERIOR;
case -1: return Location::INTERIOR;
}
return Location::NONE;
};
static int delSign(int depthDel)
{
if (depthDel > 0) return 1;
if (depthDel < 0) return -1;
return 0;
};
void copyInfo(const EdgeSourceInfo* info)
{
if (info->getIndex() == 0) {
aDim = info->getDimension();
aIsHole = info->isHole();
aDepthDelta = info->getDepthDelta();
}
else {
bDim = info->getDimension();
bIsHole = info->isHole();
bDepthDelta = info->getDepthDelta();
}
};
static bool isHoleMerged(int geomIndex, const Edge* edge1, const Edge* edge2)
{
// TOD: this might be clearer with tri-state logic for isHole?
bool isShell1 = edge1->isShell(geomIndex);
bool isShell2 = edge2->isShell(geomIndex);
bool isShellMerged = isShell1 || isShell2;
// flip since isHole is stored
return !isShellMerged;
};
public:
Edge()
: aDim(OverlayLabel::DIM_UNKNOWN)
, aDepthDelta(0)
, aIsHole(false)
, bDim(OverlayLabel::DIM_UNKNOWN)
, bDepthDelta(0)
, bIsHole(false)
, pts(nullptr)
{};
friend std::ostream& operator<<(std::ostream& os, const Edge& e);
static bool isCollapsed(const geom::CoordinateSequence* pts);
// takes ownership of pts from caller
Edge(std::unique_ptr<geom::CoordinateSequence>&& p_pts, const EdgeSourceInfo* info);
// return a clone of the underlying points
std::unique_ptr<geom::CoordinateSequence> getCoordinates()
{
return pts->clone();
};
// return a read-only pointer to the underlying points
const geom::CoordinateSequence* getCoordinatesRO() const
{
return pts.get();
};
// release the underlying points to the caller
geom::CoordinateSequence* releaseCoordinates()
{
geom::CoordinateSequence* cs = pts.release();
pts.reset(nullptr);
return cs;
};
const geom::Coordinate& getCoordinate(std::size_t index) const
{
return pts->getAt(index);
};
std::size_t size() const
{
return pts->size();
};
bool direction() const
{
if (pts->size() < 2) {
throw util::GEOSException("Edge must have >= 2 points");
}
const geom::Coordinate& p0 = pts->getAt(0);
const geom::Coordinate& p1 = pts->getAt(1);
const geom::Coordinate& pn0 = pts->getAt(pts->size() - 1);
const geom::Coordinate& pn1 = pts->getAt(pts->size() - 2);
int cmp = 0;
int cmp0 = p0.compareTo(pn0);
if (cmp0 != 0) cmp = cmp0;
if (cmp == 0) {
int cmp1 = p1.compareTo(pn1);
if (cmp1 != 0) cmp = cmp1;
}
if (cmp == 0) {
throw util::GEOSException("Edge direction cannot be determined because endpoints are equal");
}
return cmp == -1;
};
bool relativeDirection(const Edge* edge2) const
{
// assert: the edges match (have the same coordinates up to direction)
if (!getCoordinate(0).equals2D(edge2->getCoordinate(0))) {
return false;
}
if (!getCoordinate(1).equals2D(edge2->getCoordinate(1))) {
return false;
}
return true;
};
int dimension(int geomIndex) const
{
if (geomIndex == 0) return aDim;
return bDim;
};
void merge(const Edge* edge)
{
aIsHole = isHoleMerged(0, this, edge);
bIsHole = isHoleMerged(1, this, edge);
if (edge->aDim > aDim) aDim = edge->aDim;
if (edge->bDim > bDim) bDim = edge->bDim;
bool relDir = relativeDirection(edge);
int flipFactor = relDir ? 1 : -1;
aDepthDelta += flipFactor * edge->aDepthDelta;
bDepthDelta += flipFactor * edge->bDepthDelta;
};
void populateLabel(OverlayLabel &lbl) const
{
initLabel(lbl, 0, aDim, aDepthDelta, aIsHole);
initLabel(lbl, 1, bDim, bDepthDelta, bIsHole);
};
bool compareTo(const Edge& e) const
{
const geom::Coordinate& ca = getCoordinate(0);
const geom::Coordinate& cb = e.getCoordinate(0);
if(ca.compareTo(cb) < 0) {
return true;
}
else if (ca.compareTo(cb) > 0) {
return false;
}
else {
const geom::Coordinate& cca = getCoordinate(1);
const geom::Coordinate& ccb = e.getCoordinate(1);
if(cca.compareTo(ccb) < 0) {
return true;
}
else if (cca.compareTo(ccb) > 0) {
return false;
}
else {
return false;
}
}
}
};
bool EdgeComparator(const Edge* a, const Edge* b);
} // namespace geos.operation.overlayng
} // namespace geos.operation
} // namespace geos
geos::geom::CoordinateSequence
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:56
geos::geom::Coordinate
Coordinate is the lightweight class used to store coordinates.
Definition Coordinate.h:217
geos::geom::Dimension::L
@ L
Dimension value of a curve (1).
Definition Dimension.h:43
geos::geom::Dimension::False
@ False
Dimension value of the empty geometry (-1).
Definition Dimension.h:37
geos::operation::overlayng::EdgeSourceInfo
Definition EdgeSourceInfo.h:38
geos::operation::overlayng::Edge
Definition operation/overlayng/Edge.h:55
geos::operation::overlayng::Edge::relativeDirection
bool relativeDirection(const Edge *edge2) const
Definition operation/overlayng/Edge.h:265
geos::operation::overlayng::Edge::merge
void merge(const Edge *edge)
Definition operation/overlayng/Edge.h:287
geos::operation::overlayng::OverlayLabel
Definition OverlayLabel.h:86
geos::util::GEOSException
Base class for all GEOS exceptions.
Definition GEOSException.h:37
geos::geom::Location
Location
Constants representing the location of a point relative to a geometry.
Definition Location.h:32
geos::index
Provides classes for various kinds of spatial indexes.
Definition CleanCoverage.h:39
geos::operation
Provides classes for implementing operations on geometries.
Definition CleanCoverage.h:34
geos
Basic namespace for all GEOS functionalities.
Definition geos.h:38