Skip to content

Commit

Permalink
CoveragePolygonValidator section perf optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
dr-jts committed May 22, 2024
1 parent 1945569 commit 2ea7479
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 89 deletions.
57 changes: 57 additions & 0 deletions include/geos/coverage/CoveragePolygon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2024 Martin Davis <mtnclimb@gmail.com>
*
* 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/algorithm/locate/IndexedPointInAreaLocator.h>

// Forward declarations
namespace geos {
namespace geom {
class Coordinate;
class Envelope;
class Polygon;
}
}

using geos::geom::Coordinate;
using geos::geom::Envelope;
using geos::geom::Polygon;
using geos::algorithm::locate::IndexedPointInAreaLocator;

namespace geos { // geos
namespace coverage { // geos::coverage

class GEOS_DLL CoveragePolygon {

// Members
const Polygon* polygon;
Envelope polyEnv;
std::unique_ptr<IndexedPointInAreaLocator> locator;

public:
CoveragePolygon(const Polygon* poly);

bool intersectsEnv(const Envelope env);
bool intersectsEnv(const Coordinate p);
bool contains(const Coordinate p);

private:
IndexedPointInAreaLocator* getLocator();

};

} // namespace geos::coverage
} // namespace geos

38 changes: 16 additions & 22 deletions include/geos/coverage/CoveragePolygonValidator.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <geos/noding/BasicSegmentString.h>
#include <geos/geom/LineSegment.h>
#include <geos/algorithm/locate/IndexedPointInAreaLocator.h>
#include <geos/coverage/CoveragePolygon.h>
#include <geos/coverage/CoverageRing.h>

