Fix EMPTY return from single-point lines and zero-length polygons
[geos.git] / src / geom / Geometry.cpp
blobb8b8fac5fb31f785ecd2758bec2ecdfc28782391
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/Centroid.h>
38 #include <geos/algorithm/InteriorPointPoint.h>
39 #include <geos/algorithm/InteriorPointLine.h>
40 #include <geos/algorithm/InteriorPointArea.h>
41 #include <geos/algorithm/ConvexHull.h>
42 #include <geos/operation/predicate/RectangleContains.h>
43 #include <geos/operation/predicate/RectangleIntersects.h>
44 #include <geos/operation/relate/RelateOp.h>
45 #include <geos/operation/valid/IsValidOp.h>
46 #include <geos/operation/overlay/OverlayOp.h>
47 #include <geos/operation/union/UnaryUnionOp.h>
48 #include <geos/operation/overlay/snap/SnapIfNeededOverlayOp.h>
49 #include <geos/operation/buffer/BufferOp.h>
50 #include <geos/operation/distance/DistanceOp.h>
51 #include <geos/operation/IsSimpleOp.h>
52 #include <geos/io/WKBWriter.h>
53 #include <geos/io/WKTWriter.h>
54 #include <geos/version.h>
56 #include <algorithm>
57 #include <string>
58 #include <typeinfo>
59 #include <vector>
60 #include <cassert>
61 #include <memory>
63 #define SHORTCIRCUIT_PREDICATES 1
65 using namespace std;
66 using namespace geos::algorithm;
67 using namespace geos::operation::valid;
68 using namespace geos::operation::relate;
69 using namespace geos::operation::buffer;
70 using namespace geos::operation::overlay;
71 using namespace geos::operation::overlay::snap;
72 using namespace geos::operation::distance;
73 using namespace geos::operation;
75 namespace geos {
76 namespace geom { // geos::geom
80 * Return current GEOS version
82 string
83 geosversion()
85 return GEOS_VERSION;
89 * Return the version of JTS this GEOS
90 * release has been ported from.
92 string
93 jtsport()
95 return GEOS_JTS_PORT;
98 Geometry::GeometryChangedFilter Geometry::geometryChangedFilter;
100 Geometry::Geometry(const GeometryFactory *newFactory)
102 envelope(NULL),
103 factory(newFactory),
104 userData(NULL)
106 if ( factory == NULL ) {
107 factory = GeometryFactory::getDefaultInstance();
109 SRID=factory->getSRID();
112 Geometry::Geometry(const Geometry &geom)
114 SRID(geom.getSRID()),
115 factory(geom.factory),
116 userData(NULL)
118 if ( geom.envelope.get() )
120 envelope.reset(new Envelope(*(geom.envelope)));
122 //factory=geom.factory;
123 //envelope(new Envelope(*(geom.envelope.get())));
124 //SRID=geom.getSRID();
125 //userData=NULL;
128 bool
129 Geometry::hasNonEmptyElements(const vector<Geometry *>* geometries)
131 for (size_t i=0; i<geometries->size(); i++) {
132 if (!(*geometries)[i]->isEmpty()) {
133 return true;
136 return false;
139 bool
140 Geometry::hasNullElements(const CoordinateSequence* list)
142 size_t npts=list->getSize();
143 for (size_t i=0; i<npts; ++i) {
144 if (list->getAt(i).isNull()) {
145 return true;
148 return false;
151 bool
152 Geometry::hasNullElements(const vector<Geometry *>* lrs)
154 size_t n=lrs->size();
155 for (size_t i=0; i<n; ++i) {
156 if ((*lrs)[i]==NULL) {
157 return true;
160 return false;
163 /* public */
164 bool
165 Geometry::isWithinDistance(const Geometry *geom,double cDistance) const
167 const Envelope *env0=getEnvelopeInternal();
168 const Envelope *env1=geom->getEnvelopeInternal();
169 double envDist=env0->distance(env1);
170 //delete env0;
171 //delete env1;
172 if (envDist>cDistance)
174 return false;
176 // NOTE: this could be implemented more efficiently
177 double geomDist=distance(geom);
178 if (geomDist>cDistance)
180 return false;
182 return true;
185 /*public*/
186 Point*
187 Geometry::getCentroid() const
189 Coordinate centPt;
190 if ( ! getCentroid(centPt) ) return NULL;
192 // We don't use createPointFromInternalCoord here
193 // because ::getCentroid() takes care about rounding
194 Point *pt=getFactory()->createPoint(centPt);
195 return pt;
198 /*public*/
199 bool
200 Geometry::getCentroid(Coordinate& ret) const
202 if ( isEmpty() ) { return false; }
203 if ( ! Centroid::getCentroid(*this, ret) ) return false;
204 getPrecisionModel()->makePrecise(ret); // not in JTS
205 return true;
208 Point*
209 Geometry::getInteriorPoint() const
211 Coordinate interiorPt;
212 int dim=getDimension();
213 if (dim==0) {
214 InteriorPointPoint intPt(this);
215 if ( ! intPt.getInteriorPoint(interiorPt) ) return NULL;
216 } else if (dim==1) {
217 InteriorPointLine intPt(this);
218 if ( ! intPt.getInteriorPoint(interiorPt) ) return NULL;
219 } else {
220 InteriorPointArea intPt(this);
221 if ( ! intPt.getInteriorPoint(interiorPt) ) return NULL;
223 Point *p=getFactory()->createPointFromInternalCoord(&interiorPt, this);
224 return p;
228 * Notifies this Geometry that its Coordinates have been changed by an external
229 * party (using a CoordinateFilter, for example). The Geometry will flush
230 * and/or update any information it has cached (such as its {@link Envelope} ).
232 void
233 Geometry::geometryChanged()
235 apply_rw(&geometryChangedFilter);
239 * Notifies this Geometry that its Coordinates have been changed by an external
240 * party. When geometryChanged is called, this method will be called for
241 * this Geometry and its component Geometries.
242 * @see apply(GeometryComponentFilter *)
244 void
245 Geometry::geometryChangedAction()
247 //delete envelope;
248 envelope.reset(NULL);
251 bool
252 Geometry::isValid() const
254 return IsValidOp(this).isValid();
257 Geometry*
258 Geometry::getEnvelope() const
260 return getFactory()->toGeometry(getEnvelopeInternal());
263 const Envelope *
264 Geometry::getEnvelopeInternal() const
266 if (!envelope.get()) {
267 envelope = computeEnvelopeInternal();
269 return envelope.get();
272 bool
273 Geometry::disjoint(const Geometry *g) const
275 #ifdef SHORTCIRCUIT_PREDICATES
276 // short-circuit test
277 if (! getEnvelopeInternal()->intersects(g->getEnvelopeInternal()))
278 return true;
279 #endif
280 auto_ptr<IntersectionMatrix> im ( relate(g) );
281 bool res=im->isDisjoint();
282 return res;
285 bool
286 Geometry::touches(const Geometry *g) const
288 #ifdef SHORTCIRCUIT_PREDICATES
289 // short-circuit test
290 if (! getEnvelopeInternal()->intersects(g->getEnvelopeInternal()))
291 return false;
292 #endif
293 auto_ptr<IntersectionMatrix> im ( relate(g) );
294 bool res=im->isTouches(getDimension(), g->getDimension());
295 return res;
298 bool
299 Geometry::intersects(const Geometry *g) const
301 #ifdef SHORTCIRCUIT_PREDICATES
302 // short-circuit test
303 if (! getEnvelopeInternal()->intersects(g->getEnvelopeInternal()))
304 return false;
305 #endif
308 * TODO: (MD) Add optimizations:
310 * - for P-A case:
311 * If P is in env(A), test for point-in-poly
313 * - for A-A case:
314 * If env(A1).overlaps(env(A2))
315 * test for overlaps via point-in-poly first (both ways)
316 * Possibly optimize selection of point to test by finding point of A1
317 * closest to centre of env(A2).
318 * (Is there a test where we shouldn't bother - e.g. if env A
319 * is much smaller than env B, maybe there's no point in testing
320 * pt(B) in env(A)?
323 // optimization for rectangle arguments
324 if (isRectangle()) {
325 const Polygon* p = dynamic_cast<const Polygon*>(this);
326 return predicate::RectangleIntersects::intersects(*p, *g);
328 if (g->isRectangle()) {
329 const Polygon* p = dynamic_cast<const Polygon*>(g);
330 return predicate::RectangleIntersects::intersects(*p, *this);
333 auto_ptr<IntersectionMatrix> im ( relate(g) );
334 bool res=im->isIntersects();
335 return res;
338 /*public*/
339 bool
340 Geometry::covers(const Geometry* g) const
342 #ifdef SHORTCIRCUIT_PREDICATES
343 // short-circuit test
344 if (! getEnvelopeInternal()->covers(g->getEnvelopeInternal()))
345 return false;
346 #endif
348 // optimization for rectangle arguments
349 if (isRectangle()) {
350 // since we have already tested that the test envelope
351 // is covered
352 return true;
355 auto_ptr<IntersectionMatrix> im(relate(g));
356 return im->isCovers();
360 bool
361 Geometry::crosses(const Geometry *g) const
363 #ifdef SHORTCIRCUIT_PREDICATES
364 // short-circuit test
365 if (! getEnvelopeInternal()->intersects(g->getEnvelopeInternal()))
366 return false;
367 #endif
368 auto_ptr<IntersectionMatrix> im ( relate(g) );
369 bool res=im->isCrosses(getDimension(), g->getDimension());
370 return res;
373 bool
374 Geometry::within(const Geometry *g) const
376 return g->contains(this);
379 bool
380 Geometry::contains(const Geometry *g) const
382 #ifdef SHORTCIRCUIT_PREDICATES
383 // short-circuit test
384 if (! getEnvelopeInternal()->contains(g->getEnvelopeInternal()))
385 return false;
386 #endif
388 // optimization for rectangle arguments
389 if (isRectangle()) {
390 const Polygon* p = dynamic_cast<const Polygon*>(this);
391 return predicate::RectangleContains::contains(*p, *g);
393 // Incorrect: contains is not commutative
394 //if (g->isRectangle()) {
395 // return predicate::RectangleContains::contains((const Polygon&)*g, *this);
398 auto_ptr<IntersectionMatrix> im ( relate(g) );
399 bool res=im->isContains();
400 return res;
403 bool
404 Geometry::overlaps(const Geometry *g) const
406 #ifdef SHORTCIRCUIT_PREDICATES
407 // short-circuit test
408 if (! getEnvelopeInternal()->intersects(g->getEnvelopeInternal()))
409 return false;
410 #endif
411 auto_ptr<IntersectionMatrix> im ( relate(g) );
412 bool res=im->isOverlaps(getDimension(), g->getDimension());
413 return res;
416 bool
417 Geometry::relate(const Geometry *g, const string &intersectionPattern) const
419 auto_ptr<IntersectionMatrix> im ( relate(g) );
420 bool res=im->matches(intersectionPattern);
421 return res;
424 bool
425 Geometry::equals(const Geometry *g) const
427 #ifdef SHORTCIRCUIT_PREDICATES
428 // short-circuit test
429 if (! getEnvelopeInternal()->equals(g->getEnvelopeInternal()))
430 return false;
431 #endif
432 auto_ptr<IntersectionMatrix> im ( relate(g) );
433 bool res=im->isEquals(getDimension(), g->getDimension());
434 return res;
437 IntersectionMatrix*
438 Geometry::relate(const Geometry *other) const
439 //throw(IllegalArgumentException *)
441 return RelateOp::relate(this, other);
444 string
445 Geometry::toString() const
447 return toText();
450 std::ostream&
451 operator<< (std::ostream& os, const Geometry& geom)
453 io::WKBWriter writer;
454 writer.writeHEX(geom, os);
455 return os;
458 string
459 Geometry::toText() const
461 io::WKTWriter writer;
462 return writer.write(this);
465 Geometry*
466 Geometry::buffer(double distance) const
468 return BufferOp::bufferOp(this, distance);
471 Geometry*
472 Geometry::buffer(double distance,int quadrantSegments) const
474 return BufferOp::bufferOp(this, distance, quadrantSegments);
477 Geometry*
478 Geometry::buffer(double distance, int quadrantSegments, int endCapStyle) const
480 return BufferOp::bufferOp(this, distance, quadrantSegments, endCapStyle);
483 Geometry*
484 Geometry::convexHull() const
486 return ConvexHull(this).getConvexHull();
489 Geometry*
490 Geometry::intersection(const Geometry *other) const
493 * TODO: MD - add optimization for P-A case using Point-In-Polygon
496 // special case: if one input is empty ==> empty
497 if (isEmpty() || other->isEmpty() )
499 return getFactory()->createGeometryCollection();
502 return BinaryOp(this, other, overlayOp(OverlayOp::opINTERSECTION)).release();
505 Geometry*
506 Geometry::Union(const Geometry *other) const
507 //throw(TopologyException *, IllegalArgumentException *)
510 // special case: if one input is empty ==> other input
511 if ( isEmpty() ) return other->clone();
512 if ( other->isEmpty() ) return clone();
514 Geometry *out = NULL;
516 #ifdef SHORTCIRCUIT_PREDICATES
517 // if envelopes are disjoint return a MULTI geom or
518 // a geometrycollection
519 if ( ! getEnvelopeInternal()->intersects(other->getEnvelopeInternal()) )
521 //cerr<<"SHORTCIRCUITED-UNION engaged"<<endl;
522 const GeometryCollection *coll;
524 size_t ngeomsThis = getNumGeometries();
525 size_t ngeomsOther = other->getNumGeometries();
527 // Allocated for ownership transfer
528 vector<Geometry *> *v = new vector<Geometry *>();
529 v->reserve(ngeomsThis+ngeomsOther);
532 if ( NULL != (coll = dynamic_cast<const GeometryCollection *>(this)) )
534 for (size_t i=0; i<ngeomsThis; ++i)
535 v->push_back(coll->getGeometryN(i)->clone());
536 } else {
537 v->push_back(this->clone());
540 if ( NULL != (coll = dynamic_cast<const GeometryCollection *>(other)) )
542 for (size_t i=0; i<ngeomsOther; ++i)
543 v->push_back(coll->getGeometryN(i)->clone());
544 } else {
545 v->push_back(other->clone());
548 out = factory->buildGeometry(v);
549 return out;
551 #endif
553 return BinaryOp(this, other, overlayOp(OverlayOp::opUNION)).release();
556 /* public */
557 Geometry::AutoPtr
558 Geometry::Union() const
560 using geos::operation::geounion::UnaryUnionOp;
561 return UnaryUnionOp::Union(*this);
564 Geometry*
565 Geometry::difference(const Geometry *other) const
566 //throw(IllegalArgumentException *)
568 // special case: if A.isEmpty ==> empty; if B.isEmpty ==> A
569 if (isEmpty()) return getFactory()->createGeometryCollection();
570 if (other->isEmpty()) return clone();
572 return BinaryOp(this, other, overlayOp(OverlayOp::opDIFFERENCE)).release();
575 Geometry*
576 Geometry::symDifference(const Geometry *other) const
578 // special case: if either input is empty ==> other input
579 if ( isEmpty() ) return other->clone();
580 if ( other->isEmpty() ) return clone();
582 // if envelopes are disjoint return a MULTI geom or
583 // a geometrycollection
584 if ( ! getEnvelopeInternal()->intersects(other->getEnvelopeInternal()) )
586 const GeometryCollection *coll;
588 size_t ngeomsThis = getNumGeometries();
589 size_t ngeomsOther = other->getNumGeometries();
591 // Allocated for ownership transfer
592 vector<Geometry *> *v = new vector<Geometry *>();
593 v->reserve(ngeomsThis+ngeomsOther);
596 if ( NULL != (coll = dynamic_cast<const GeometryCollection *>(this)) )
598 for (size_t i=0; i<ngeomsThis; ++i)
599 v->push_back(coll->getGeometryN(i)->clone());
600 } else {
601 v->push_back(this->clone());
604 if ( NULL != (coll = dynamic_cast<const GeometryCollection *>(other)) )
606 for (size_t i=0; i<ngeomsOther; ++i)
607 v->push_back(coll->getGeometryN(i)->clone());
608 } else {
609 v->push_back(other->clone());
612 return factory->buildGeometry(v);
615 return BinaryOp(this, other, overlayOp(OverlayOp::opSYMDIFFERENCE)).release();
619 Geometry::compareTo(const Geometry *geom) const
621 // compare to self
622 if ( this == geom ) return 0;
624 if (getClassSortIndex()!=geom->getClassSortIndex()) {
625 return getClassSortIndex()-geom->getClassSortIndex();
627 if (isEmpty() && geom->isEmpty()) {
628 return 0;
630 if (isEmpty()) {
631 return -1;
633 if (geom->isEmpty()) {
634 return 1;
636 return compareToSameClass(geom);
639 bool
640 Geometry::isEquivalentClass(const Geometry *other) const
642 if (typeid(*this)==typeid(*other))
643 return true;
644 else
645 return false;
648 void
649 Geometry::checkNotGeometryCollection(const Geometry *g)
650 //throw(IllegalArgumentException *)
652 if ((typeid(*g)==typeid(GeometryCollection))) {
653 throw geos::util::IllegalArgumentException("This method does not support GeometryCollection arguments\n");
658 Geometry::getClassSortIndex() const
660 //const type_info &t=typeid(*this);
662 if ( typeid(*this) == typeid(Point) ) return 0;
663 else if ( typeid(*this) == typeid(MultiPoint) ) return 1;
664 else if ( typeid(*this) == typeid(LineString) ) return 2;
665 else if ( typeid(*this) == typeid(LinearRing) ) return 3;
666 else if ( typeid(*this) == typeid(MultiLineString) ) return 4;
667 else if ( typeid(*this) == typeid(Polygon) ) return 5;
668 else if ( typeid(*this) == typeid(MultiPolygon) ) return 6;
669 else
671 assert(typeid(*this) == typeid(GeometryCollection)); // unsupported class
672 return 7;
675 #if 0
676 string str="Class not supported: ";
677 str.append(typeid(*this).name());
678 str.append("");
679 Assert::shouldNeverReachHere(str);
680 return -1;
681 #endif
685 Geometry::compare(vector<Coordinate> a, vector<Coordinate> b) const
687 size_t i=0;
688 size_t j=0;
689 while (i<a.size() && j<b.size()) {
690 Coordinate& aCoord=a[i];
691 Coordinate& bCoord=b[j];
692 int comparison=aCoord.compareTo(bCoord);
693 if (comparison!=0) {
694 return comparison;
696 i++;
697 j++;
699 if (i<a.size()) {
700 return 1;
702 if (j<b.size()) {
703 return -1;
705 return 0;
709 Geometry::compare(vector<Geometry *> a, vector<Geometry *> b) const
711 size_t i=0;
712 size_t j=0;
713 while (i<a.size() && j<b.size()) {
714 Geometry *aGeom=a[i];
715 Geometry *bGeom=b[j];
716 int comparison=aGeom->compareTo(bGeom);
717 if (comparison!=0) {
718 return comparison;
720 i++;
721 j++;
723 if (i<a.size()) {
724 return 1;
726 if (j<b.size()) {
727 return -1;
729 return 0;
733 * Returns the minimum distance between this Geometry
734 * and the other Geometry
736 * @param other the Geometry from which to compute the distance
738 double
739 Geometry::distance(const Geometry *other) const
741 return DistanceOp::distance(this, other);
745 * Returns the area of this <code>Geometry</code>.
746 * Areal Geometries have a non-zero area.
747 * They override this function to compute the area.
748 * Others return 0.0
750 * @return the area of the Geometry
752 double
753 Geometry::getArea() const
755 return 0.0;
759 * Returns the length of this <code>Geometry</code>.
760 * Linear geometries return their length.
761 * Areal geometries return their perimeter.
762 * They override this function to compute the area.
763 * Others return 0.0
765 * @return the length of the Geometry
767 double
768 Geometry::getLength() const
770 return 0.0;
773 Geometry::~Geometry()
775 //delete envelope;
778 bool
779 GeometryGreaterThen::operator()(const Geometry *first, const Geometry *second)
781 if (first->compareTo(second)>0)
782 return true;
783 else
784 return false;
787 bool
788 Geometry::equal(const Coordinate& a, const Coordinate& b,
789 double tolerance) const
791 if (tolerance==0)
793 return a == b; // 2D only !!!
795 //double dist=a.distance(b);
796 return a.distance(b)<=tolerance;
799 void
800 Geometry::apply_ro(GeometryFilter *filter) const
802 filter->filter_ro(this);
805 void
806 Geometry::apply_rw(GeometryFilter *filter)
808 filter->filter_rw(this);
811 void
812 Geometry::apply_ro(GeometryComponentFilter *filter) const
814 filter->filter_ro(this);
817 void
818 Geometry::apply_rw(GeometryComponentFilter *filter)
820 filter->filter_rw(this);
823 bool
824 Geometry::isSimple() const
826 checkNotGeometryCollection(this);
827 operation::IsSimpleOp op(*this);
828 return op.isSimple();
831 /* public */
832 const PrecisionModel*
833 Geometry::getPrecisionModel() const
835 return factory->getPrecisionModel();
838 } // namespace geos::geom
839 } // namespace geos