1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2009 2011 Sandro Santilli <strk@keybit.net>
7 * Copyright (C) 2005-2006 Refractions Research Inc.
8 * Copyright (C) 2001-2002 Vivid Solutions Inc.
10 * This is free software; you can redistribute and/or modify it under
11 * the terms of the GNU Lesser General Public Licence as published
12 * by the Free Software Foundation.
13 * See the COPYING file for more information.
15 **********************************************************************
17 * Last port: geom/Geometry.java rev. 1.112
19 **********************************************************************/
21 #include <geos/geom/BinaryOp.h>
22 #include <geos/geom/Geometry.h>
23 #include <geos/geom/GeometryFactory.h>
24 #include <geos/geom/PrecisionModel.h>
25 #include <geos/geom/CoordinateSequence.h>
26 #include <geos/geom/GeometryComponentFilter.h>
27 #include <geos/geom/GeometryFilter.h>
28 #include <geos/geom/GeometryCollection.h>
29 #include <geos/geom/Point.h>
30 #include <geos/geom/MultiPoint.h>
31 #include <geos/geom/LineString.h>
32 #include <geos/geom/LinearRing.h>
33 #include <geos/geom/MultiLineString.h>
34 #include <geos/geom/MultiPolygon.h>
35 #include <geos/geom/IntersectionMatrix.h>
36 #include <geos/util/IllegalArgumentException.h>
37 #include <geos/algorithm/CentroidPoint.h>
38 #include <geos/algorithm/CentroidLine.h>
39 #include <geos/algorithm/CentroidArea.h>
40 #include <geos/algorithm/InteriorPointPoint.h>
41 #include <geos/algorithm/InteriorPointLine.h>
42 #include <geos/algorithm/InteriorPointArea.h>
43 #include <geos/algorithm/ConvexHull.h>
44 #include <geos/operation/predicate/RectangleContains.h>
45 #include <geos/operation/predicate/RectangleIntersects.h>
46 #include <geos/operation/relate/RelateOp.h>
47 #include <geos/operation/valid/IsValidOp.h>
48 #include <geos/operation/overlay/OverlayOp.h>
49 #include <geos/operation/union/UnaryUnionOp.h>
50 #include <geos/operation/overlay/snap/SnapIfNeededOverlayOp.h>
51 #include <geos/operation/buffer/BufferOp.h>
52 #include <geos/operation/distance/DistanceOp.h>
53 #include <geos/operation/IsSimpleOp.h>
54 #include <geos/io/WKBWriter.h>
55 #include <geos/io/WKTWriter.h>
56 #include <geos/version.h>
65 #define SHORTCIRCUIT_PREDICATES 1
68 using namespace geos::algorithm
;
69 using namespace geos::operation::valid
;
70 using namespace geos::operation::relate
;
71 using namespace geos::operation::buffer
;
72 using namespace geos::operation::overlay
;
73 using namespace geos::operation::overlay::snap
;
74 using namespace geos::operation::distance
;
75 using namespace geos::operation
;
78 namespace geom
{ // geos::geom
82 * Return current GEOS version
91 * Return the version of JTS this GEOS
92 * release has been ported from.
100 Geometry::GeometryChangedFilter
Geometry::geometryChangedFilter
;
102 // REMOVE THIS, use GeometryFactory::getDefaultInstance() directly
103 const GeometryFactory
* Geometry::INTERNAL_GEOMETRY_FACTORY
=GeometryFactory::getDefaultInstance();
105 Geometry::Geometry(const GeometryFactory
*newFactory
)
111 if ( factory
== NULL
) {
112 factory
= INTERNAL_GEOMETRY_FACTORY
;
114 SRID
=factory
->getSRID();
117 Geometry::Geometry(const Geometry
&geom
)
119 SRID(geom
.getSRID()),
120 factory(geom
.factory
),
123 if ( geom
.envelope
.get() )
125 envelope
.reset(new Envelope(*(geom
.envelope
)));
127 //factory=geom.factory;
128 //envelope(new Envelope(*(geom.envelope.get())));
129 //SRID=geom.getSRID();
134 Geometry::hasNonEmptyElements(const vector
<Geometry
*>* geometries
)
136 for (size_t i
=0; i
<geometries
->size(); i
++) {
137 if (!(*geometries
)[i
]->isEmpty()) {
145 Geometry::hasNullElements(const CoordinateSequence
* list
)
147 size_t npts
=list
->getSize();
148 for (size_t i
=0; i
<npts
; ++i
) {
149 if (list
->getAt(i
).isNull()) {
157 Geometry::hasNullElements(const vector
<Geometry
*>* lrs
)
159 size_t n
=lrs
->size();
160 for (size_t i
=0; i
<n
; ++i
) {
161 if ((*lrs
)[i
]==NULL
) {
170 Geometry::isWithinDistance(const Geometry
*geom
,double cDistance
) const
172 const Envelope
*env0
=getEnvelopeInternal();
173 const Envelope
*env1
=geom
->getEnvelopeInternal();
174 double envDist
=env0
->distance(env1
);
177 if (envDist
>cDistance
)
181 // NOTE: this could be implemented more efficiently
182 double geomDist
=distance(geom
);
183 if (geomDist
>cDistance
)
192 Geometry::getCentroid() const
195 if ( ! getCentroid(centPt
) ) return NULL
;
197 // We don't use createPointFromInternalCoord here
198 // because ::getCentroid() takes care about rounding
199 Point
*pt
=getFactory()->createPoint(centPt
);
205 Geometry::getCentroid(Coordinate
& ret
) const
207 if ( isEmpty() ) { return false; }
211 int dim
=getDimension();
215 if ( ! cent
.getCentroid(c
) )
220 if ( ! cent
.getCentroid(c
) )
225 if ( ! cent
.getCentroid(c
) )
229 getPrecisionModel()->makePrecise(c
);
236 Geometry::getInteriorPoint() const
238 Coordinate interiorPt
;
239 int dim
=getDimension();
241 InteriorPointPoint
intPt(this);
242 if ( ! intPt
.getInteriorPoint(interiorPt
) ) return NULL
;
244 InteriorPointLine
intPt(this);
245 if ( ! intPt
.getInteriorPoint(interiorPt
) ) return NULL
;
247 InteriorPointArea
intPt(this);
248 if ( ! intPt
.getInteriorPoint(interiorPt
) ) return NULL
;
250 Point
*p
=getFactory()->createPointFromInternalCoord(&interiorPt
, this);
255 * Notifies this Geometry that its Coordinates have been changed by an external
256 * party (using a CoordinateFilter, for example). The Geometry will flush
257 * and/or update any information it has cached (such as its {@link Envelope} ).
260 Geometry::geometryChanged()
262 apply_rw(&geometryChangedFilter
);
266 * Notifies this Geometry that its Coordinates have been changed by an external
267 * party. When geometryChanged is called, this method will be called for
268 * this Geometry and its component Geometries.
269 * @see apply(GeometryComponentFilter *)
272 Geometry::geometryChangedAction()
275 envelope
.reset(NULL
);
279 Geometry::isValid() const
281 return IsValidOp(this).isValid();
285 Geometry::getEnvelope() const
287 return getFactory()->toGeometry(getEnvelopeInternal());
291 Geometry::getEnvelopeInternal() const
293 if (!envelope
.get()) {
294 envelope
= computeEnvelopeInternal();
296 return envelope
.get();
300 Geometry::disjoint(const Geometry
*g
) const
302 #ifdef SHORTCIRCUIT_PREDICATES
303 // short-circuit test
304 if (! getEnvelopeInternal()->intersects(g
->getEnvelopeInternal()))
307 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
308 bool res
=im
->isDisjoint();
313 Geometry::touches(const Geometry
*g
) const
315 #ifdef SHORTCIRCUIT_PREDICATES
316 // short-circuit test
317 if (! getEnvelopeInternal()->intersects(g
->getEnvelopeInternal()))
320 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
321 bool res
=im
->isTouches(getDimension(), g
->getDimension());
326 Geometry::intersects(const Geometry
*g
) const
328 #ifdef SHORTCIRCUIT_PREDICATES
329 // short-circuit test
330 if (! getEnvelopeInternal()->intersects(g
->getEnvelopeInternal()))
335 * TODO: (MD) Add optimizations:
338 * If P is in env(A), test for point-in-poly
341 * If env(A1).overlaps(env(A2))
342 * test for overlaps via point-in-poly first (both ways)
343 * Possibly optimize selection of point to test by finding point of A1
344 * closest to centre of env(A2).
345 * (Is there a test where we shouldn't bother - e.g. if env A
346 * is much smaller than env B, maybe there's no point in testing
350 // optimization for rectangle arguments
352 const Polygon
* p
= dynamic_cast<const Polygon
*>(this);
353 return predicate::RectangleIntersects::intersects(*p
, *g
);
355 if (g
->isRectangle()) {
356 const Polygon
* p
= dynamic_cast<const Polygon
*>(g
);
357 return predicate::RectangleIntersects::intersects(*p
, *this);
360 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
361 bool res
=im
->isIntersects();
367 Geometry::covers(const Geometry
* g
) const
369 #ifdef SHORTCIRCUIT_PREDICATES
370 // short-circuit test
371 if (! getEnvelopeInternal()->covers(g
->getEnvelopeInternal()))
375 // optimization for rectangle arguments
377 // since we have already tested that the test envelope
382 auto_ptr
<IntersectionMatrix
> im(relate(g
));
383 return im
->isCovers();
388 Geometry::crosses(const Geometry
*g
) const
390 #ifdef SHORTCIRCUIT_PREDICATES
391 // short-circuit test
392 if (! getEnvelopeInternal()->intersects(g
->getEnvelopeInternal()))
395 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
396 bool res
=im
->isCrosses(getDimension(), g
->getDimension());
401 Geometry::within(const Geometry
*g
) const
403 return g
->contains(this);
407 Geometry::contains(const Geometry
*g
) const
409 #ifdef SHORTCIRCUIT_PREDICATES
410 // short-circuit test
411 if (! getEnvelopeInternal()->contains(g
->getEnvelopeInternal()))
415 // optimization for rectangle arguments
417 const Polygon
* p
= dynamic_cast<const Polygon
*>(this);
418 return predicate::RectangleContains::contains(*p
, *g
);
420 // Incorrect: contains is not commutative
421 //if (g->isRectangle()) {
422 // return predicate::RectangleContains::contains((const Polygon&)*g, *this);
425 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
426 bool res
=im
->isContains();
431 Geometry::overlaps(const Geometry
*g
) const
433 #ifdef SHORTCIRCUIT_PREDICATES
434 // short-circuit test
435 if (! getEnvelopeInternal()->intersects(g
->getEnvelopeInternal()))
438 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
439 bool res
=im
->isOverlaps(getDimension(), g
->getDimension());
444 Geometry::relate(const Geometry
*g
, const string
&intersectionPattern
) const
446 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
447 bool res
=im
->matches(intersectionPattern
);
452 Geometry::equals(const Geometry
*g
) const
454 #ifdef SHORTCIRCUIT_PREDICATES
455 // short-circuit test
456 if (! getEnvelopeInternal()->equals(g
->getEnvelopeInternal()))
459 auto_ptr
<IntersectionMatrix
> im ( relate(g
) );
460 bool res
=im
->isEquals(getDimension(), g
->getDimension());
465 Geometry::relate(const Geometry
*other
) const
466 //throw(IllegalArgumentException *)
468 return RelateOp::relate(this, other
);
472 Geometry::toString() const
478 operator<< (std::ostream
& os
, const Geometry
& geom
)
480 io::WKBWriter writer
;
481 writer
.writeHEX(geom
, os
);
486 Geometry::toText() const
488 io::WKTWriter writer
;
489 return writer
.write(this);
493 Geometry::buffer(double distance
) const
495 return BufferOp::bufferOp(this, distance
);
499 Geometry::buffer(double distance
,int quadrantSegments
) const
501 return BufferOp::bufferOp(this, distance
, quadrantSegments
);
505 Geometry::buffer(double distance
, int quadrantSegments
, int endCapStyle
) const
507 return BufferOp::bufferOp(this, distance
, quadrantSegments
, endCapStyle
);
511 Geometry::convexHull() const
513 return ConvexHull(this).getConvexHull();
517 Geometry::intersection(const Geometry
*other
) const
520 * TODO: MD - add optimization for P-A case using Point-In-Polygon
523 // special case: if one input is empty ==> empty
524 if (isEmpty() || other
->isEmpty() )
526 return getFactory()->createGeometryCollection();
529 return BinaryOp(this, other
, overlayOp(OverlayOp::opINTERSECTION
)).release();
533 Geometry::Union(const Geometry
*other
) const
534 //throw(TopologyException *, IllegalArgumentException *)
537 // special case: if one input is empty ==> other input
538 if ( isEmpty() ) return other
->clone();
539 if ( other
->isEmpty() ) return clone();
541 Geometry
*out
= NULL
;
543 #ifdef SHORTCIRCUIT_PREDICATES
544 // if envelopes are disjoint return a MULTI geom or
545 // a geometrycollection
546 if ( ! getEnvelopeInternal()->intersects(other
->getEnvelopeInternal()) )
548 //cerr<<"SHORTCIRCUITED-UNION engaged"<<endl;
549 const GeometryCollection
*coll
;
551 size_t ngeomsThis
= getNumGeometries();
552 size_t ngeomsOther
= other
->getNumGeometries();
554 // Allocated for ownership transfer
555 vector
<Geometry
*> *v
= new vector
<Geometry
*>();
556 v
->reserve(ngeomsThis
+ngeomsOther
);
559 if ( NULL
!= (coll
= dynamic_cast<const GeometryCollection
*>(this)) )
561 for (size_t i
=0; i
<ngeomsThis
; ++i
)
562 v
->push_back(coll
->getGeometryN(i
)->clone());
564 v
->push_back(this->clone());
567 if ( NULL
!= (coll
= dynamic_cast<const GeometryCollection
*>(other
)) )
569 for (size_t i
=0; i
<ngeomsOther
; ++i
)
570 v
->push_back(coll
->getGeometryN(i
)->clone());
572 v
->push_back(other
->clone());
575 out
= factory
->buildGeometry(v
);
580 return BinaryOp(this, other
, overlayOp(OverlayOp::opUNION
)).release();
585 Geometry::Union() const
587 using geos::operation::geounion::UnaryUnionOp
;
588 return UnaryUnionOp::Union(*this);
592 Geometry::difference(const Geometry
*other
) const
593 //throw(IllegalArgumentException *)
595 // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A
596 if (isEmpty()) return getFactory()->createGeometryCollection();
597 if (other
->isEmpty()) return clone();
599 return BinaryOp(this, other
, overlayOp(OverlayOp::opDIFFERENCE
)).release();
603 Geometry::symDifference(const Geometry
*other
) const
605 // special case: if either input is empty ==> other input
606 if ( isEmpty() ) return other
->clone();
607 if ( other
->isEmpty() ) return clone();
609 return BinaryOp(this, other
, overlayOp(OverlayOp::opSYMDIFFERENCE
)).release();
613 Geometry::compareTo(const Geometry
*geom
) const
616 if ( this == geom
) return 0;
618 if (getClassSortIndex()!=geom
->getClassSortIndex()) {
619 return getClassSortIndex()-geom
->getClassSortIndex();
621 if (isEmpty() && geom
->isEmpty()) {
627 if (geom
->isEmpty()) {
630 return compareToSameClass(geom
);
634 Geometry::isEquivalentClass(const Geometry
*other
) const
636 if (typeid(*this)==typeid(*other
))
643 Geometry::checkNotGeometryCollection(const Geometry
*g
)
644 //throw(IllegalArgumentException *)
646 if ((typeid(*g
)==typeid(GeometryCollection
))) {
647 throw geos::util::IllegalArgumentException("This method does not support GeometryCollection arguments\n");
652 Geometry::getClassSortIndex() const
654 //const type_info &t=typeid(*this);
656 if ( typeid(*this) == typeid(Point
) ) return 0;
657 else if ( typeid(*this) == typeid(MultiPoint
) ) return 1;
658 else if ( typeid(*this) == typeid(LineString
) ) return 2;
659 else if ( typeid(*this) == typeid(LinearRing
) ) return 3;
660 else if ( typeid(*this) == typeid(MultiLineString
) ) return 4;
661 else if ( typeid(*this) == typeid(Polygon
) ) return 5;
662 else if ( typeid(*this) == typeid(MultiPolygon
) ) return 6;
665 assert(typeid(*this) == typeid(GeometryCollection
)); // unsupported class
670 string str
="Class not supported: ";
671 str
.append(typeid(*this).name());
673 Assert::shouldNeverReachHere(str
);
679 Geometry::compare(vector
<Coordinate
> a
, vector
<Coordinate
> b
) const
683 while (i
<a
.size() && j
<b
.size()) {
684 Coordinate
& aCoord
=a
[i
];
685 Coordinate
& bCoord
=b
[j
];
686 int comparison
=aCoord
.compareTo(bCoord
);
703 Geometry::compare(vector
<Geometry
*> a
, vector
<Geometry
*> b
) const
707 while (i
<a
.size() && j
<b
.size()) {
708 Geometry
*aGeom
=a
[i
];
709 Geometry
*bGeom
=b
[j
];
710 int comparison
=aGeom
->compareTo(bGeom
);
727 * Returns the minimum distance between this Geometry
728 * and the other Geometry
730 * @param other the Geometry from which to compute the distance
733 Geometry::distance(const Geometry
*other
) const
735 return DistanceOp::distance(this, other
);
739 * Returns the area of this <code>Geometry</code>.
740 * Areal Geometries have a non-zero area.
741 * They override this function to compute the area.
744 * @return the area of the Geometry
747 Geometry::getArea() const
753 * Returns the length of this <code>Geometry</code>.
754 * Linear geometries return their length.
755 * Areal geometries return their perimeter.
756 * They override this function to compute the area.
759 * @return the length of the Geometry
762 Geometry::getLength() const
767 Geometry::~Geometry()
773 GeometryGreaterThen::operator()(const Geometry
*first
, const Geometry
*second
)
775 if (first
->compareTo(second
)>0)
782 Geometry::equal(const Coordinate
& a
, const Coordinate
& b
,
783 double tolerance
) const
787 return a
== b
; // 2D only !!!
789 //double dist=a.distance(b);
790 return a
.distance(b
)<=tolerance
;
794 Geometry::apply_ro(GeometryFilter
*filter
) const
796 filter
->filter_ro(this);
800 Geometry::apply_rw(GeometryFilter
*filter
)
802 filter
->filter_rw(this);
806 Geometry::apply_ro(GeometryComponentFilter
*filter
) const
808 filter
->filter_ro(this);
812 Geometry::apply_rw(GeometryComponentFilter
*filter
)
814 filter
->filter_rw(this);
818 Geometry::isSimple() const
820 checkNotGeometryCollection(this);
821 operation::IsSimpleOp
op(*this);
822 return op
.isSimple();
826 const PrecisionModel
*
827 Geometry::getPrecisionModel() const
829 return factory
->getPrecisionModel();
832 } // namespace geos::geom