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  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>
18  */
19 #ifdef OSGEARTH_HAVE_GEOS
20 
21 #include <osgEarthSymbology/GEOS>
22 #include <osg/Notify>
23 
24 #include <geos/geom/PrecisionModel.h>
25 #include <geos/geom/GeometryFactory.h>
26 #include <geos/geom/Coordinate.h>
27 #include <geos/geom/CoordinateSequence.h>
28 #include <geos/geom/CoordinateArraySequenceFactory.h>
29 #include <geos/geom/Geometry.h>
30 #include <geos/geom/Point.h>
31 #include <geos/geom/MultiPoint.h>
32 #include <geos/geom/Polygon.h>
33 #include <geos/geom/MultiPolygon.h>
34 #include <geos/geom/LineString.h>
35 #include <geos/geom/MultiLineString.h>
36 #include <geos/geom/LinearRing.h>
37 #include <geos/operation/valid/IsValidOp.h>
38 #include <geos/util/IllegalArgumentException.h>
39 
40 using namespace osgEarth;
41 using namespace osgEarth::Symbology;
42 using namespace geos;
43 using namespace geos::operation;
44 
45 #define LC "[GEOS] "
46 
47 #define GEOS_VERSION_AT_LEAST(MAJOR, MINOR) \
48     ((GEOS_VERSION_MAJOR>MAJOR) || (GEOS_VERSION_MAJOR==MAJOR && GEOS_VERSION_MINOR>=MINOR))
49 
50 namespace
51 {
52     std::unique_ptr<geom::CoordinateSequence>
vec3dArray2CoordSeq(const Symbology::Geometry * input,bool close,const geom::CoordinateSequenceFactory * factory)53     vec3dArray2CoordSeq( const Symbology::Geometry* input, bool close, const geom::CoordinateSequenceFactory* factory )
54     {
55         bool needToClose = close && input->size() > 2 && input->front() != input->back();
56 
57         std::vector<geos::geom::Coordinate>* coords = new std::vector<geom::Coordinate>();
58         coords->reserve( input->size() + (needToClose ? 1 : 0) );
59         for( osg::Vec3dArray::const_iterator i = input->begin(); i != input->end(); ++i )
60         {
61             coords->push_back( geom::Coordinate( i->x(), i->y(), i->z() ));
62         }
63         if ( needToClose )
64         {
65             coords->push_back( coords->front() );
66         }
67 	std::unique_ptr<geom::CoordinateSequence> seq = factory->create( coords );
68 
69         return seq;
70     }
71 
72     geom::Geometry*
import(const Symbology::Geometry * input,const geom::GeometryFactory * f)73     import( const Symbology::Geometry* input, const geom::GeometryFactory* f )
74     {
75         geom::Geometry* output = 0L;
76 
77         if ( input->getType() == Symbology::Geometry::TYPE_UNKNOWN )
78         {
79             output = 0L;
80         }
81         else if ( input->getType() == Symbology::Geometry::TYPE_MULTI )
82         {
83             const Symbology::MultiGeometry* multi = static_cast<const Symbology::MultiGeometry*>( input );
84 
85             Symbology::Geometry::Type compType = multi->getComponentType();
86 
87             std::vector<geom::Geometry*>* children = new std::vector<geom::Geometry*>();
88             for( Symbology::GeometryCollection::const_iterator i = multi->getComponents().begin(); i != multi->getComponents().end(); ++i )
89             {
90                 geom::Geometry* child = import( i->get(), f );
91                 if ( child )
92                     children->push_back( child );
93             }
94             if ( children->size() > 0 )
95             {
96                 if ( compType == Symbology::Geometry::TYPE_POLYGON )
97                     output = f->createMultiPolygon( children );
98                 else if ( compType == Symbology::Geometry::TYPE_LINESTRING )
99                     output = f->createMultiLineString( children );
100                 else if ( compType == Symbology::Geometry::TYPE_POINTSET )
101                     output = f->createMultiPoint( children );
102                 else
103                     output = f->createGeometryCollection( children );
104             }
105             else
106                 delete children;
107         }
108         else
109         {
110             // any other type will at least contain points:
111 	    std::unique_ptr<geom::CoordinateSequence> seq = 0L;
112 
113             try
114             {
115                 switch( input->getType() )
116                 {
117                 case Symbology::Geometry::TYPE_UNKNOWN:
118                     break;
119                 case Symbology::Geometry::TYPE_MULTI: break;
120 
121                 case Symbology::Geometry::TYPE_POINTSET:
122                     seq = vec3dArray2CoordSeq( input, false, f->getCoordinateSequenceFactory() );
123                     if ( seq ) output = f->createPoint( *seq );
124                     break;
125 
126                 case Symbology::Geometry::TYPE_LINESTRING:
127                     seq = vec3dArray2CoordSeq( input, false, f->getCoordinateSequenceFactory() );
128                     if ( seq ) output = f->createLineString( *seq );
129                     break;
130 
131                 case Symbology::Geometry::TYPE_RING:
132                     seq = vec3dArray2CoordSeq( input, true, f->getCoordinateSequenceFactory() );
133                     if ( seq ) output = f->createLinearRing( *seq );
134                     break;
135 
136                 case Symbology::Geometry::TYPE_POLYGON:
137                     seq = vec3dArray2CoordSeq( input, true, f->getCoordinateSequenceFactory() );
138                     geom::LinearRing* shell = 0L;
139                     if ( seq )
140                         shell = f->createLinearRing( *seq );
141 
142                     if ( shell )
143                     {
144                         const Symbology::Polygon* poly = static_cast<const Symbology::Polygon*>(input);
145                         std::vector<geom::Geometry*>* holes = poly->getHoles().size() > 0 ? new std::vector<geom::Geometry*>() : 0L;
146                         if (holes)
147                         {
148                             for( Symbology::RingCollection::const_iterator r = poly->getHoles().begin(); r != poly->getHoles().end(); ++r )
149                             {
150                                 geom::Geometry* hole = import( r->get(), f );
151                                 if ( hole ) holes->push_back( hole );
152                             }
153                             if (holes->size() == 0)
154                             {
155                                 delete holes;
156                                 holes = 0L;
157                             }
158                         }
159                         output = f->createPolygon( shell, (std::vector<geom::LinearRing * >*)holes );
160                     }
161 
162                     break;
163                 }
164             }
165             catch( util::IllegalArgumentException )
166             {
167                 // catch GEOS exceptions..
168                 //if ( seq )
169                 //    delete seq;
170 
171                 OE_DEBUG << "GEOS::import: Removed degenerate geometry" << std::endl;
172             }
173         }
174 
175         return output;
176     }
177 
178     Symbology::Geometry*
exportPolygon(const geom::Polygon * input)179     exportPolygon( const geom::Polygon* input )
180     {
181         Symbology::Polygon* output = 0L;
182 
183         const geom::LineString* outerRing = input->getExteriorRing();
184         if ( outerRing )
185         {
186             const geom::CoordinateSequence* s = outerRing->getCoordinatesRO();
187 
188             output = new Symbology::Polygon( s->getSize() );
189 
190             for( unsigned int j=0; j<s->getSize(); j++ )
191             {
192                 const geom::Coordinate& c = s->getAt( j );
193                 output->push_back( osg::Vec3d( c.x, c.y, !osg::isNaN(c.z)? c.z : 0.0) );
194                 //OE_NOTICE << "c.z = " << c.z << "\n";
195             }
196             output->rewind( Symbology::Ring::ORIENTATION_CCW );
197 
198             for( unsigned k=0; k < input->getNumInteriorRing(); k++ )
199             {
200                 const geom::LineString* inner = input->getInteriorRingN( k );
201                 const geom::CoordinateSequence* s = inner->getCoordinatesRO();
202                 Symbology::Ring* hole = new Symbology::Ring( s->getSize() );
203                 for( unsigned int m = 0; m<s->getSize(); m++ )
204                 {
205                     const geom::Coordinate& c = s->getAt( m );
206                     hole->push_back( osg::Vec3d( c.x, c.y, !osg::isNaN(c.z)? c.z : 0.0) );
207                 }
208                 hole->rewind( Symbology::Ring::ORIENTATION_CW );
209                 output->getHoles().push_back( hole );
210             }
211         }
212         return output;
213     }
214 }
215 
216 
GEOSContext()217 GEOSContext::GEOSContext()
218 {
219     // double-precison:
220     geos::geom::PrecisionModel* pm = new geos::geom::PrecisionModel(geom::PrecisionModel::FLOATING);
221 
222     // Factory will clone the PM
223 #if GEOS_VERSION_AT_LEAST(3,6)
224     _factory = geos::geom::GeometryFactory::create( pm );
225 #else
226     _factory = new geos::geom::GeometryFactory( pm );
227 #endif
228 
229     // Delete the template.
230     delete pm;
231 }
232 
~GEOSContext()233 GEOSContext::~GEOSContext()
234 {
235 #if !GEOS_VERSION_AT_LEAST(3,6)
236     delete _factory;
237 #endif
238 }
239 
240 geom::Geometry*
importGeometry(const Symbology::Geometry * input)241 GEOSContext::importGeometry(const Symbology::Geometry* input)
242 {
243     geom::Geometry* output = 0L;
244     if ( input && input->isValid() )
245     {
246 #if GEOS_VERSION_AT_LEAST(3,6)
247         output = import( input, _factory.get() );
248 #else
249         output = import( input, _factory );
250 
251         // if output is ok, it will have a pointer to f. this is probably a leak.
252         // TODO: Check whether this is a leak!! -gw
253         //if ( !output )
254         //    delete f;
255 #endif
256     }
257     return output;
258 }
259 
260 
261 
262 Symbology::Geometry*
exportGeometry(const geom::Geometry * input)263 GEOSContext::exportGeometry(const geom::Geometry* input)
264 {
265     Symbology::GeometryCollection parts;
266 
267     if ( dynamic_cast<const geom::Point*>( input ) )
268     {
269         const geom::Point* point = dynamic_cast< const geom::Point* >(input);
270         Symbology::PointSet* part = new Symbology::PointSet();
271         const geom::Coordinate* c = point->getCoordinate();
272         part->push_back(osg::Vec3d(c->x, c->y, c->z));
273         return part;
274     }
275     else if ( dynamic_cast<const geom::MultiPoint*>( input ) )
276     {
277         const geom::MultiPoint* mp = dynamic_cast<const geom::MultiPoint*>( input );
278         Symbology::PointSet* part = new Symbology::PointSet( mp->getNumPoints() );
279         for( unsigned int i=0; i < mp->getNumPoints(); i++ )
280         {
281             const geom::Geometry* g = mp->getGeometryN(i);
282             if ( g )
283             {
284                 const geom::Coordinate* c = mp->getCoordinate();
285                 if ( c )
286                 {
287                     part->push_back( osg::Vec3d( c->x, c->y, c->z ) ); //p->getX(), p->getY(), 0 ) );
288                 }
289             }
290         }
291         parts.push_back( part );
292     }
293     else if ( dynamic_cast<const geom::LineString*>( input ) )
294     {
295         const geom::LineString* line = dynamic_cast<const geom::LineString*>( input );
296         Symbology::LineString* part = new Symbology::LineString( line->getNumPoints() );
297         for( unsigned int i=0; i<line->getNumPoints(); i++ )
298         {
299             const geom::Coordinate& c = line->getCoordinateN(i);
300             part->push_back( osg::Vec3d( c.x, c.y, c.z ) ); //0 ) );
301         }
302         parts.push_back( part );
303     }
304     else if ( dynamic_cast<const geom::MultiLineString*>( input ) )
305     {
306         const geom::MultiLineString* m = dynamic_cast<const geom::MultiLineString*>( input );
307         for( unsigned int i=0; i<m->getNumGeometries(); i++ )
308         {
309             Symbology::Geometry* part = exportGeometry( m->getGeometryN(i) );
310             if ( part ) parts.push_back( part );
311         }
312     }
313     else if ( dynamic_cast<const geom::Polygon*>( input ) )
314     {
315         const geom::Polygon* poly = dynamic_cast<const geom::Polygon*>( input );
316         Symbology::Geometry* part = exportPolygon( poly );
317         if ( part ) parts.push_back( part );
318     }
319     else if ( dynamic_cast<const geom::MultiPolygon*>( input ) )
320     {
321         //OE_NOTICE << "Multipolygon" << std::endl;
322         const geom::MultiPolygon* mpoly = dynamic_cast<const geom::MultiPolygon*>( input );
323         for( unsigned int i=0; i<mpoly->getNumGeometries(); i++ )
324         {
325             Symbology::Geometry* part = exportPolygon( dynamic_cast<const geom::Polygon*>( mpoly->getGeometryN(i) ) );
326             if ( part ) parts.push_back( part );
327         }
328     }
329 
330     if ( parts.size() == 1 )
331     {
332         osg::ref_ptr<Symbology::Geometry> part = parts.front().get();
333         parts.clear();
334         return part.release();
335     }
336     else if ( parts.size() > 1 )
337     {
338         return new Symbology::MultiGeometry( parts );
339     }
340     else
341     {
342         return 0L;
343     }
344 }
345 
346 
347 void
disposeGeometry(geom::Geometry * input)348 GEOSContext::disposeGeometry(geom::Geometry* input)
349 {
350     if (input)
351     {
352 #if GEOS_VERSION_AT_LEAST(3,6)
353         _factory->destroyGeometry(input);
354 #else
355         geom::GeometryFactory* f = const_cast<geom::GeometryFactory*>(input->getFactory());
356         if ( f != _factory )
357             delete f;
358 #endif
359     }
360 }
361 
362 #endif // OSGEARTH_HAVE_GEOS
363 
364