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