#include <unordered_map>
Expand Down Expand Up @@ -195,11 +196,10 @@ class GEOS_DLL CoveragePolygonValidator {
// Members
const Geometry* targetGeom;
std::vector<const Geometry*> adjGeoms;
std::vector<const Polygon*> m_adjPolygons;
//std::vector<const Polygon*> m_adjPolygons;
const GeometryFactory* geomFactory;
double gapWidth = 0.0;
std::map<std::size_t, std::unique_ptr<IndexedPointInAreaLocator>> adjPolygonLocators;
// std::vector<std::unique_ptr<CoverageRing>> coverageRingStore;
std::vector<std::unique_ptr<CoveragePolygon>> m_adjCovPolygons;
std::deque<CoverageRing> coverageRingStore;
std::vector<std::unique_ptr<CoordinateSequence>> localCoordinateSequences;
std::deque<CoverageRingSegment> coverageRingSegmentStore;
Expand Down Expand Up @@ -273,6 +273,8 @@ class GEOS_DLL CoveragePolygonValidator {

private:

static std::vector<std::unique_ptr<CoveragePolygon>>
toCoveragePolygons(std::vector<const Polygon*> polygons);
static std::vector<const Polygon*> extractPolygons(std::vector<const Geometry*>& geoms);

/* private */
Expand Down Expand Up @@ -336,34 +338,26 @@ class GEOS_DLL CoveragePolygonValidator {
* to an adjacent polygon.
*
* @param targetRings the rings with segments to test
* @param adjPolygons the adjacent polygons
* @param adjCovPolygons the adjacent polygons
*/
void markInvalidInteriorSegments(
std::vector<CoverageRing*>& targetRings,
std::vector<const Polygon*>& adjPolygons);
std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons);

void markInvalidInteriorSection(
CoverageRing* ring,
std::size_t iStart,
std::size_t iEnd,
std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons );

void markInvalidInteriorSegment(
CoverageRing* ring, std::size_t i, CoveragePolygon* adjPoly);

void checkTargetRings(
std::vector<CoverageRing*>& targetRings,
std::vector<CoverageRing*>& adjRngs,
const Envelope& targetEnv);

/**
* Tests if a coordinate is in the interior of some adjacent polygon.
* Uses the cached Point-In-Polygon indexed locators, for performance.
*
* @param p the coordinate to test
* @param adjPolygons the list of polygons
* @return true if the point is in the interior
*/
bool isInteriorVertex(const Coordinate& p,
std::vector<const Polygon*>& adjPolygons);


bool polygonContainsPoint(std::size_t index,
const Polygon* poly, const Coordinate& pt);

IndexedPointInAreaLocator* getLocator(std::size_t index, const Polygon* poly);

std::unique_ptr<Geometry> createInvalidLines(std::vector<CoverageRing*>& rings);

std::vector<CoverageRing*> createRings(const Geometry* geom);
Expand Down
2 changes: 2 additions & 0 deletions include/geos/coverage/CoverageRing.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class GEOS_DLL CoverageRing : public noding::BasicSegmentString {

CoverageRing(const LinearRing* ring, bool isShell);

geom::Envelope getEnvelope(std::size_t start, std::size_t end);

/**
* Tests if all rings have known status (matched or invalid)
* for all segments.
Expand Down
76 changes: 76 additions & 0 deletions src/coverage/CoveragePolygon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**********************************************************************
*
* GEOS - Geometry Engine Open Source
* http://geos.osgeo.org
*
* Copyright (C) 2024 Martin Davis <mtnclimb@gmail.com>
*
* 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.
*
**********************************************************************/

#include <geos/coverage/CoveragePolygon.h>

#include <geos/geom/Coordinate.h>
#include <geos/geom/Envelope.h>
#include <geos/geom/Location.h>
#include <geos/geom/Polygon.h>

using geos::algorithm::locate::IndexedPointInAreaLocator;
using geos::geom::Coordinate;
using geos::geom::Envelope;
using geos::geom::Location;
using geos::geom::Polygon;

namespace geos { // geos
namespace coverage { // geos.coverage

/* public */
CoveragePolygon::CoveragePolygon(const Polygon* poly)
: polygon(poly)
{
polyEnv = *(poly->getEnvelopeInternal());
}

/* public */
bool
CoveragePolygon::intersectsEnv(const Envelope env)
{
return polyEnv.intersects(env);
}

/* public */
bool
CoveragePolygon::intersectsEnv(const Coordinate p)
{
return polyEnv.intersects(p);
}

/* public */
bool
CoveragePolygon::contains(const Coordinate p)
{
if (! intersectsEnv(p))
return false;
IndexedPointInAreaLocator* pia = getLocator();
return Location::INTERIOR == pia->locate(&p);
}

/* private */
IndexedPointInAreaLocator*
CoveragePolygon::getLocator()
{
if (locator == nullptr) {
//locator = std::make_unique<IndexedPointInAreaLocator>(new IndexedPointInAreaLocator(*polygon));
locator = std::unique_ptr<IndexedPointInAreaLocator>(new IndexedPointInAreaLocator(*polygon));
}
return locator.get();
}

} // namespace geos.coverage
} // namespace geos


122 changes: 55 additions & 67 deletions src/coverage/CoveragePolygonValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
**********************************************************************/

#include <geos/coverage/CoveragePolygonValidator.h>

#include <geos/coverage/InvalidSegmentDetector.h>
#include <geos/coverage/CoveragePolygon.h>

#include <geos/algorithm/Orientation.h>
#include <geos/geom/Coordinate.h>
Expand Down Expand Up @@ -88,10 +90,10 @@ CoveragePolygonValidator::setGapWidth(double p_gapWidth)
std::unique_ptr<Geometry>
CoveragePolygonValidator::validate()
{
m_adjPolygons = extractPolygons(adjGeoms);

std::vector<const Polygon*> adjPolygons = extractPolygons(adjGeoms);
m_adjCovPolygons = toCoveragePolygons(adjPolygons);
std::vector<CoverageRing*> targetRings = createRings(targetGeom);
std::vector<CoverageRing*> adjRings = createRings(m_adjPolygons);
std::vector<CoverageRing*> adjRings = createRings(adjPolygons);

/**
* Mark matching segments first.
Expand All @@ -106,6 +108,15 @@ CoveragePolygonValidator::validate()
return createInvalidLines(targetRings);
}

/* private static */
std::vector<std::unique_ptr<CoveragePolygon>>
CoveragePolygonValidator::toCoveragePolygons(std::vector<const Polygon*> polygons) {
std::vector<std::unique_ptr<CoveragePolygon>> covPolys;
for (const Polygon* poly : polygons) {
covPolys.push_back( std::unique_ptr<CoveragePolygon>(new CoveragePolygon(poly)) );
}
return covPolys;
}

/* private */
void
Expand All @@ -132,10 +143,9 @@ CoveragePolygonValidator::checkTargetRings(
* Do further checks to see if any of them are are invalid.
*/
markInvalidInteractingSegments(targetRings, adjRings, gapWidth);
markInvalidInteriorSegments(targetRings, m_adjPolygons);
markInvalidInteriorSegments(targetRings, m_adjCovPolygons);
}


/* private static */
std::vector<const Polygon*>
CoveragePolygonValidator::extractPolygons(std::vector<const Geometry*>& geoms)
Expand Down Expand Up @@ -246,85 +256,63 @@ CoveragePolygonValidator::markInvalidInteractingSegments(
void
CoveragePolygonValidator::markInvalidInteriorSegments(
std::vector<CoverageRing*>& targetRings,
std::vector<const Polygon*>& adjPolygons)
std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons )
{
for (CoverageRing* ring : targetRings) {
for (std::size_t i = 0; i < ring->size() - 1; i++) {
//-- skip check for segments with known state.
if (ring->isKnown(i))
continue;

/**
* Check if vertex is in interior of an adjacent polygon.
* If so, the segments on either side are in the interior.
* Mark them invalid, unless they are already matched.
*/
const Coordinate& p = ring->getCoordinate(i);
if (isInteriorVertex(p, adjPolygons)) {
ring->markInvalid(i);
//-- previous segment may be interior (but may also be matched)
std::size_t iPrev = i == 0 ? ring->size()-2 : i-1;
if (! ring->isKnown(iPrev))
ring->markInvalid(iPrev);
}
std::size_t stride = 1000; //-- RING_SECTION_STRIDE;
for (std::size_t i = 0; i < ring->size() - 1; i += stride) {
std::size_t iEnd = i + stride;
if (iEnd >= ring->size())
iEnd = ring->size() - 1;
markInvalidInteriorSection(ring, i, iEnd, adjCovPolygons);
}
}
}


/* private */
bool
CoveragePolygonValidator::isInteriorVertex(
const Coordinate& p,
std::vector<const Polygon*>& adjPolygons)
void
CoveragePolygonValidator::markInvalidInteriorSection(
CoverageRing* ring,
std::size_t iStart,
std::size_t iEnd,
std::vector<std::unique_ptr<CoveragePolygon>>& adjCovPolygons )
{
/**
* There should not be too many adjacent polygons,
* and hopefully not too many segments with unknown status
* so a linear scan should not be too inefficient
*/
//TODO: try a spatial index?
for (std::size_t i = 0; i < adjPolygons.size(); i++) {
const Polygon* adjPoly = adjPolygons[i];
if (polygonContainsPoint(i, adjPoly, p))
return true;
Envelope sectionEnv = ring->getEnvelope(iStart, iEnd);
//TODO: is it worth indexing polygons?
for (auto& adjPoly : adjCovPolygons) {
if (adjPoly->intersectsEnv(sectionEnv)) {
//-- test vertices in section
for (std::size_t i = iStart; i < iEnd; i++) {
markInvalidInteriorSegment(ring, i, adjPoly.get());
}
}
}
return false;
}


/* private */
bool
CoveragePolygonValidator::polygonContainsPoint(std::size_t index,
const Polygon* poly, const Coordinate& pt)
void
CoveragePolygonValidator::markInvalidInteriorSegment(
CoverageRing* ring, std::size_t i, CoveragePolygon* adjPoly)
{
if (! poly->getEnvelopeInternal()->intersects(pt))
return false;
IndexedPointInAreaLocator* pia = getLocator(index, poly);
return Location::INTERIOR == pia->locate(&pt);
}

//-- skip check for segments with known state.
if (ring->isKnown(i))
return;

/* private */
IndexedPointInAreaLocator*
CoveragePolygonValidator::getLocator(std::size_t index, const Polygon* poly)
{
auto it = adjPolygonLocators.find(index);
// found locator already constructed
if (it != adjPolygonLocators.end()) {
return (it->second).get();
}
// construct new locator for this polygon
else {
IndexedPointInAreaLocator* ipia = new IndexedPointInAreaLocator(*poly);
adjPolygonLocators.emplace(std::piecewise_construct,
std::forward_as_tuple(index),
std::forward_as_tuple(ipia));
return ipia;
/**
* Check if vertex is in interior of an adjacent polygon.
* If so, the segments on either side are in the interior.
* Mark them invalid, unless they are already matched.
*/
const Coordinate& p = ring->getCoordinate(i);
if (adjPoly->contains(p)) {
ring->markInvalid(i);
//-- previous segment may be interior (but may also be matched)
std::size_t iPrev = i == 0 ? ring->size()-2 : i-1;
if (! ring->isKnown(iPrev))
ring->markInvalid(iPrev);
}
}


/* private */
std::unique_ptr<Geometry>
CoveragePolygonValidator::createInvalidLines(std::vector<CoverageRing*>& rings)
Expand Down
Loading

0 comments on commit 2ea7479

Please sign in to comment.