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