1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 2011 Sandro Santilli <strk@keybit.net>
7 * Copyright (C) 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: operation/distance/DistanceOp.java r335 (JTS-1.12-)
19 **********************************************************************/
21 #include <geos/operation/distance/DistanceOp.h>
22 #include <geos/operation/distance/GeometryLocation.h>
23 #include <geos/operation/distance/ConnectedElementLocationFilter.h>
24 #include <geos/algorithm/PointLocator.h>
25 #include <geos/algorithm/CGAlgorithms.h>
26 #include <geos/geom/Coordinate.h>
27 #include <geos/geom/CoordinateSequence.h>
28 #include <geos/geom/CoordinateArraySequence.h>
29 #include <geos/geom/LineString.h>
30 #include <geos/geom/Point.h>
31 #include <geos/geom/Polygon.h>
32 #include <geos/geom/Envelope.h>
33 #include <geos/geom/LineSegment.h>
34 #include <geos/geom/util/PolygonExtracter.h>
35 #include <geos/geom/util/LinearComponentExtracter.h>
36 #include <geos/geom/util/PointExtracter.h>
37 #include <geos/util/IllegalArgumentException.h>
47 using namespace geos::geom
;
48 //using namespace geos::algorithm;
51 namespace operation
{ // geos.operation
52 namespace distance
{ // geos.operation.distance
55 //using namespace geom::util;
57 /*public static (deprecated)*/
59 DistanceOp::distance(const Geometry
*g0
, const Geometry
*g1
)
61 DistanceOp
distOp(g0
,g1
);
62 return distOp
.distance();
67 DistanceOp::distance(const Geometry
& g0
, const Geometry
& g1
)
69 DistanceOp
distOp(g0
,g1
);
70 return distOp
.distance();
73 /*public static deprecated*/
75 DistanceOp::closestPoints(const Geometry
*g0
, const Geometry
*g1
)
77 DistanceOp
distOp(g0
,g1
);
78 return distOp
.nearestPoints();
83 DistanceOp::nearestPoints(const Geometry
*g0
, const Geometry
*g1
)
85 DistanceOp
distOp(g0
,g1
);
86 return distOp
.nearestPoints();
89 DistanceOp::DistanceOp(const Geometry
*g0
, const Geometry
*g1
):
91 terminateDistance(0.0),
92 minDistanceLocation(0),
93 minDistance(DoubleMax
)
99 DistanceOp::DistanceOp(const Geometry
& g0
, const Geometry
& g1
):
101 terminateDistance(0.0),
102 minDistanceLocation(0),
103 minDistance(DoubleMax
)
109 DistanceOp::DistanceOp(const Geometry
& g0
, const Geometry
& g1
, double tdist
)
112 terminateDistance(tdist
),
113 minDistanceLocation(0),
114 minDistance(DoubleMax
)
120 DistanceOp::~DistanceOp()
123 for (i
=0; i
<newCoords
.size(); i
++) delete newCoords
[i
];
124 if ( minDistanceLocation
)
126 for (i
=0; i
<minDistanceLocation
->size(); i
++)
128 delete (*minDistanceLocation
)[i
];
130 delete minDistanceLocation
;
135 * Report the distance between the closest points on the input geometries.
137 * @return the distance between the geometries
140 DistanceOp::distance()
142 using geos::util::IllegalArgumentException
;
144 if ( geom
[0] == 0 || geom
[1] == 0 )
145 throw IllegalArgumentException("null geometries are not supported");
146 if ( geom
[0]->isEmpty() || geom
[1]->isEmpty() ) return 0.0;
147 computeMinDistance();
153 DistanceOp::closestPoints()
155 return nearestPoints();
161 DistanceOp::nearestPoints()
163 // lazily creates minDistanceLocation
164 computeMinDistance();
166 assert(0 != minDistanceLocation
);
167 std::vector
<GeometryLocation
*>& locs
= *minDistanceLocation
;
169 // Empty input geometries result in this behaviour
170 if ( locs
[0] == 0 || locs
[1] == 0 )
172 // either both or none are set..
173 assert(locs
[0] == 0 && locs
[1] == 0);
178 GeometryLocation
* loc0
= locs
[0];
179 GeometryLocation
* loc1
= locs
[1];
180 const Coordinate
& c0
= loc0
->getCoordinate();
181 const Coordinate
& c1
= loc1
->getCoordinate();
183 CoordinateSequence
* nearestPts
= new CoordinateArraySequence();
191 vector
<GeometryLocation
*>* DistanceOp::nearestLocations(){
192 computeMinDistance();
193 return minDistanceLocation
;
197 DistanceOp::updateMinDistance(vector
<GeometryLocation
*>& locGeom
, bool flip
)
199 assert(minDistanceLocation
);
201 // if not set then don't update
202 if (locGeom
[0]==NULL
)
205 std::cerr
<< "updateMinDistance called with loc[0] == null and loc[1] == " << locGeom
[1] << std::endl
;
207 assert(locGeom
[1] == NULL
);
211 delete (*minDistanceLocation
)[0];
212 delete (*minDistanceLocation
)[1];
214 (*minDistanceLocation
)[0]=locGeom
[1];
215 (*minDistanceLocation
)[1]=locGeom
[0];
217 (*minDistanceLocation
)[0]=locGeom
[0];
218 (*minDistanceLocation
)[1]=locGeom
[1];
224 DistanceOp::computeMinDistance()
226 // only compute once!
227 if (minDistanceLocation
) return;
230 std::cerr
<< "---Start: " << geom
[0]->toString() << " - " << geom
[1]->toString() << std::endl
;
233 minDistanceLocation
= new vector
<GeometryLocation
*>(2);
235 computeContainmentDistance();
237 if (minDistance
<= terminateDistance
)
242 computeFacetDistance();
245 std::cerr
<< "---End " << std::endl
;
251 DistanceOp::computeContainmentDistance()
253 using geom::util::PolygonExtracter
;
255 Polygon::ConstVect polys1
;
256 PolygonExtracter::getPolygons(*(geom
[1]), polys1
);
260 std::cerr
<< "PolygonExtracter found " << polys1
.size() << " polygons in geometry 2" << std::endl
;
264 // Expected to fill minDistanceLocation items
265 // if minDistance <= terminateDistance
267 vector
<GeometryLocation
*> *locPtPoly
= new vector
<GeometryLocation
*>(2);
268 // test if either geometry has a vertex inside the other
269 if ( ! polys1
.empty() )
271 vector
<GeometryLocation
*> *insideLocs0
=
272 ConnectedElementLocationFilter::getLocations(geom
[0]);
273 computeInside(insideLocs0
, polys1
, locPtPoly
);
274 if (minDistance
<= terminateDistance
) {
275 assert( (*locPtPoly
)[0] );
276 assert( (*locPtPoly
)[1] );
277 (*minDistanceLocation
)[0] = (*locPtPoly
)[0];
278 (*minDistanceLocation
)[1] = (*locPtPoly
)[1];
280 for (size_t i
=0; i
<insideLocs0
->size(); i
++)
282 GeometryLocation
*l
= (*insideLocs0
)[i
];
283 if ( l
!= (*minDistanceLocation
)[0] &&
284 l
!= (*minDistanceLocation
)[1] )
292 for (size_t i
=0; i
<insideLocs0
->size(); i
++)
293 delete (*insideLocs0
)[i
];
297 Polygon::ConstVect polys0
;
298 PolygonExtracter::getPolygons(*(geom
[0]), polys0
);
301 std::cerr
<< "PolygonExtracter found " << polys0
.size() << " polygons in geometry 1" << std::endl
;
305 if ( ! polys0
.empty() )
307 vector
<GeometryLocation
*> *insideLocs1
= ConnectedElementLocationFilter::getLocations(geom
[1]);
308 computeInside(insideLocs1
, polys0
, locPtPoly
);
309 if (minDistance
<= terminateDistance
) {
310 // flip locations, since we are testing geom 1 VS geom 0
311 assert( (*locPtPoly
)[0] );
312 assert( (*locPtPoly
)[1] );
313 (*minDistanceLocation
)[0] = (*locPtPoly
)[1];
314 (*minDistanceLocation
)[1] = (*locPtPoly
)[0];
316 for (size_t i
=0; i
<insideLocs1
->size(); i
++)
318 GeometryLocation
*l
= (*insideLocs1
)[i
];
319 if ( l
!= (*minDistanceLocation
)[0] &&
320 l
!= (*minDistanceLocation
)[1] )
328 for (size_t i
=0; i
<insideLocs1
->size(); i
++)
329 delete (*insideLocs1
)[i
];
335 // If minDistance <= terminateDistance we must have
336 // set minDistanceLocations to some non-null item
337 assert( minDistance
> terminateDistance
||
338 ( (*minDistanceLocation
)[0] && (*minDistanceLocation
)[1] ) );
344 DistanceOp::computeInside(vector
<GeometryLocation
*> *locs
,
345 const Polygon::ConstVect
& polys
,
346 vector
<GeometryLocation
*> *locPtPoly
)
348 for (size_t i
=0, ni
=locs
->size(); i
<ni
; ++i
)
350 GeometryLocation
*loc
=(*locs
)[i
];
351 for (size_t j
=0, nj
=polys
.size(); j
<nj
; ++j
)
353 computeInside(loc
, polys
[j
], locPtPoly
);
354 if (minDistance
<=terminateDistance
) return;
361 DistanceOp::computeInside(GeometryLocation
*ptLoc
,
363 vector
<GeometryLocation
*> *locPtPoly
)
365 const Coordinate
&pt
=ptLoc
->getCoordinate();
367 // if pt is not in exterior, distance to geom is 0
368 if (Location::EXTERIOR
!=ptLocator
.locate(pt
, static_cast<const Geometry
*>(poly
)))
371 (*locPtPoly
)[0] = ptLoc
;
372 GeometryLocation
*locPoly
= new GeometryLocation(poly
, pt
);
373 (*locPtPoly
)[1] = locPoly
;
380 DistanceOp::computeFacetDistance()
382 using geom::util::LinearComponentExtracter
;
383 using geom::util::PointExtracter
;
385 vector
<GeometryLocation
*> locGeom(2);
388 * Geometries are not wholely inside, so compute distance from lines
390 * of one to lines and points of the other
392 LineString::ConstVect lines0
;
393 LineString::ConstVect lines1
;
394 LinearComponentExtracter::getLines(*(geom
[0]), lines0
);
395 LinearComponentExtracter::getLines(*(geom
[1]), lines1
);
398 std::cerr
<< "LinearComponentExtracter found "
399 << lines0
.size() << " lines in geometry 1 and "
400 << lines1
.size() << " lines in geometry 2 "
404 Point::ConstVect pts0
;
405 Point::ConstVect pts1
;
406 PointExtracter::getPoints(*(geom
[0]), pts0
);
407 PointExtracter::getPoints(*(geom
[1]), pts1
);
410 std::cerr
<< "PointExtracter found "
411 << pts0
.size() << " points in geometry 1 and "
412 << pts1
.size() << " points in geometry 2 "
416 // exit whenever minDistance goes LE than terminateDistance
417 computeMinDistanceLines(lines0
, lines1
, locGeom
);
418 updateMinDistance(locGeom
, false);
419 if (minDistance
<= terminateDistance
) {
421 std::cerr
<< "Early termination after line-line distance" << std::endl
;
428 computeMinDistanceLinesPoints(lines0
, pts1
, locGeom
);
429 updateMinDistance(locGeom
, false);
430 if (minDistance
<= terminateDistance
) {
432 std::cerr
<< "Early termination after lines0-points1 distance" << std::endl
;
439 computeMinDistanceLinesPoints(lines1
, pts0
, locGeom
);
440 updateMinDistance(locGeom
, true);
441 if (minDistance
<= terminateDistance
){
443 std::cerr
<< "Early termination after lines1-points0 distance" << std::endl
;
450 computeMinDistancePoints(pts0
, pts1
, locGeom
);
451 updateMinDistance(locGeom
, false);
454 std::cerr
<< "termination after pts-pts distance" << std::endl
;
460 DistanceOp::computeMinDistanceLines(
461 const LineString::ConstVect
& lines0
,
462 const LineString::ConstVect
& lines1
,
463 vector
<GeometryLocation
*>& locGeom
)
465 for (size_t i
=0, ni
=lines0
.size(); i
<ni
; ++i
)
467 const LineString
*line0
=lines0
[i
];
468 for (size_t j
=0, nj
=lines1
.size(); j
<nj
; ++j
) {
469 const LineString
*line1
=lines1
[j
];
470 computeMinDistance(line0
, line1
, locGeom
);
471 if (minDistance
<=terminateDistance
) return;
478 DistanceOp::computeMinDistancePoints(
479 const Point::ConstVect
& points0
,
480 const Point::ConstVect
& points1
,
481 vector
<GeometryLocation
*>& locGeom
)
483 for (size_t i
=0, ni
=points0
.size(); i
<ni
; ++i
)
485 const Point
*pt0
= points0
[i
];
486 for (size_t j
=0, nj
=points1
.size(); j
<nj
; ++j
)
488 const Point
*pt1
= points1
[j
];
489 double dist
= pt0
->getCoordinate()->distance(*(pt1
->getCoordinate()));
492 std::cerr
<< "Distance "
493 << pt0
->toString() << " - "
494 << pt1
->toString() << ": "
495 << dist
<< ", minDistance: " << minDistance
499 if (dist
< minDistance
)
502 // this is wrong - need to determine closest points on both segments!!!
504 locGeom
[0] = new GeometryLocation(pt0
, 0, *(pt0
->getCoordinate()));
506 locGeom
[1] = new GeometryLocation(pt1
, 0, *(pt1
->getCoordinate()));
509 if (minDistance
<=terminateDistance
) return;
516 DistanceOp::computeMinDistanceLinesPoints(
517 const LineString::ConstVect
& lines
,
518 const Point::ConstVect
& points
,
519 vector
<GeometryLocation
*>& locGeom
)
521 for (size_t i
=0;i
<lines
.size();i
++)
523 const LineString
*line
=lines
[i
];
524 for (size_t j
=0;j
<points
.size();j
++)
526 const Point
*pt
=points
[j
];
527 computeMinDistance(line
,pt
,locGeom
);
528 if (minDistance
<=terminateDistance
) return;
535 DistanceOp::computeMinDistance(
536 const LineString
*line0
,
537 const LineString
*line1
,
538 vector
<GeometryLocation
*>& locGeom
)
540 using geos::algorithm::CGAlgorithms
;
542 const Envelope
*env0
=line0
->getEnvelopeInternal();
543 const Envelope
*env1
=line1
->getEnvelopeInternal();
544 if (env0
->distance(env1
)>minDistance
) {
548 const CoordinateSequence
*coord0
=line0
->getCoordinatesRO();
549 const CoordinateSequence
*coord1
=line1
->getCoordinatesRO();
550 size_t npts0
=coord0
->getSize();
551 size_t npts1
=coord1
->getSize();
553 // brute force approach!
554 for(size_t i
=0; i
<npts0
-1; ++i
)
556 for(size_t j
=0; j
<npts1
-1; ++j
)
558 double dist
=CGAlgorithms::distanceLineLine(coord0
->getAt(i
),coord0
->getAt(i
+1),
559 coord1
->getAt(j
),coord1
->getAt(j
+1));
560 if (dist
< minDistance
) {
562 LineSegment
seg0(coord0
->getAt(i
), coord0
->getAt(i
+ 1));
563 LineSegment
seg1(coord1
->getAt(j
), coord1
->getAt(j
+ 1));
564 CoordinateSequence
* closestPt
= seg0
.closestPoints(seg1
);
565 Coordinate
*c1
= new Coordinate(closestPt
->getAt(0));
566 Coordinate
*c2
= new Coordinate(closestPt
->getAt(1));
567 newCoords
.push_back(c1
);
568 newCoords
.push_back(c2
);
572 locGeom
[0] = new GeometryLocation(line0
, i
, *c1
);
574 locGeom
[1] = new GeometryLocation(line1
, j
, *c2
);
576 if (minDistance
<=terminateDistance
) return;
583 DistanceOp::computeMinDistance(const LineString
*line
,
585 vector
<GeometryLocation
*>& locGeom
)
587 using geos::algorithm::CGAlgorithms
;
589 const Envelope
*env0
=line
->getEnvelopeInternal();
590 const Envelope
*env1
=pt
->getEnvelopeInternal();
591 if (env0
->distance(env1
)>minDistance
) {
594 const CoordinateSequence
*coord0
=line
->getCoordinatesRO();
595 Coordinate
*coord
=new Coordinate(*(pt
->getCoordinate()));
596 newCoords
.push_back(coord
);
598 // brute force approach!
599 size_t npts0
=coord0
->getSize();
600 for(size_t i
=0; i
<npts0
-1; ++i
)
602 double dist
=CGAlgorithms::distancePointLine(*coord
,coord0
->getAt(i
),coord0
->getAt(i
+1));
603 if (dist
< minDistance
) {
605 LineSegment
seg(coord0
->getAt(i
), coord0
->getAt(i
+ 1));
606 Coordinate segClosestPoint
;
607 seg
.closestPoint(*coord
, segClosestPoint
);
610 locGeom
[0] = new GeometryLocation(line
, i
, segClosestPoint
);
612 locGeom
[1] = new GeometryLocation(pt
, 0, *coord
);
614 if (minDistance
<=terminateDistance
) return;
620 DistanceOp::isWithinDistance(const geom::Geometry
& g0
,
621 const geom::Geometry
& g1
,
624 DistanceOp
distOp(g0
, g1
, distance
);
625 return distOp
.distance() <= distance
;
628 } // namespace geos.operation.distance
629 } // namespace geos.operation
632 /**********************************************************************
634 * Revision 1.24 2006/06/12 11:29:24 strk
635 * unsigned int => size_t
637 * Revision 1.23 2006/03/24 09:52:41 strk
638 * USE_INLINE => GEOS_INLINE
640 * Revision 1.22 2006/03/23 12:12:01 strk
641 * Fixes to allow build with -DGEOS_INLINE
643 * Revision 1.21 2006/03/21 17:55:01 strk
644 * opDistance.h header split
646 * Revision 1.20 2006/03/03 10:46:21 strk
647 * Removed 'using namespace' from headers, added missing headers in .cpp files, removed useless includes in headers (bug#46)
648 **********************************************************************/