EVOLUTION-MANAGER
Edit File: Geometry.cpp
/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2009 2011 Sandro Santilli <strk@kbt.io> * Copyright (C) 2005-2006 Refractions Research Inc. * Copyright (C) 2001-2002 Vivid Solutions Inc. * * 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: geom/Geometry.java rev. 1.112 * **********************************************************************/ #include <geos/geom/BinaryOp.h> #include <geos/geom/Geometry.h> #include <geos/geom/GeometryFactory.h> #include <geos/geom/PrecisionModel.h> #include <geos/geom/CoordinateSequence.h> #include <geos/geom/GeometryComponentFilter.h> #include <geos/geom/GeometryFilter.h> #include <geos/geom/GeometryCollection.h> #include <geos/geom/Point.h> #include <geos/geom/MultiPoint.h> #include <geos/geom/LineString.h> #include <geos/geom/LinearRing.h> #include <geos/geom/MultiLineString.h> #include <geos/geom/MultiPolygon.h> #include <geos/geom/IntersectionMatrix.h> #include <geos/util/IllegalArgumentException.h> #include <geos/algorithm/Centroid.h> #include <geos/algorithm/InteriorPointPoint.h> #include <geos/algorithm/InteriorPointLine.h> #include <geos/algorithm/InteriorPointArea.h> #include <geos/algorithm/ConvexHull.h> #include <geos/operation/intersection/Rectangle.h> #include <geos/operation/intersection/RectangleIntersection.h> #include <geos/operation/predicate/RectangleContains.h> #include <geos/operation/predicate/RectangleIntersects.h> #include <geos/operation/relate/RelateOp.h> #include <geos/operation/valid/IsValidOp.h> #include <geos/operation/overlay/OverlayOp.h> #include <geos/operation/union/UnaryUnionOp.h> #include <geos/operation/overlay/snap/SnapIfNeededOverlayOp.h> #include <geos/operation/buffer/BufferOp.h> #include <geos/operation/distance/DistanceOp.h> #include <geos/operation/IsSimpleOp.h> #include <geos/io/WKBWriter.h> #include <geos/io/WKTWriter.h> #include <geos/version.h> #include <algorithm> #include <string> #include <typeinfo> #include <vector> #include <cassert> #include <memory> #ifdef _MSC_VER # ifdef MSVC_USE_VLD # include <vld.h> # endif #endif #define SHORTCIRCUIT_PREDICATES 1 //#define USE_RECTANGLE_INTERSECTION 1 using namespace std; using namespace geos::algorithm; using namespace geos::operation::valid; using namespace geos::operation::relate; using namespace geos::operation::buffer; using namespace geos::operation::overlay; using namespace geos::operation::overlay::snap; using namespace geos::operation::distance; using namespace geos::operation; namespace geos { namespace geom { // geos::geom /* * Return current GEOS version */ string geosversion() { return GEOS_VERSION; } /* * Return the version of JTS this GEOS * release has been ported from. */ string jtsport() { return GEOS_JTS_PORT; } Geometry::GeometryChangedFilter Geometry::geometryChangedFilter; Geometry::Geometry(const GeometryFactory* newFactory) : envelope(nullptr), _factory(newFactory), _userData(nullptr) { if(_factory == nullptr) { _factory = GeometryFactory::getDefaultInstance(); } SRID = _factory->getSRID(); _factory->addRef(); } Geometry::Geometry(const Geometry& geom) : SRID(geom.getSRID()), _factory(geom._factory), _userData(nullptr) { if(geom.envelope.get()) { envelope.reset(new Envelope(*(geom.envelope))); } //factory=geom.factory; //envelope(new Envelope(*(geom.envelope.get()))); //SRID=geom.getSRID(); //_userData=NULL; _factory->addRef(); } bool Geometry::hasNullElements(const CoordinateSequence* list) { size_t npts = list->getSize(); for(size_t i = 0; i < npts; ++i) { if(list->getAt(i).isNull()) { return true; } } return false; } /* public */ bool Geometry::isWithinDistance(const Geometry* geom, double cDistance) const { const Envelope* env0 = getEnvelopeInternal(); const Envelope* env1 = geom->getEnvelopeInternal(); double envDist = env0->distance(env1); if(envDist > cDistance) { return false; } // NOTE: this could be implemented more efficiently double geomDist = distance(geom); if(geomDist > cDistance) { return false; } return true; } /*public*/ std::unique_ptr<Point> Geometry::getCentroid() const { Coordinate centPt; if(! getCentroid(centPt)) { return nullptr; } // We don't use createPointFromInternalCoord here // because ::getCentroid() takes care about rounding return std::unique_ptr<Point>(getFactory()->createPoint(centPt)); } /*public*/ bool Geometry::getCentroid(Coordinate& ret) const { if(isEmpty()) { return false; } if(! Centroid::getCentroid(*this, ret)) { return false; } getPrecisionModel()->makePrecise(ret); // not in JTS return true; } std::unique_ptr<Point> Geometry::getInteriorPoint() const { Coordinate interiorPt; int dim = getDimension(); if(dim == 0) { InteriorPointPoint intPt(this); if(! intPt.getInteriorPoint(interiorPt)) { return nullptr; } } else if(dim == 1) { InteriorPointLine intPt(this); if(! intPt.getInteriorPoint(interiorPt)) { return nullptr; } } else { InteriorPointArea intPt(this); if(! intPt.getInteriorPoint(interiorPt)) { return nullptr; } } std::unique_ptr<Point> p(getFactory()->createPointFromInternalCoord(&interiorPt, this)); return p; } /** * Notifies this Geometry that its Coordinates have been changed by an external * party (using a CoordinateFilter, for example). The Geometry will flush * and/or update any information it has cached (such as its {@link Envelope} ). */ void Geometry::geometryChanged() { apply_rw(&geometryChangedFilter); } /** * Notifies this Geometry that its Coordinates have been changed by an external * party. When geometryChanged is called, this method will be called for * this Geometry and its component Geometries. * @see apply(GeometryComponentFilter *) */ void Geometry::geometryChangedAction() { envelope.reset(nullptr); } bool Geometry::isValid() const { return IsValidOp(this).isValid(); } std::unique_ptr<Geometry> Geometry::getEnvelope() const { return std::unique_ptr<Geometry>(getFactory()->toGeometry(getEnvelopeInternal())); } const Envelope* Geometry::getEnvelopeInternal() const { if(!envelope.get()) { envelope = computeEnvelopeInternal(); } return envelope.get(); } bool Geometry::disjoint(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { return true; } #endif unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isDisjoint(); return res; } bool Geometry::touches(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { return false; } #endif unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isTouches(getDimension(), g->getDimension()); return res; } bool Geometry::intersects(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { return false; } #endif /** * TODO: (MD) Add optimizations: * * - for P-A case: * If P is in env(A), test for point-in-poly * * - for A-A case: * If env(A1).overlaps(env(A2)) * test for overlaps via point-in-poly first (both ways) * Possibly optimize selection of point to test by finding point of A1 * closest to centre of env(A2). * (Is there a test where we shouldn't bother - e.g. if env A * is much smaller than env B, maybe there's no point in testing * pt(B) in env(A)? */ // optimization for rectangle arguments if(isRectangle()) { const Polygon* p = dynamic_cast<const Polygon*>(this); return predicate::RectangleIntersects::intersects(*p, *g); } if(g->isRectangle()) { const Polygon* p = dynamic_cast<const Polygon*>(g); return predicate::RectangleIntersects::intersects(*p, *this); } unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isIntersects(); return res; } /*public*/ bool Geometry::covers(const Geometry* g) const { // optimization - lower dimension cannot cover areas if(g->getDimension() == 2 && getDimension() < 2) { return false; } // optimization - P cannot cover a non-zero-length L // Note that a point can cover a zero-length lineal geometry if(g->getDimension() == 1 && getDimension() < 1 && g->getLength() > 0.0) { return false; } #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->covers(g->getEnvelopeInternal())) { return false; } #endif // optimization for rectangle arguments if(isRectangle()) { // since we have already tested that the test envelope // is covered return true; } unique_ptr<IntersectionMatrix> im(relate(g)); return im->isCovers(); } bool Geometry::crosses(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { return false; } #endif unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isCrosses(getDimension(), g->getDimension()); return res; } bool Geometry::within(const Geometry* g) const { return g->contains(this); } bool Geometry::contains(const Geometry* g) const { // optimization - lower dimension cannot contain areas if(g->getDimension() == 2 && getDimension() < 2) { return false; } // optimization - P cannot contain a non-zero-length L // Note that a point can contain a zero-length lineal geometry, // since the line has no boundary due to Mod-2 Boundary Rule if(g->getDimension() == 1 && getDimension() < 1 && g->getLength() > 0.0) { return false; } #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->contains(g->getEnvelopeInternal())) { return false; } #endif // optimization for rectangle arguments if(isRectangle()) { const Polygon* p = dynamic_cast<const Polygon*>(this); return predicate::RectangleContains::contains(*p, *g); } // Incorrect: contains is not commutative //if (g->isRectangle()) { // return predicate::RectangleContains::contains((const Polygon&)*g, *this); //} unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isContains(); return res; } bool Geometry::overlaps(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->intersects(g->getEnvelopeInternal())) { return false; } #endif unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isOverlaps(getDimension(), g->getDimension()); return res; } bool Geometry::relate(const Geometry* g, const string& intersectionPattern) const { unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->matches(intersectionPattern); return res; } bool Geometry::equals(const Geometry* g) const { #ifdef SHORTCIRCUIT_PREDICATES // short-circuit test if(! getEnvelopeInternal()->equals(g->getEnvelopeInternal())) { return false; } #endif if(isEmpty()) { return g->isEmpty(); } else if(g->isEmpty()) { return isEmpty(); } unique_ptr<IntersectionMatrix> im(relate(g)); bool res = im->isEquals(getDimension(), g->getDimension()); return res; } std::unique_ptr<IntersectionMatrix> Geometry::relate(const Geometry* other) const { return RelateOp::relate(this, other); } string Geometry::toString() const { return toText(); } std::ostream& operator<< (std::ostream& os, const Geometry& geom) { io::WKBWriter writer; writer.writeHEX(geom, os); return os; } string Geometry::toText() const { io::WKTWriter writer; return writer.write(this); } std::unique_ptr<Geometry> Geometry::buffer(double p_distance) const { return std::unique_ptr<Geometry>(BufferOp::bufferOp(this, p_distance)); } std::unique_ptr<Geometry> Geometry::buffer(double p_distance, int quadrantSegments) const { return std::unique_ptr<Geometry>(BufferOp::bufferOp(this, p_distance, quadrantSegments)); } std::unique_ptr<Geometry> Geometry::buffer(double p_distance, int quadrantSegments, int endCapStyle) const { return std::unique_ptr<Geometry>(BufferOp::bufferOp(this, p_distance, quadrantSegments, endCapStyle)); } std::unique_ptr<Geometry> Geometry::convexHull() const { return ConvexHull(this).getConvexHull(); } std::unique_ptr<Geometry> Geometry::intersection(const Geometry* other) const { /** * TODO: MD - add optimization for P-A case using Point-In-Polygon */ // special case: if one input is empty ==> empty if(isEmpty() || other->isEmpty()) { return std::unique_ptr<Geometry>(getFactory()->createGeometryCollection()); } #ifdef USE_RECTANGLE_INTERSECTION // optimization for rectangle arguments using operation::intersection::Rectangle; using operation::intersection::RectangleIntersection; if(isRectangle()) { const Envelope* env = getEnvelopeInternal(); Rectangle rect(env->getMinX(), env->getMinY(), env->getMaxX(), env->getMaxY()); return RectangleIntersection::clip(*other, rect).release(); } if(other->isRectangle()) { const Envelope* env = other->getEnvelopeInternal(); Rectangle rect(env->getMinX(), env->getMinY(), env->getMaxX(), env->getMaxY()); return RectangleIntersection::clip(*this, rect).release(); } #endif return BinaryOp(this, other, overlayOp(OverlayOp::opINTERSECTION)); } std::unique_ptr<Geometry> Geometry::Union(const Geometry* other) const { // special case: if one input is empty ==> other input if(isEmpty()) { return other->clone(); } if(other->isEmpty()) { return clone(); } #ifdef SHORTCIRCUIT_PREDICATES // if envelopes are disjoint return a MULTI geom or // a geometrycollection if(! getEnvelopeInternal()->intersects(other->getEnvelopeInternal())) { //cerr<<"SHORTCIRCUITED-UNION engaged"<<endl; const GeometryCollection* coll; size_t ngeomsThis = getNumGeometries(); size_t ngeomsOther = other->getNumGeometries(); // Allocated for ownership transfer vector<Geometry*>* v = new vector<Geometry*>(); v->reserve(ngeomsThis + ngeomsOther); if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(this))) { for(size_t i = 0; i < ngeomsThis; ++i) { v->push_back(coll->getGeometryN(i)->clone().release()); } } else { v->push_back(this->clone().release()); } if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(other))) { for(size_t i = 0; i < ngeomsOther; ++i) { v->push_back(coll->getGeometryN(i)->clone().release()); } } else { v->push_back(other->clone().release()); } std::unique_ptr<Geometry>out(_factory->buildGeometry(v)); return out; } #endif return BinaryOp(this, other, overlayOp(OverlayOp::opUNION)); } /* public */ Geometry::Ptr Geometry::Union() const { using geos::operation::geounion::UnaryUnionOp; return UnaryUnionOp::Union(*this); } std::unique_ptr<Geometry> Geometry::difference(const Geometry* other) const //throw(IllegalArgumentException *) { // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A if(isEmpty()) { return std::unique_ptr<Geometry>(getFactory()->createGeometryCollection()); } if(other->isEmpty()) { return clone(); } return BinaryOp(this, other, overlayOp(OverlayOp::opDIFFERENCE)); } std::unique_ptr<Geometry> Geometry::symDifference(const Geometry* other) const { // special case: if either input is empty ==> other input if(isEmpty()) { return other->clone(); } if(other->isEmpty()) { return clone(); } // if envelopes are disjoint return a MULTI geom or // a geometrycollection if(! getEnvelopeInternal()->intersects(other->getEnvelopeInternal())) { const GeometryCollection* coll; size_t ngeomsThis = getNumGeometries(); size_t ngeomsOther = other->getNumGeometries(); // Allocated for ownership transfer vector<Geometry*>* v = new vector<Geometry*>(); v->reserve(ngeomsThis + ngeomsOther); if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(this))) { for(size_t i = 0; i < ngeomsThis; ++i) { v->push_back(coll->getGeometryN(i)->clone().release()); } } else { v->push_back(this->clone().release()); } if(nullptr != (coll = dynamic_cast<const GeometryCollection*>(other))) { for(size_t i = 0; i < ngeomsOther; ++i) { v->push_back(coll->getGeometryN(i)->clone().release()); } } else { v->push_back(other->clone().release()); } return std::unique_ptr<Geometry>(_factory->buildGeometry(v)); } return BinaryOp(this, other, overlayOp(OverlayOp::opSYMDIFFERENCE)); } int Geometry::compareTo(const Geometry* geom) const { // compare to self if(this == geom) { return 0; } if(getSortIndex() != geom->getSortIndex()) { int diff = getSortIndex() - geom->getSortIndex(); return (diff > 0) - (diff < 0); // signum() } if(isEmpty() && geom->isEmpty()) { return 0; } if(isEmpty()) { return -1; } if(geom->isEmpty()) { return 1; } return compareToSameClass(geom); } bool Geometry::isEquivalentClass(const Geometry* other) const { if(typeid(*this) == typeid(*other)) { return true; } else { return false; } } /*public static*/ void Geometry::checkNotGeometryCollection(const Geometry* g) //throw(IllegalArgumentException *) { if(g->getSortIndex() == SORTINDEX_GEOMETRYCOLLECTION) { throw geos::util::IllegalArgumentException("This method does not support GeometryCollection arguments\n"); } } void Geometry::GeometryChangedFilter::filter_rw(Geometry* geom) { geom->geometryChangedAction(); } int Geometry::compare(vector<Coordinate> a, vector<Coordinate> b) const { size_t i = 0; size_t j = 0; while(i < a.size() && j < b.size()) { Coordinate& aCoord = a[i]; Coordinate& bCoord = b[j]; int comparison = aCoord.compareTo(bCoord); if(comparison != 0) { return comparison; } i++; j++; } if(i < a.size()) { return 1; } if(j < b.size()) { return -1; } return 0; } int Geometry::compare(vector<Geometry*> a, vector<Geometry*> b) const { size_t i = 0; size_t j = 0; while(i < a.size() && j < b.size()) { Geometry* aGeom = a[i]; Geometry* bGeom = b[j]; int comparison = aGeom->compareTo(bGeom); if(comparison != 0) { return comparison; } i++; j++; } if(i < a.size()) { return 1; } if(j < b.size()) { return -1; } return 0; } int Geometry::compare(const std::vector<std::unique_ptr<Geometry>> & a, const std::vector<std::unique_ptr<Geometry>> & b) const { size_t i = 0; size_t j = 0; while(i < a.size() && j < b.size()) { Geometry* aGeom = a[i].get(); Geometry* bGeom = b[j].get(); int comparison = aGeom->compareTo(bGeom); if(comparison != 0) { return comparison; } i++; j++; } if(i < a.size()) { return 1; } if(j < b.size()) { return -1; } return 0; } /** * Returns the minimum distance between this Geometry * and the other Geometry * * @param other the Geometry from which to compute the distance */ double Geometry::distance(const Geometry* other) const { return DistanceOp::distance(this, other); } /** * Returns the area of this <code>Geometry</code>. * Areal Geometries have a non-zero area. * They override this function to compute the area. * Others return 0.0 * * @return the area of the Geometry */ double Geometry::getArea() const { return 0.0; } /** * Returns the length of this <code>Geometry</code>. * Linear geometries return their length. * Areal geometries return their perimeter. * They override this function to compute the area. * Others return 0.0 * * @return the length of the Geometry */ double Geometry::getLength() const { return 0.0; } Geometry::~Geometry() { _factory->dropRef(); } bool GeometryGreaterThen::operator()(const Geometry* first, const Geometry* second) { if(first->compareTo(second) > 0) { return true; } else { return false; } } bool Geometry::equal(const Coordinate& a, const Coordinate& b, double tolerance) const { if(tolerance == 0) { return a == b; // 2D only !!! } //double dist=a.distance(b); return a.distance(b) <= tolerance; } void Geometry::apply_ro(GeometryFilter* filter) const { filter->filter_ro(this); } void Geometry::apply_rw(GeometryFilter* filter) { filter->filter_rw(this); } void Geometry::apply_ro(GeometryComponentFilter* filter) const { filter->filter_ro(this); } void Geometry::apply_rw(GeometryComponentFilter* filter) { filter->filter_rw(this); } bool Geometry::isSimple() const { operation::IsSimpleOp op(*this); return op.isSimple(); } /* public */ const PrecisionModel* Geometry::getPrecisionModel() const { return _factory->getPrecisionModel(); } } // namespace geos::geom } // namespace geos