1 /**********************************************************************
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
6 * Copyright (C) 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: algorithm/ConvexHull.java r407 (JTS-1.12+)
19 **********************************************************************/
21 #include <geos/algorithm/ConvexHull.h>
22 #include <geos/algorithm/CGAlgorithms.h>
23 #include <geos/geom/GeometryFactory.h>
24 #include <geos/geom/Coordinate.h>
25 #include <geos/geom/Point.h>
26 #include <geos/geom/Polygon.h>
27 #include <geos/geom/LineString.h>
28 #include <geos/geom/CoordinateSequence.h>
29 #include <geos/geom/CoordinateSequenceFactory.h>
35 # include "geos/algorithm/ConvexHull.inl"
38 using namespace geos::geom
;
41 namespace algorithm
{ // geos.algorithm
46 * This is the class used to sort in radial order.
47 * It's defined here as it's a static one.
49 class RadiallyLessThen
{
53 const Coordinate
*origin
;
56 polarCompare(const Coordinate
*o
, const Coordinate
*p
,
64 int orient
= CGAlgorithms::computeOrientation(*o
, *p
, *q
);
67 if (orient
== CGAlgorithms::COUNTERCLOCKWISE
) return 1;
68 if (orient
== CGAlgorithms::CLOCKWISE
) return -1;
70 // points are collinear - check distance
71 double op
= dxp
* dxp
+ dyp
* dyp
;
72 double oq
= dxq
* dxq
+ dyq
* dyq
;
84 RadiallyLessThen(const Coordinate
*c
): origin(c
) {}
86 bool operator() (const Coordinate
*p1
, const Coordinate
*p2
)
88 return ( polarCompare(origin
, p1
, p2
) == -1 );
93 } // unnamed namespace
97 ConvexHull::toCoordinateSequence(Coordinate::ConstVect
&cv
)
99 const CoordinateSequenceFactory
*csf
=
100 geomFactory
->getCoordinateSequenceFactory();
102 // Create a new Coordinate::Vect for feeding it to
103 // the CoordinateSequenceFactory
104 Coordinate::Vect
*vect
=new Coordinate::Vect();
107 vect
->reserve(n
); // avoid multiple reallocs
109 for (size_t i
=0; i
<n
; ++i
)
111 vect
->push_back(*(cv
[i
])); // Coordinate copy
114 return csf
->create(vect
); // takes ownership of the vector
120 ConvexHull::computeOctPts(const Coordinate::ConstVect
&inputPts
,
121 Coordinate::ConstVect
&pts
)
123 // Initialize all slots with first input coordinate
124 pts
= Coordinate::ConstVect(8, inputPts
[0]);
126 for (size_t i
=1, n
=inputPts
.size(); i
<n
; ++i
)
128 if (inputPts
[i
]->x
< pts
[0]->x
) {
129 pts
[0] = inputPts
[i
];
131 if (inputPts
[i
]->x
- inputPts
[i
]->y
< pts
[1]->x
- pts
[1]->y
) {
132 pts
[1] = inputPts
[i
];
134 if (inputPts
[i
]->y
> pts
[2]->y
) {
135 pts
[2] = inputPts
[i
];
137 if (inputPts
[i
]->x
+ inputPts
[i
]->y
> pts
[3]->x
+ pts
[3]->y
) {
138 pts
[3] = inputPts
[i
];
140 if (inputPts
[i
]->x
> pts
[4]->x
) {
141 pts
[4] = inputPts
[i
];
143 if (inputPts
[i
]->x
- inputPts
[i
]->y
> pts
[5]->x
- pts
[5]->y
) {
144 pts
[5] = inputPts
[i
];
146 if (inputPts
[i
]->y
< pts
[6]->y
) {
147 pts
[6] = inputPts
[i
];
149 if (inputPts
[i
]->x
+ inputPts
[i
]->y
< pts
[7]->x
+ pts
[7]->y
) {
150 pts
[7] = inputPts
[i
];
159 ConvexHull::computeOctRing(const Coordinate::ConstVect
&inputPts
,
160 Coordinate::ConstVect
&dest
)
162 computeOctPts(inputPts
, dest
);
164 // Remove consecutive equal Coordinates
165 // unique() returns an iterator to the end of the resulting
166 // sequence, we erase from there to the end.
167 dest
.erase( std::unique(dest
.begin(),dest
.end()), dest
.end() );
169 // points must all lie in a line
170 if ( dest
.size() < 3 ) return false;
173 dest
.push_back(dest
[0]);
180 ConvexHull::reduce(Coordinate::ConstVect
&pts
)
182 Coordinate::ConstVect polyPts
;
184 if ( ! computeOctRing(pts
, polyPts
) ) {
185 // unable to compute interior polygon for some reason
189 // add points defining polygon
190 Coordinate::ConstSet reducedSet
;
191 reducedSet
.insert(polyPts
.begin(), polyPts
.end());
194 * Add all unique points not in the interior poly.
195 * CGAlgorithms.isPointInRing is not defined for points
196 * actually on the ring, but this doesn't matter since
197 * the points of the interior polygon are forced to be
198 * in the reduced set.
200 * @@TIP: there should be a std::algo for this
202 for (size_t i
=0, n
=pts
.size(); i
<n
; ++i
)
204 if ( !CGAlgorithms::isPointInRing(*(pts
[i
]), polyPts
) )
206 reducedSet
.insert(pts
[i
]);
210 inputPts
.assign(reducedSet
.begin(), reducedSet
.end());
212 if ( inputPts
.size() < 3 ) padArray3(inputPts
);
217 ConvexHull::padArray3(geom::Coordinate::ConstVect
&pts
)
219 for (size_t i
= pts
.size(); i
< 3; ++i
) {
220 pts
.push_back(pts
[0]);
225 ConvexHull::getConvexHull()
227 size_t nInputPts
=inputPts
.size();
229 if (nInputPts
==0) // Return an empty geometry
230 return geomFactory
->createEmptyGeometry();
232 if (nInputPts
==1) // Return a Point
234 // Copy the Coordinate from the ConstVect
235 return geomFactory
->createPoint(*(inputPts
[0]));
238 if (nInputPts
==2) // Return a LineString
240 // Copy all Coordinates from the ConstVect
241 CoordinateSequence
*cs
= toCoordinateSequence(inputPts
);
242 return geomFactory
->createLineString(cs
);
245 // use heuristic to reduce points, if large
251 // sort points for Graham scan.
254 // Use Graham scan to find convex hull.
255 Coordinate::ConstVect cHS
;
256 grahamScan(inputPts
, cHS
);
258 return lineOrPolygon(cHS
);
264 ConvexHull::preSort(Coordinate::ConstVect
&pts
)
266 // find the lowest point in the set. If two or more points have
267 // the same minimum y coordinate choose the one with the minimum x.
268 // This focal point is put in array location pts[0].
269 for(size_t i
=1, n
=pts
.size(); i
<n
; ++i
)
271 const Coordinate
*p0
=pts
[0]; // this will change
272 const Coordinate
*pi
=pts
[i
];
273 if ( (pi
->y
<p0
->y
) || ((pi
->y
==p0
->y
) && (pi
->x
<p0
->x
)) )
275 const Coordinate
*t
=p0
;
281 // sort the points radially around the focal point.
282 std::sort(pts
.begin(), pts
.end(), RadiallyLessThen(pts
[0]));
287 ConvexHull::grahamScan(const Coordinate::ConstVect
&c
,
288 Coordinate::ConstVect
&ps
)
294 for(size_t i
=3, n
=c
.size(); i
<n
; ++i
)
296 const Coordinate
*p
= ps
.back(); ps
.pop_back();
297 while (!ps
.empty() &&
298 CGAlgorithms::computeOrientation(
299 *(ps
.back()), *p
, *(c
[i
])) > 0)
301 p
= ps
.back(); ps
.pop_back();
311 // * Returns a pointer to input, modifying input
313 //CoordinateSequence*
314 //ConvexHull::preSort(CoordinateSequence *pts)
318 // // find the lowest point in the set. If two or more points have
319 // // the same minimum y coordinate choose the one with the minimu x.
320 // // This focal point is put in array location pts[0].
321 // size_t npts=pts->getSize();
322 // for(size_t i=1; i<npts; ++i)
324 // const Coordinate &p0=pts->getAt(0); // this will change
325 // const Coordinate &pi=pts->getAt(i);
326 // if ( (pi.y<p0.y) || ((pi.y==p0.y) && (pi.x<p0.x)) )
329 // pts->setAt(pi, 0);
330 // pts->setAt( t, i);
333 // // sort the points radially around the focal point.
339 // * Returns a newly allocated CoordinateSequence object
341 //CoordinateSequence*
342 //ConvexHull::grahamScan(const CoordinateSequence *c)
344 // const Coordinate *p;
346 // vector<Coordinate> *ps=new vector<Coordinate>();
347 // ps->push_back(c->getAt(0));
348 // ps->push_back(c->getAt(1));
349 // ps->push_back(c->getAt(2));
352 // size_t npts=c->getSize();
353 // for(size_t i=3; i<npts; ++i)
357 // while (CGAlgorithms::computeOrientation(ps->back(), *p, c->getAt(i)) > 0)
362 // ps->push_back(*p);
363 // ps->push_back(c->getAt(i));
365 // ps->push_back(c->getAt(0));
367 // return geomFactory->getCoordinateSequenceFactory()->create(ps);
371 //ConvexHull::radialSort(CoordinateSequence *p)
373 // // A selection sort routine, assumes the pivot point is
374 // // the first point (i.e., p[0]).
376 // const Coordinate &p0=p->getAt(0); // the pivot point
379 // size_t npts=p->getSize();
380 // for(size_t i=1; i<npts-1; ++i)
384 // for(size_t j=i+1; j<npts; ++j)
386 // const Coordinate &pj=p->getAt(j);
388 // if ( polarCompare(p0, pj, p->getAt(min)) < 0 )
395 // * Swap point[i] and point[min]
396 // * We can skip this if they have
402 // p->setAt(p->getAt(min), i);
412 ConvexHull::isBetween(const Coordinate
&c1
, const Coordinate
&c2
, const Coordinate
&c3
)
414 if (CGAlgorithms::computeOrientation(c1
, c2
, c3
)!=0) {
418 if (c1
.x
<=c2
.x
&& c2
.x
<=c3
.x
) {
421 if (c3
.x
<=c2
.x
&& c2
.x
<=c1
.x
) {
426 if (c1
.y
<=c2
.y
&& c2
.y
<=c3
.y
) {
429 if (c3
.y
<=c2
.y
&& c2
.y
<=c1
.y
) {
437 //ConvexHull::makeBigQuad(const CoordinateSequence *pts, BigQuad &bigQuad)
439 // bigQuad.northmost=bigQuad.southmost=
440 // bigQuad.westmost=bigQuad.eastmost=pts->getAt(0);
442 // size_t npts=pts->getSize();
443 // for (size_t i=1; i<npts; ++i)
445 // const Coordinate &pi=pts->getAt(i);
447 // if (pi.x<bigQuad.westmost.x)
448 // bigQuad.westmost=pi;
450 // if (pi.x>bigQuad.eastmost.x)
451 // bigQuad.eastmost=pi;
453 // if (pi.y<bigQuad.southmost.y)
454 // bigQuad.southmost=pi;
456 // if (pi.y>bigQuad.northmost.y)
457 // bigQuad.northmost=pi;
463 ConvexHull::lineOrPolygon(const Coordinate::ConstVect
&input
)
465 Coordinate::ConstVect cleaned
;
467 cleanRing(input
, cleaned
);
469 if (cleaned
.size()==3) { // shouldn't this be 2 ??
471 CoordinateSequence
*cl1
=toCoordinateSequence(cleaned
);
472 LineString
*ret
= geomFactory
->createLineString(cl1
);
475 CoordinateSequence
*cl2
=toCoordinateSequence(cleaned
);
476 LinearRing
*linearRing
=geomFactory
->createLinearRing(cl2
);
477 return geomFactory
->createPolygon(linearRing
,NULL
);
482 ConvexHull::cleanRing(const Coordinate::ConstVect
&original
,
483 Coordinate::ConstVect
&cleaned
)
485 size_t npts
=original
.size();
487 const Coordinate
*last
= original
[npts
-1];
489 //util::Assert::equals(*(original[0]), *last);
491 assert(original
[0]->equals2D(*last
));
493 const Coordinate
*prev
= NULL
;
494 for (size_t i
=0; i
<npts
-1; ++i
)
496 const Coordinate
*curr
= original
[i
];
497 const Coordinate
*next
= original
[i
+1];
499 // skip consecutive equal coordinates
500 if (curr
->equals2D(*next
)) continue;
502 if ( prev
!= NULL
&& isBetween(*prev
, *curr
, *next
) )
507 cleaned
.push_back(curr
);
511 cleaned
.push_back(last
);
517 // * @param vertices the vertices of a linear ring, which may or may not be
518 // * flattened (i.e. vertices collinear)
519 // * @return a newly allocated CoordinateSequence
521 //CoordinateSequence*
522 //ConvexHull::cleanRing(const CoordinateSequence *original)
524 // Assert::equals(original->getAt(0),original->getAt(original->getSize()-1));
526 // size_t npts=original->getSize();
528 // vector<Coordinate> *newPts=new vector<Coordinate>;
529 // newPts->reserve(npts);
531 // const Coordinate *previousDistinctCoordinate=NULL;
532 // for(size_t i=0; i<npts-1; ++i)
534 // const Coordinate ¤tCoordinate=original->getAt(i);
535 // const Coordinate &nextCoordinate=original->getAt(i+1);
537 // // skip repeated points (shouldn't this have been already done elsewhere?)
538 // if (currentCoordinate==nextCoordinate) continue;
540 // // skip collinear point
541 // if (previousDistinctCoordinate!=NULL &&
542 // isBetween(*previousDistinctCoordinate, currentCoordinate, nextCoordinate))
547 // newPts->push_back(currentCoordinate);
549 // previousDistinctCoordinate=¤tCoordinate;
552 // newPts->push_back(original->getAt(npts-1));
554 // CoordinateSequence *cleanedRing=geomFactory->getCoordinateSequenceFactory()->create(newPts);
555 // return cleanedRing;
558 } // namespace geos.algorithm
561 /**********************************************************************
563 * Revision 1.22 2006/06/12 10:49:43 strk
564 * unsigned int => size_t
566 * Revision 1.21 2006/03/24 09:52:41 strk
567 * USE_INLINE => GEOS_INLINE
569 * Revision 1.20 2006/03/21 11:12:23 strk
570 * Cleanups: headers inclusion and Log section
572 * Revision 1.19 2006/03/09 16:46:45 strk
573 * geos::geom namespace definition, first pass at headers split
574 **********************************************************************/