1 /* -*-c++-*- */
2 /* osgEarth - Geospatial SDK for OpenSceneGraph
3 * Copyright 2019 Pelican Mapping
4 * http://osgearth.org
5 *
6 * osgEarth is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
16 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
17 * IN THE SOFTWARE.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>
21 */
22 #include <osgEarthSymbology/Geometry>
23 #include <osgEarthSymbology/GEOS>
24 #include <algorithm>
25 #include <iterator>
26
27 using namespace osgEarth;
28 using namespace osgEarth::Symbology;
29
30 #ifdef OSGEARTH_HAVE_GEOS
31 # include <geos/geom/Geometry.h>
32 # include <geos/geom/GeometryFactory.h>
33 # include <geos/operation/buffer/BufferOp.h>
34 # include <geos/operation/buffer/BufferBuilder.h>
35 # include <geos/operation/overlay/OverlayOp.h>
36 using namespace geos;
37 using namespace geos::operation;
38 #endif
39
40 #define GEOS_OUT OE_DEBUG
41
42 #define LC "[Geometry] "
43
44
Geometry(const Geometry & rhs)45 Geometry::Geometry( const Geometry& rhs ) :
46 osgEarth::MixinVector<osg::Vec3d,osg::Referenced>( rhs )
47 {
48 //nop
49 }
50
Geometry(int capacity)51 Geometry::Geometry( int capacity )
52 {
53 if ( capacity > 0 )
54 reserve( capacity );
55 }
56
Geometry(const Vec3dVector * data)57 Geometry::Geometry( const Vec3dVector* data )
58 {
59 reserve( data->size() );
60 insert( begin(), data->begin(), data->end() );
61 }
62
~Geometry()63 Geometry::~Geometry()
64 {
65 }
66
67 int
getTotalPointCount() const68 Geometry::getTotalPointCount() const
69 {
70 return size();
71 }
72
73 Bounds
getBounds() const74 Geometry::getBounds() const
75 {
76 Bounds bounds;
77 for( const_iterator i = begin(); i != end(); ++i )
78 bounds.expandBy( i->x(), i->y(), i->z() );
79 return bounds;
80 }
81
82 Geometry*
cloneAs(const Geometry::Type & newType) const83 Geometry::cloneAs( const Geometry::Type& newType ) const
84 {
85 //if ( newType == getType() )
86 // return static_cast<Geometry*>( clone() );
87
88 switch( newType )
89 {
90 case TYPE_POINTSET:
91 return new PointSet( &this->asVector() );
92 case TYPE_LINESTRING:
93 return new LineString( &this->asVector() );
94 case TYPE_RING:
95 return new Ring( &this->asVector() );
96 case TYPE_POLYGON:
97 if ( dynamic_cast<const Polygon*>(this) )
98 return new Polygon( *static_cast<const Polygon*>(this) );
99 else
100 return new Polygon( &this->asVector() );
101 case TYPE_UNKNOWN:
102 return new Geometry( &this->asVector() );
103 default:
104 break;
105 }
106 return 0L;
107 }
108
109 osg::Vec3Array*
createVec3Array() const110 Geometry::createVec3Array() const
111 {
112 osg::Vec3Array* result = new osg::Vec3Array( this->size() );
113 std::copy( begin(), end(), result->begin() );
114 return result;
115 }
116
117 osg::Vec3dArray*
createVec3dArray() const118 Geometry::createVec3dArray() const
119 {
120 osg::Vec3dArray* result = new osg::Vec3dArray( this->size() );
121 std::copy( begin(), end(), result->begin() );
122 return result;
123 }
124
125 Geometry*
create(Type type,const Vec3dVector * toCopy)126 Geometry::create( Type type, const Vec3dVector* toCopy )
127 {
128 Geometry* output = 0L;
129 switch( type ) {
130 case TYPE_POINTSET:
131 output = new PointSet( toCopy ); break;
132 case TYPE_LINESTRING:
133 output = new LineString( toCopy ); break;
134 case TYPE_RING:
135 output = new Ring( toCopy ); break;
136 case TYPE_POLYGON:
137 output = new Polygon( toCopy ); break;
138 default:
139 break;
140 }
141 return output;
142 }
143
144 bool
hasBufferOperation()145 Geometry::hasBufferOperation()
146 {
147 #ifdef OSGEARTH_HAVE_GEOS
148 return true;
149 #else
150 return false;
151 #endif
152 }
153
154 bool
buffer(double distance,osg::ref_ptr<Geometry> & output,const BufferParameters & params) const155 Geometry::buffer(double distance,
156 osg::ref_ptr<Geometry>& output,
157 const BufferParameters& params ) const
158 {
159 #ifdef OSGEARTH_HAVE_GEOS
160
161 GEOSContext gc;
162
163 geom::Geometry* inGeom = gc.importGeometry( this );
164 if ( inGeom )
165 {
166 buffer::BufferParameters::EndCapStyle geosEndCap =
167 params._capStyle == BufferParameters::CAP_ROUND ? buffer::BufferParameters::CAP_ROUND :
168 params._capStyle == BufferParameters::CAP_SQUARE ? buffer::BufferParameters::CAP_SQUARE :
169 params._capStyle == BufferParameters::CAP_FLAT ? buffer::BufferParameters::CAP_FLAT :
170 buffer::BufferParameters::CAP_SQUARE;
171
172 buffer::BufferParameters::JoinStyle geosJoinStyle =
173 params._joinStyle == BufferParameters::JOIN_ROUND ? buffer::BufferParameters::JOIN_ROUND :
174 params._joinStyle == BufferParameters::JOIN_MITRE ? buffer::BufferParameters::JOIN_MITRE :
175 params._joinStyle == BufferParameters::JOIN_BEVEL ? buffer::BufferParameters::JOIN_BEVEL :
176 buffer::BufferParameters::JOIN_ROUND;
177
178 //JB: Referencing buffer::BufferParameters::DEFAULT_QUADRANT_SEGMENTS causes link errors b/c it is defined as a static in the header of BufferParameters.h and not defined in the cpp anywhere.
179 // This seems to only effect the Linux build, Windows works fine
180 int geosQuadSegs = params._cornerSegs > 0
181 ? params._cornerSegs
182 : 8; //buffer::BufferParameters::DEFAULT_QUADRANT_SEGMENTS;
183
184 geom::Geometry* outGeom = NULL;
185
186 buffer::BufferParameters geosBufferParams;
187 geosBufferParams.setQuadrantSegments( geosQuadSegs );
188 geosBufferParams.setEndCapStyle( geosEndCap );
189 geosBufferParams.setJoinStyle( geosJoinStyle );
190 buffer::BufferBuilder bufBuilder( geosBufferParams );
191
192 try
193 {
194 if (params._singleSided)
195 {
196 outGeom = bufBuilder.bufferLineSingleSided(inGeom, distance, params._leftSide);
197 }
198 else
199 {
200 outGeom = bufBuilder.buffer(inGeom, distance);
201 }
202 }
203 catch(const geos::util::GEOSException& ex)
204 {
205 OE_NOTICE << LC << "buffer(GEOS): "
206 << (ex.what()? ex.what() : " no error message")
207 << std::endl;
208 outGeom = 0L;
209 }
210
211 bool sharedFactory =
212 inGeom && outGeom &&
213 inGeom->getFactory() == outGeom->getFactory();
214
215 if ( outGeom )
216 {
217 output = gc.exportGeometry( outGeom );
218 gc.disposeGeometry( outGeom );
219 }
220
221 gc.disposeGeometry( inGeom );
222 }
223
224 return output.valid();
225
226 #else // OSGEARTH_HAVE_GEOS
227
228 OE_WARN << LC << "Buffer failed - GEOS not available" << std::endl;
229 return false;
230
231 #endif // OSGEARTH_HAVE_GEOS
232 }
233
234 bool
crop(const Polygon * cropPoly,osg::ref_ptr<Geometry> & output) const235 Geometry::crop( const Polygon* cropPoly, osg::ref_ptr<Geometry>& output ) const
236 {
237 #ifdef OSGEARTH_HAVE_GEOS
238 bool success = false;
239 output = 0L;
240
241 GEOSContext gc;
242
243 //Create the GEOS Geometries
244 geom::Geometry* inGeom = gc.importGeometry( this );
245 geom::Geometry* cropGeom = gc.importGeometry( cropPoly );
246
247 if ( inGeom )
248 {
249 geom::Geometry* outGeom = 0L;
250 try {
251 outGeom = overlay::OverlayOp::overlayOp(
252 inGeom,
253 cropGeom,
254 overlay::OverlayOp::opINTERSECTION );
255 }
256 catch (const geos::util::TopologyException& ex) {
257 GEOS_OUT << LC << "Crop(GEOS): "
258 << (ex.what()? ex.what() : " no error message")
259 << std::endl;
260 outGeom = 0L;
261 }
262 catch(const geos::util::GEOSException& ex) {
263 OE_INFO << LC << "Crop(GEOS): "
264 << (ex.what()? ex.what() : " no error message")
265 << std::endl;
266 outGeom = 0L;
267 }
268
269 if ( outGeom )
270 {
271 output = gc.exportGeometry( outGeom );
272
273 if ( output.valid())
274 {
275 if ( output->isValid() )
276 {
277 success = true;
278 }
279 else
280 {
281 // GEOS result is invalid
282 output = 0L;
283 }
284 }
285 else
286 {
287 // set output to empty geometry to indicate the (valid) empty case,
288 // still returning false but allows for check.
289 if (outGeom->getNumPoints() == 0)
290 {
291 output = new osgEarth::Symbology::Geometry();
292 }
293 }
294
295 gc.disposeGeometry( outGeom );
296 }
297 }
298
299 //Destroy the geometry
300 gc.disposeGeometry( cropGeom );
301 gc.disposeGeometry( inGeom );
302
303 return success;
304
305 #else // OSGEARTH_HAVE_GEOS
306
307 OE_WARN << LC << "Crop failed - GEOS not available" << std::endl;
308 return false;
309
310 #endif // OSGEARTH_HAVE_GEOS
311 }
312
313 bool
crop(const Bounds & bounds,osg::ref_ptr<Geometry> & output) const314 Geometry::crop( const Bounds& bounds, osg::ref_ptr<Geometry>& output ) const
315 {
316 osg::ref_ptr<Polygon> poly = new Polygon;
317 poly->resize( 4 );
318 (*poly)[0].set(bounds.xMin(), bounds.yMin(), 0);
319 (*poly)[1].set(bounds.xMax(), bounds.yMin(), 0);
320 (*poly)[2].set(bounds.xMax(), bounds.yMax(), 0);
321 (*poly)[3].set(bounds.xMin(), bounds.yMax(), 0);
322 return crop(poly.get(), output);
323 }
324
325 bool
geounion(const Geometry * other,osg::ref_ptr<Geometry> & output) const326 Geometry::geounion( const Geometry* other, osg::ref_ptr<Geometry>& output ) const
327 {
328 #ifdef OSGEARTH_HAVE_GEOS
329 bool success = false;
330 output = 0L;
331
332 GEOSContext gc;
333
334 //Create the GEOS Geometries
335 geom::Geometry* inGeom = gc.importGeometry( this );
336 geom::Geometry* otherGeom = gc.importGeometry( other );
337
338 if ( inGeom )
339 {
340 geom::Geometry* outGeom = 0L;
341 try {
342 outGeom = overlay::OverlayOp::overlayOp(
343 inGeom,
344 otherGeom,
345 overlay::OverlayOp::opUNION );
346 }
347 catch (const geos::util::TopologyException& ex) {
348 GEOS_OUT << LC << "Crop(GEOS): "
349 << (ex.what()? ex.what() : " no error message")
350 << std::endl;
351 outGeom = 0L;
352 }
353 catch(const geos::util::GEOSException& ex) {
354 OE_INFO << LC << "Union(GEOS): "
355 << (ex.what()? ex.what() : " no error message")
356 << std::endl;
357 outGeom = 0L;
358 }
359
360 if ( outGeom )
361 {
362 output = gc.exportGeometry( outGeom );
363
364 if ( output.valid())
365 {
366 if ( output->isValid() )
367 {
368 success = true;
369 }
370 else
371 {
372 // GEOS result is invalid
373 output = 0L;
374 }
375 }
376 else
377 {
378 // set output to empty geometry to indicate the (valid) empty case,
379 // still returning false but allows for check.
380 if (outGeom->getNumPoints() == 0)
381 {
382 output = new osgEarth::Symbology::Geometry();
383 }
384 }
385
386 gc.disposeGeometry( outGeom );
387 }
388 }
389
390 //Destroy the geometry
391 gc.disposeGeometry( otherGeom );
392 gc.disposeGeometry( inGeom );
393
394 return success;
395
396 #else // OSGEARTH_HAVE_GEOS
397
398 OE_WARN << LC << "Union failed - GEOS not available" << std::endl;
399 return false;
400
401 #endif // OSGEARTH_HAVE_GEOS
402 }
403
404 bool
difference(const Polygon * diffPolygon,osg::ref_ptr<Geometry> & output) const405 Geometry::difference( const Polygon* diffPolygon, osg::ref_ptr<Geometry>& output ) const
406 {
407 #ifdef OSGEARTH_HAVE_GEOS
408
409 GEOSContext gc;
410
411 //Create the GEOS Geometries
412 geom::Geometry* inGeom = gc.importGeometry( this );
413 geom::Geometry* diffGeom = gc.importGeometry( diffPolygon );
414
415 if ( inGeom )
416 {
417 geom::Geometry* outGeom = 0L;
418 try {
419 outGeom = overlay::OverlayOp::overlayOp(
420 inGeom,
421 diffGeom,
422 overlay::OverlayOp::opDIFFERENCE );
423 }
424 catch (const geos::util::TopologyException& ex) {
425 GEOS_OUT << LC << "Crop(GEOS): "
426 << (ex.what()? ex.what() : " no error message")
427 << std::endl;
428 outGeom = 0L;
429 }
430 catch(const geos::util::GEOSException& ex) {
431 OE_INFO << LC << "Diff(GEOS): "
432 << (ex.what()? ex.what() : " no error message")
433 << std::endl;
434 outGeom = 0L;
435 }
436
437 if ( outGeom )
438 {
439 output = gc.exportGeometry( outGeom );
440 gc.disposeGeometry( outGeom );
441
442 if ( output.valid() && !output->isValid() )
443 {
444 output = 0L;
445 }
446 }
447 }
448
449 //Destroy the geometry
450 gc.disposeGeometry( diffGeom );
451 gc.disposeGeometry( inGeom );
452
453 return output.valid();
454
455 #else // OSGEARTH_HAVE_GEOS
456
457 OE_WARN << LC << "Difference failed - GEOS not available" << std::endl;
458 return false;
459
460 #endif // OSGEARTH_HAVE_GEOS
461 }
462
463 bool
intersects(const class Geometry * other) const464 Geometry::intersects(
465 const class Geometry* other
466 ) const
467 {
468 #ifdef OSGEARTH_HAVE_GEOS
469
470 GEOSContext gc;
471
472 //Create the GEOS Geometries
473 geom::Geometry* inGeom = gc.importGeometry( this );
474 geom::Geometry* otherGeom = gc.importGeometry( other );
475
476 bool intersects = inGeom->intersects( otherGeom );
477
478 //Destroy the geometry
479 gc.disposeGeometry( otherGeom );
480 gc.disposeGeometry( inGeom );
481
482 return intersects;
483
484 #else // OSGEARTH_HAVE_GEOS
485
486 OE_WARN << LC << "Intersects failed - GEOS not available" << std::endl;
487 return false;
488
489 #endif // OSGEARTH_HAVE_GEOS
490 }
491
492 osg::Vec3d
localize()493 Geometry::localize()
494 {
495 osg::Vec3d offset;
496
497 Bounds bounds = getBounds();
498 if ( bounds.isValid() )
499 {
500 osg::Vec2d center = bounds.center2d();
501 offset.set( center.x(), center.y(), 0 );
502
503 GeometryIterator i( this );
504 while( i.hasMore() )
505 {
506 Geometry* part = i.next();
507 for( Geometry::iterator j = part->begin(); j != part->end(); ++j )
508 {
509 *j = *j - offset;
510 }
511 }
512 }
513
514 return offset;
515 }
516
517 void
delocalize(const osg::Vec3d & offset)518 Geometry::delocalize( const osg::Vec3d& offset )
519 {
520 GeometryIterator i( this );
521 while( i.hasMore() )
522 {
523 Geometry* part = i.next();
524 for( Geometry::iterator j = part->begin(); j != part->end(); ++j )
525 {
526 *j = *j + offset;
527 }
528 }
529 }
530
531 void
rewind(Orientation orientation)532 Geometry::rewind( Orientation orientation )
533 {
534 Orientation current = getOrientation();
535 if ( current != orientation && current != ORIENTATION_DEGENERATE && orientation != ORIENTATION_DEGENERATE )
536 {
537 std::reverse( begin(), end() );
538 }
539 }
540
removeDuplicates()541 void Geometry::removeDuplicates()
542 {
543 if (size() > 1)
544 {
545 osg::Vec3d v = front();
546 for (Geometry::iterator itr = begin(); itr != end(); )
547 {
548 if (itr != begin() && v == *itr)
549 {
550 itr = erase(itr);
551 }
552 else
553 {
554 v = *itr;
555 itr++;
556 }
557 }
558 }
559 }
560
561 void
removeColinearPoints()562 Geometry::removeColinearPoints()
563 {
564 if ( size() >= 3 )
565 {
566 std::vector<unsigned> ind;
567
568 for(unsigned i=0; i<size()-2; ++i)
569 {
570 osg::Vec3d v0( at(i+1) - at(i) );
571 v0.normalize();
572 osg::Vec3d v1( at(i+2) - at(i) );
573 v1.normalize();
574 if ( osg::equivalent(v0*v1, 1.0) )
575 ind.push_back(i+1);
576 }
577
578 for(std::vector<unsigned>::reverse_iterator r = ind.rbegin(); r != ind.rend(); ++r)
579 {
580 erase( begin() + (*r) );
581 }
582 }
583 }
584
585 Geometry::Orientation
getOrientation() const586 Geometry::getOrientation() const
587 {
588 // adjust for a non-open ring:
589 int n = size();
590 if ( n > 0 && front() == back() )
591 n--;
592
593 if ( n < 3 )
594 return Geometry::ORIENTATION_DEGENERATE;
595
596 // copy the open vec:
597 std::vector<osg::Vec3d> v;
598 v.reserve( n );
599 std::copy( begin(), begin()+n, std::back_inserter(v) );
600
601 int rmin = 0;
602 double xmin = v[0].x();
603 double ymin = v[0].y();
604 v[0].z() = 0;
605 for( int i=1; i<n; ++i ) {
606 double x = v[i].x();
607 double y = v[i].y();
608 v[i].z() = 0;
609 if ( y > ymin )
610 continue;
611 if ( y == ymin ) {
612 if (x < xmin )
613 continue;
614 }
615 rmin = i;
616 xmin = x;
617 ymin = y;
618 }
619
620 int rmin_less_1 = rmin-1 >= 0 ? rmin-1 : n-1;
621 int rmin_plus_1 = rmin+1 < n ? rmin+1 : 0;
622
623 osg::Vec3 in = v[rmin] - v[rmin_less_1]; in.normalize();
624 osg::Vec3 out = v[rmin_plus_1] - v[rmin]; out.normalize();
625 osg::Vec3 cross = in ^ out;
626
627 return
628 cross.z() < 0.0 ? Geometry::ORIENTATION_CW :
629 cross.z() > 0.0 ? Geometry::ORIENTATION_CCW :
630 Geometry::ORIENTATION_DEGENERATE;
631 }
632
633 double
getLength() const634 Geometry::getLength() const
635 {
636 if (empty())
637 return 0.0;
638
639 double length = 0;
640 for (unsigned int i = 0; i < size()-1; ++i)
641 {
642 osg::Vec3d current = (*this)[i];
643 osg::Vec3d next = (*this)[i+1];
644 length += (next - current).length();
645 }
646 return length;
647 }
648
649 // ensures that the first and last points are idential.
650 void
close()651 Geometry::close()
652 {
653 if ( size() > 0 && front() != back() )
654 push_back( front() );
655 }
656
657 //----------------------------------------------------------------------------
658
PointSet(const PointSet & rhs)659 PointSet::PointSet( const PointSet& rhs ) :
660 Geometry( rhs )
661 {
662 //nop
663 }
664
~PointSet()665 PointSet::~PointSet()
666 {
667 }
668
669 void
close()670 PointSet::close()
671 {
672 //NOP. Don't close point sets..
673 }
674
675 //----------------------------------------------------------------------------
676
LineString(const LineString & rhs)677 LineString::LineString( const LineString& rhs ) :
678 Geometry( rhs )
679 {
680 //nop
681 }
682
LineString(const Vec3dVector * data)683 LineString::LineString( const Vec3dVector* data ) :
684 Geometry( data )
685 {
686 //nop
687 }
688
~LineString()689 LineString::~LineString()
690 {
691 }
692
693 bool
getSegment(double length,osg::Vec3d & start,osg::Vec3d & end)694 LineString::getSegment(double length, osg::Vec3d& start, osg::Vec3d& end)
695 {
696 double pos = 0;
697 for (unsigned int i = 0; i < size()-1; ++i)
698 {
699 osg::Vec3d current = (*this)[i];
700 osg::Vec3d next = (*this)[i+1];
701 pos += (next - current).length();
702 if (pos > length)
703 {
704 start = current;
705 end = next;
706 return true;
707 }
708 }
709 return false;
710 }
711
712 void
close()713 LineString::close()
714 {
715 //NOP - dont' close line strings.
716 }
717
718 //----------------------------------------------------------------------------
719
Ring(const Ring & rhs)720 Ring::Ring( const Ring& rhs ) :
721 Geometry( rhs )
722 {
723 //nop
724 }
725
Ring(const Vec3dVector * data)726 Ring::Ring( const Vec3dVector* data ) :
727 Geometry( data )
728 {
729 open();
730 }
731
~Ring()732 Ring::~Ring()
733 {
734 }
735
736 Geometry*
cloneAs(const Geometry::Type & newType) const737 Ring::cloneAs( const Geometry::Type& newType ) const
738 {
739 if ( newType == TYPE_LINESTRING )
740 {
741 LineString* line = new LineString( &this->asVector() );
742 if ( line->size() > 1 && line->front() != line->back() )
743 line->push_back( front() );
744 return line;
745 }
746 else return Geometry::cloneAs( newType );
747 }
748
749 double
getLength() const750 Ring::getLength() const
751 {
752 if (empty())
753 return 0.0;
754
755 double length = Geometry::getLength();
756 if ( isOpen() )
757 {
758 length += (front()-back()).length();
759 }
760 return length;
761 }
762
763 // ensures that the first and last points are not idential.
764 void
open()765 Ring::open()
766 {
767 while( size() > 2 && front() == back() )
768 erase( end()-1 );
769 }
770
771 void
close()772 Ring::close()
773 {
774 Geometry::close();
775 }
776
777 // whether the ring is open.
778 bool
isOpen() const779 Ring::isOpen() const
780 {
781 return size() > 1 && front() != back();
782 }
783
784 // gets the signed area.
785 double
getSignedArea2D() const786 Ring::getSignedArea2D() const
787 {
788 const_cast<Ring*>(this)->open();
789
790 double sum = 0.0;
791
792 for( unsigned i=0; i<size(); ++i )
793 {
794 const osg::Vec3d& p0 = (*this)[0];
795 const osg::Vec3d& p1 = i+1 < size() ? (*this)[i+1] : (*this)[0];
796 sum += p0.x()*p1.y() - p1.x()*p0.y();
797 }
798 return .5*sum;
799 }
800
801 // opens and rewinds the polygon to the specified orientation.
802 void
rewind(Orientation orientation)803 Ring::rewind( Orientation orientation )
804 {
805 open();
806 Geometry::rewind( orientation );
807 }
808
809 // point-in-polygon test
810 bool
contains2D(double x,double y) const811 Ring::contains2D( double x, double y ) const
812 {
813 bool result = false;
814 const Ring& poly = *this;
815 for( unsigned i=0, j=size()-1; i<size(); j = i++ )
816 {
817 if ((((poly[i].y() <= y) && (y < poly[j].y())) ||
818 ((poly[j].y() <= y) && (y < poly[i].y()))) &&
819 (x < (poly[j].x()-poly[i].x()) * (y-poly[i].y())/(poly[j].y()-poly[i].y())+poly[i].x()))
820 {
821 result = !result;
822 }
823 }
824 return result;
825 }
826
827 //----------------------------------------------------------------------------
828
Polygon(const Polygon & rhs)829 Polygon::Polygon( const Polygon& rhs ) :
830 Ring( rhs )
831 {
832 for( RingCollection::const_iterator r = rhs._holes.begin(); r != rhs._holes.end(); ++r )
833 _holes.push_back( new Ring(*r->get()) );
834 }
835
Polygon(const Vec3dVector * data)836 Polygon::Polygon( const Vec3dVector* data ) :
837 Ring( data )
838 {
839 //nop
840 }
841
~Polygon()842 Polygon::~Polygon()
843 {
844 }
845
846 int
getTotalPointCount() const847 Polygon::getTotalPointCount() const
848 {
849 int total = Ring::getTotalPointCount();
850 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
851 total += i->get()->getTotalPointCount();
852 return total;
853 }
854
855 bool
contains2D(double x,double y) const856 Polygon::contains2D( double x, double y ) const
857 {
858 // first check the outer ring
859 if ( !Ring::contains2D(x, y) )
860 return false;
861
862 // then check each inner ring (holes). Point has to be inside the outer ring,
863 // but NOT inside any of the holes
864 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
865 {
866 if ( i->get()->contains2D(x, y) )
867 return false;
868 }
869
870 return true;
871 }
872
873 void
open()874 Polygon::open()
875 {
876 Ring::open();
877 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
878 (*i)->open();
879 }
880
881 void
close()882 Polygon::close()
883 {
884 Ring::close();
885 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
886 (*i)->close();
887 }
888
889 void
removeDuplicates()890 Polygon::removeDuplicates()
891 {
892 Ring::removeDuplicates();
893 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
894 (*i)->removeDuplicates();
895 }
896
897 void
removeColinearPoints()898 Polygon::removeColinearPoints()
899 {
900 Ring::removeColinearPoints();
901 for( RingCollection::const_iterator i = _holes.begin(); i != _holes.end(); ++i )
902 (*i)->removeColinearPoints();
903 }
904
905 //----------------------------------------------------------------------------
906
MultiGeometry(const MultiGeometry & rhs)907 MultiGeometry::MultiGeometry( const MultiGeometry& rhs ) :
908 Geometry( rhs )
909 {
910 for( GeometryCollection::const_iterator i = rhs._parts.begin(); i != rhs._parts.end(); ++i )
911 _parts.push_back( i->get()->clone() ); //i->clone() ); //osg::clone<Geometry>( i->get() ) );
912 }
913
MultiGeometry(const GeometryCollection & parts)914 MultiGeometry::MultiGeometry( const GeometryCollection& parts ) :
915 _parts( parts )
916 {
917 //nop
918 }
919
~MultiGeometry()920 MultiGeometry::~MultiGeometry()
921 {
922 }
923
924 Geometry::Type
getComponentType() const925 MultiGeometry::getComponentType() const
926 {
927 if (_parts.size() == 0)
928 return TYPE_UNKNOWN;
929
930 if (_parts.front()->getType() == TYPE_MULTI)
931 return _parts.front()->getComponentType();
932
933 return _parts.front()->getType();
934 }
935
936 int
getTotalPointCount() const937 MultiGeometry::getTotalPointCount() const
938 {
939 int total = 0;
940 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
941 total += i->get()->getTotalPointCount();
942 return total;
943 }
944
945 double
getLength() const946 MultiGeometry::getLength() const
947 {
948 double total = 0.0;
949 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
950 total += i->get()->getLength();
951 return total;
952 }
953
954 unsigned
getNumGeometries() const955 MultiGeometry::getNumGeometries() const
956 {
957 unsigned total = 0;
958 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
959 total += i->get()->getNumGeometries();
960 return total;
961 }
962
963 Bounds
getBounds() const964 MultiGeometry::getBounds() const
965 {
966 Bounds bounds;
967 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
968 {
969 bounds.expandBy( i->get()->getBounds() );
970 }
971 return bounds;
972 }
973
974 Geometry*
cloneAs(const Geometry::Type & newType) const975 MultiGeometry::cloneAs( const Geometry::Type& newType ) const
976 {
977 MultiGeometry* multi = new MultiGeometry();
978 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
979 {
980 Geometry* part = i->get()->cloneAs( i->get()->getType() );
981 if ( part ) multi->getComponents().push_back( part );
982 }
983 return multi;
984 }
985
986 bool
isValid() const987 MultiGeometry::isValid() const
988 {
989 if ( _parts.size() == 0 )
990 return false;
991
992 bool valid = true;
993 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end() && valid; ++i )
994 {
995 if ( !i->get()->isValid() )
996 valid = false;
997 }
998 return valid;
999 }
1000
1001 void
close()1002 MultiGeometry::close()
1003 {
1004 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
1005 {
1006 i->get()->close();
1007 }
1008 }
1009
1010 // opens and rewinds the polygon to the specified orientation.
1011 void
rewind(Orientation orientation)1012 MultiGeometry::rewind( Orientation orientation )
1013 {
1014 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
1015 {
1016 i->get()->rewind( orientation );
1017 }
1018 }
1019
1020 void
removeColinearPoints()1021 MultiGeometry::removeColinearPoints()
1022 {
1023 for( GeometryCollection::const_iterator i = _parts.begin(); i != _parts.end(); ++i )
1024 {
1025 i->get()->removeColinearPoints();
1026 }
1027 }
1028
1029 //----------------------------------------------------------------------------
1030
GeometryIterator(Geometry * geom,bool holes)1031 GeometryIterator::GeometryIterator( Geometry* geom, bool holes ) :
1032 _next( 0L ),
1033 _traverseMulti( true ),
1034 _traversePolyHoles( holes )
1035 {
1036 if ( geom )
1037 {
1038 _stack.push( geom );
1039 fetchNext();
1040 }
1041 }
1042
1043 bool
hasMore() const1044 GeometryIterator::hasMore() const
1045 {
1046 return _next != 0L;
1047 }
1048
1049 Geometry*
next()1050 GeometryIterator::next()
1051 {
1052 Geometry* n = _next;
1053 fetchNext();
1054 return n;
1055 }
1056
1057 void
fetchNext()1058 GeometryIterator::fetchNext()
1059 {
1060 _next = 0L;
1061 if ( _stack.size() == 0 )
1062 return;
1063
1064 Geometry* current = _stack.top();
1065 _stack.pop();
1066
1067 if ( current->getType() == Geometry::TYPE_MULTI && _traverseMulti )
1068 {
1069 MultiGeometry* m = static_cast<MultiGeometry*>(current);
1070 for( GeometryCollection::const_iterator i = m->getComponents().begin(); i != m->getComponents().end(); ++i )
1071 _stack.push( i->get() );
1072 fetchNext();
1073 }
1074 else if ( current->getType() == Geometry::TYPE_POLYGON && _traversePolyHoles )
1075 {
1076 Polygon* p = static_cast<Polygon*>(current);
1077 for( RingCollection::const_iterator i = p->getHoles().begin(); i != p->getHoles().end(); ++i )
1078 _stack.push( i->get() );
1079 _next = current;
1080 }
1081 else
1082 {
1083 _next = current;
1084 }
1085 }
1086
1087 //----------------------------------------------------------------------------
1088
ConstGeometryIterator(const Geometry * geom,bool holes)1089 ConstGeometryIterator::ConstGeometryIterator( const Geometry* geom, bool holes ) :
1090 _next( 0L ),
1091 _traverseMulti( true ),
1092 _traversePolyHoles( holes )
1093 {
1094 if ( geom )
1095 {
1096 _stack.push( geom );
1097 fetchNext();
1098 }
1099 }
1100
1101 bool
hasMore() const1102 ConstGeometryIterator::hasMore() const
1103 {
1104 return _next != 0L;
1105 }
1106
1107 const Geometry*
next()1108 ConstGeometryIterator::next()
1109 {
1110 const Geometry* n = _next;
1111 fetchNext();
1112 return n;
1113 }
1114
1115 void
fetchNext()1116 ConstGeometryIterator::fetchNext()
1117 {
1118 _next = 0L;
1119 if ( _stack.size() == 0 )
1120 return;
1121
1122 const Geometry* current = _stack.top();
1123 _stack.pop();
1124
1125 if ( current->getType() == Geometry::TYPE_MULTI && _traverseMulti )
1126 {
1127 const MultiGeometry* m = static_cast<const MultiGeometry*>(current);
1128 for( GeometryCollection::const_iterator i = m->getComponents().begin(); i != m->getComponents().end(); ++i )
1129 _stack.push( i->get() );
1130 fetchNext();
1131 }
1132 else if ( current->getType() == Geometry::TYPE_POLYGON && _traversePolyHoles )
1133 {
1134 const Polygon* p = static_cast<const Polygon*>(current);
1135 for( RingCollection::const_iterator i = p->getHoles().begin(); i != p->getHoles().end(); ++i )
1136 _stack.push( i->get() );
1137 _next = current;
1138 }
1139 else
1140 {
1141 _next = current;
1142 }
1143 }
1144
1145 //----------------------------------------------------------------------------
1146
ConstSegmentIterator(const Geometry * verts,bool forceClosedLoop)1147 ConstSegmentIterator::ConstSegmentIterator( const Geometry* verts, bool forceClosedLoop ) :
1148 _verts(&verts->asVector()),
1149 _closeLoop(forceClosedLoop),
1150 _iter(verts->begin())
1151 {
1152 _done = _verts->size() < 2;
1153
1154 if ( !forceClosedLoop )
1155 {
1156 _closeLoop = dynamic_cast<const Ring*>(verts) != 0L;
1157 }
1158 }
1159
1160 Segment
next()1161 ConstSegmentIterator::next()
1162 {
1163 osg::Vec3d p0 = *_iter++;
1164 if ( _iter == _verts->end() )
1165 {
1166 _iter = _verts->begin();
1167 _done = true;
1168 }
1169 else if ( _iter+1 == _verts->end() && !_closeLoop )
1170 {
1171 _done = true;
1172 }
1173
1174 return Segment( p0, *_iter );
1175 }
1176