static VALUE method_geometry_envelope(VALUE self) { VALUE result; RGeo_GeometryData* self_data; const GEOSGeometry* self_geom; GEOSContextHandle_t geos_context; GEOSGeometry* envelope; result = Qnil; self_data = RGEO_GEOMETRY_DATA_PTR(self); self_geom = self_data->geom; if (self_geom) { geos_context = self_data->geos_context; envelope = GEOSEnvelope_r(geos_context, self_geom); // GEOS returns an "empty" point for an empty collection's envelope. // We don't allow that type, so we replace it with an empty collection. if (!envelope || GEOSGeomTypeId_r(geos_context, envelope) == GEOS_POINT && GEOSGetNumCoordinates_r(geos_context, envelope) == 0) { if (envelope) { GEOSGeom_destroy_r(geos_context, envelope); } envelope = GEOSGeom_createCollection_r(geos_context, GEOS_GEOMETRYCOLLECTION, NULL, 0); } result = rgeo_wrap_geos_geometry(self_data->factory, envelope, Qnil); } return result; }
GEOSGeometry *LineAccumulator::as_geom(GEOSContextHandle_t handle) { m_lines.remove_if(degenerate_line); if(m_lines.size() > 1) { //std::cerr << "Checking first & last" << std::endl; Point first, last; first = m_lines.front().front(); last = m_lines.back().back(); //std::cerr << "first: " << first.x << ", " << first.y << std::endl; //std::cerr << "last: " << last.x << ", " << last.y << std::endl; if(close(first.x, last.x) && close(first.y, last.y)) { m_lines.front().pop_front(); m_lines.back().splice(m_lines.back().end(), m_lines.front()); m_lines.pop_front(); } } std::vector<GEOSGeometry *> geoms; std::list<Line>::const_iterator ilines; for(ilines = m_lines.begin(); ilines != m_lines.end(); ++ilines) { std::list<Point>::const_iterator ipoints; int i; GEOSCoordSequence *coords = GEOSCoordSeq_create_r(handle, (*ilines).size(), 2); for(ipoints = (*ilines).begin(), i = 0; ipoints != (*ilines).end(); ++ipoints, ++i) { GEOSCoordSeq_setX_r(handle, coords, i, ipoints->x); GEOSCoordSeq_setY_r(handle, coords, i, ipoints->y); } geoms.push_back(GEOSGeom_createLineString_r(handle, coords)); } GEOSGeometry *geom; if(geoms.empty()) { geom = GEOSGeom_createEmptyCollection_r(handle, GEOS_MULTILINESTRING); } else { geom = GEOSGeom_createCollection_r(handle, GEOS_MULTILINESTRING, &geoms[0], geoms.size()); } return geom; }
static VALUE method_geometry_boundary(VALUE self) { VALUE result = Qnil; RGeo_GeometryData* self_data = RGEO_GEOMETRY_DATA_PTR(self); const GEOSGeometry* self_geom = self_data->geom; if (self_geom) { GEOSContextHandle_t geos_context = self_data->geos_context; GEOSGeometry* boundary = GEOSBoundary_r(geos_context, self_geom); // GEOS returns NULL for the boundary of an empty collection. // Replace that with an empty collection. if (!boundary) { boundary = GEOSGeom_createCollection_r(geos_context, GEOS_GEOMETRYCOLLECTION, NULL, 0); } result = rgeo_wrap_geos_geometry(self_data->factory, boundary, Qnil); } return result; }
static GEOSGeometry *collectFacesWithEvenAncestors( Face **faces, int nfaces ) { GEOSContextHandle_t handle = QgsGeos::getGEOSHandler(); unsigned int ngeoms = 0; GEOSGeometry **geoms = new GEOSGeometry*[nfaces]; for ( int i = 0; i < nfaces; ++i ) { Face *f = faces[i]; if ( countParens( f ) % 2 ) continue; // we skip odd parents geoms geoms[ngeoms++] = GEOSGeom_clone_r( handle, f->geom ); } GEOSGeometry *ret = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOLYGON, geoms, ngeoms ); delete [] geoms; return ret; }
static VALUE method_geometry_envelope(VALUE self) { VALUE result; RGeo_GeometryData* self_data; const GEOSGeometry* self_geom; GEOSContextHandle_t geos_context; GEOSGeometry* envelope; result = Qnil; self_data = RGEO_GEOMETRY_DATA_PTR(self); self_geom = self_data->geom; if (self_geom) { geos_context = self_data->geos_context; envelope = GEOSEnvelope_r(geos_context, self_geom); if (!envelope) { envelope = GEOSGeom_createCollection_r(geos_context, GEOS_GEOMETRYCOLLECTION, NULL, 0); } result = rgeo_wrap_geos_geometry(self_data->factory, envelope, Qnil); } return result; }
SEXP rgeos_topologyfunc(SEXP env, SEXP obj, SEXP id, SEXP byid, p_topofunc topofunc) { GEOSContextHandle_t GEOShandle = getContextHandle(env); SEXP p4s = GET_SLOT(obj, install("proj4string")); GEOSGeom geom = rgeos_convert_R2geos(env, obj); int type = GEOSGeomTypeId_r(GEOShandle, geom); int n = 1; if (LOGICAL_POINTER(byid)[0] && type == GEOS_GEOMETRYCOLLECTION) n = GEOSGetNumGeometries_r(GEOShandle, geom); if (n < 1) error("rgeos_topologyfunc: invalid number of geometries"); GEOSGeom *resgeoms = (GEOSGeom *) R_alloc((size_t) n, sizeof(GEOSGeom)); for(int i=0; i<n; i++) { const GEOSGeometry *curgeom = (n > 1) ? (GEOSGeom) GEOSGetGeometryN_r(GEOShandle, geom, i) : geom; if (curgeom == NULL) error("rgeos_topologyfunc: unable to get subgeometries"); if ( topofunc == GEOSUnionCascaded_r && GEOSGeomTypeId_r(GEOShandle, curgeom) == GEOS_POLYGON) { resgeoms[i] = GEOSGeom_clone_r(GEOShandle, curgeom); } else { resgeoms[i] = topofunc(GEOShandle, curgeom); if (resgeoms[i] == NULL) error("rgeos_topologyfunc: unable to calculate"); } } GEOSGeom_destroy_r(GEOShandle, geom); GEOSGeom res = (n == 1) ? resgeoms[0] : GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, resgeoms, (unsigned int) n); return( rgeos_convert_geos2R(env, res, p4s, id) ); // releases res }
// Will return NULL on error (expect error handler being called by then) static GEOSGeometry *LWGEOM_GEOS_makeValidCollection( const GEOSGeometry *gin, QString &errorMessage ) { GEOSContextHandle_t handle = QgsGeos::getGEOSHandler(); int nvgeoms = GEOSGetNumGeometries_r( handle, gin ); if ( nvgeoms == -1 ) { errorMessage = QStringLiteral( "GEOSGetNumGeometries: %1" ).arg( QStringLiteral( "?" ) ); return nullptr; } QVector<GEOSGeometry *> vgeoms( nvgeoms ); for ( int i = 0; i < nvgeoms; ++i ) { vgeoms[i] = LWGEOM_GEOS_makeValid( GEOSGetGeometryN_r( handle, gin, i ), errorMessage ); if ( ! vgeoms[i] ) { while ( i-- ) GEOSGeom_destroy_r( handle, vgeoms[i] ); // we expect lwerror being called already by makeValid return nullptr; } } // Collect areas and lines (if any line) try { return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms.data(), nvgeoms ); } catch ( GEOSException &e ) { // cleanup and throw for ( int i = 0; i < nvgeoms; ++i ) GEOSGeom_destroy_r( handle, vgeoms[i] ); errorMessage = QStringLiteral( "GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() ); return nullptr; } }
SEXP rgeos_simplify(SEXP env, SEXP obj, SEXP tol, SEXP id, SEXP byid, SEXP topPres) { GEOSContextHandle_t GEOShandle = getContextHandle(env); SEXP p4s = GET_SLOT(obj, install("proj4string")); GEOSGeom geom = rgeos_convert_R2geos(env, obj); int type = GEOSGeomTypeId_r(GEOShandle, geom); int preserve = LOGICAL_POINTER(topPres)[0]; double tolerance = NUMERIC_POINTER(tol)[0]; int n = 1; if (LOGICAL_POINTER(byid)[0] && type == GEOS_GEOMETRYCOLLECTION) n = GEOSGetNumGeometries_r(GEOShandle, geom); if (n < 1) error("rgeos_simplify: invalid number of geometries"); GEOSGeom *resgeoms = (GEOSGeom *) R_alloc((size_t) n, sizeof(GEOSGeom)); for(int i=0; i<n; i++) { const GEOSGeometry *curgeom = (n > 1) ? (GEOSGeom) GEOSGetGeometryN_r(GEOShandle, geom, i) : geom; if (curgeom == NULL) error("rgeos_topologyfunc: unable to get subgeometries"); resgeoms[i] = (preserve) ? GEOSTopologyPreserveSimplify_r(GEOShandle, curgeom, tolerance) : GEOSSimplify_r(GEOShandle, curgeom, tolerance); } GEOSGeom_destroy_r(GEOShandle, geom); GEOSGeom res = (n == 1) ? resgeoms[0] : GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, resgeoms, (unsigned int) n); return( rgeos_convert_geos2R(env, res, p4s, id) ); }
QgsGeometry QgsGeometryAnalyzer::createOffsetGeometry( const QgsGeometry& geom, const QgsGeometry& lineGeom, double offset ) { if ( !geom || lineGeom.isEmpty() ) { return QgsGeometry(); } QList<QgsGeometry> inputGeomList; if ( geom.isMultipart() ) { inputGeomList = geom.asGeometryCollection(); } else { inputGeomList.push_back( geom ); } QList<GEOSGeometry*> outputGeomList; QList<QgsGeometry>::const_iterator inputGeomIt = inputGeomList.constBegin(); GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt ) { if ( geom.type() == QgsWkbTypes::LineGeometry ) { GEOSGeometry* inputGeomItGeos = inputGeomIt->exportToGeos(); GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( geosctxt, inputGeomItGeos, -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ); GEOSGeom_destroy_r( geosctxt, inputGeomItGeos ); if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) ) { return QgsGeometry(); } if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 ) { GEOSGeom_destroy_r( geosctxt, offsetGeom ); return QgsGeometry(); } outputGeomList.push_back( offsetGeom ); } else if ( geom.type() == QgsWkbTypes::PointGeometry ) { QgsPoint p = ( *inputGeomIt ).asPoint(); p = createPointOffset( p.x(), p.y(), offset, lineGeom ); GEOSCoordSequence* ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 ); GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() ); GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() ); GEOSGeometry* geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq ); outputGeomList.push_back( geosPt ); } } QgsGeometry outGeometry; if ( !geom.isMultipart() ) { GEOSGeometry* outputGeom = outputGeomList.at( 0 ); if ( outputGeom ) { outGeometry.fromGeos( outputGeom ); } } else { GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()]; for ( int i = 0; i < outputGeomList.size(); ++i ) { geomArray[i] = outputGeomList.at( i ); } GEOSGeometry* collection = nullptr; if ( geom.type() == QgsWkbTypes::PointGeometry ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() ); } else if ( geom.type() == QgsWkbTypes::LineGeometry ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() ); } outGeometry.fromGeos( collection ); delete[] geomArray; } return outGeometry; }
VALUE rgeo_wrap_geos_geometry(VALUE factory, GEOSGeometry* geom, VALUE klass) { VALUE result; RGeo_FactoryData* factory_data; GEOSContextHandle_t factory_context; VALUE klasses; RGeo_Globals* globals; VALUE inferred_klass; char is_collection; RGeo_GeometryData* data; result = Qnil; if (geom || !NIL_P(klass)) { factory_data = NIL_P(factory) ? NULL : RGEO_FACTORY_DATA_PTR(factory); factory_context = factory_data ? factory_data->geos_context : NULL; globals = factory_data ? factory_data->globals : NULL; // We don't allow "empty" points, so replace such objects with // an empty collection. if (geom && factory) { if (GEOSGeomTypeId_r(factory_context, geom) == GEOS_POINT && GEOSGetNumCoordinates_r(factory_context, geom) == 0) { GEOSGeom_destroy_r(factory_context, geom); geom = GEOSGeom_createCollection_r(factory_context, GEOS_GEOMETRYCOLLECTION, NULL, 0); klass = globals->geos_geometry_collection; } } klasses = Qnil; if (TYPE(klass) != T_CLASS) { inferred_klass = Qnil; is_collection = 0; switch (GEOSGeomTypeId_r(factory_context, geom)) { case GEOS_POINT: inferred_klass = globals->geos_point; break; case GEOS_LINESTRING: inferred_klass = globals->geos_line_string; break; case GEOS_LINEARRING: inferred_klass = globals->geos_linear_ring; break; case GEOS_POLYGON: inferred_klass = globals->geos_polygon; break; case GEOS_MULTIPOINT: inferred_klass = globals->geos_multi_point; is_collection = 1; break; case GEOS_MULTILINESTRING: inferred_klass = globals->geos_multi_line_string; is_collection = 1; break; case GEOS_MULTIPOLYGON: inferred_klass = globals->geos_multi_polygon; is_collection = 1; break; case GEOS_GEOMETRYCOLLECTION: inferred_klass = globals->geos_geometry_collection; is_collection = 1; break; default: inferred_klass = globals->geos_geometry; break; } if (TYPE(klass) == T_ARRAY && is_collection) { klasses = klass; } klass = inferred_klass; } data = ALLOC(RGeo_GeometryData); if (data) { if (geom) { GEOSSetSRID_r(factory_context, geom, factory_data->srid); } data->geos_context = factory_context; data->geom = geom; data->prep = factory_data && ((factory_data->flags & RGEO_FACTORYFLAGS_PREPARE_HEURISTIC) != 0) ? (GEOSPreparedGeometry*)1 : NULL; data->factory = factory; data->klasses = klasses; result = Data_Wrap_Struct(klass, mark_geometry_func, destroy_geometry_func, data); } } return result; }
ErrorList topolTest::checkGaps( QgsVectorLayer *layer1, QgsVectorLayer *layer2, bool isExtent ) { Q_UNUSED( layer2 ); int i = 0; ErrorList errorList; GEOSContextHandle_t geosctxt = QgsGeos::getGEOSHandler(); // could be enabled for lines and points too // so duplicate rule may be removed? if ( layer1->geometryType() != QgsWkbTypes::PolygonGeometry ) { return errorList; } QList<FeatureLayer>::iterator it; QgsGeometry g1; QList<GEOSGeometry *> geomList; qDebug() << mFeatureList1.count() << " features in list!"; for ( it = mFeatureList1.begin(); it != mFeatureList1.end(); ++it ) { qDebug() << "reading features-" << i; if ( !( ++i % 100 ) ) { emit progress( i ); } if ( testCanceled() ) { break; } g1 = it->feature.geometry(); if ( g1.isNull() ) { continue; } if ( !_canExportToGeos( g1 ) ) { continue; } if ( !g1.isGeosValid() ) { qDebug() << "invalid geometry found..skipping.." << it->feature.id(); continue; } if ( g1.isMultipart() ) { QgsMultiPolygonXY polys = g1.asMultiPolygon(); for ( int m = 0; m < polys.count(); m++ ) { QgsPolygonXY polygon = polys[m]; QgsGeometry polyGeom = QgsGeometry::fromPolygonXY( polygon ); geomList.push_back( QgsGeos::asGeos( polyGeom ).release() ); } } else { geomList.push_back( QgsGeos::asGeos( g1 ).release() ); } } GEOSGeometry **geomArray = new GEOSGeometry*[geomList.size()]; for ( int i = 0; i < geomList.size(); ++i ) { //qDebug() << "filling geometry array-" << i; geomArray[i] = geomList.at( i ); } qDebug() << "creating geometry collection-"; if ( geomList.isEmpty() ) { //qDebug() << "geometry list is empty!"; delete [] geomArray; return errorList; } GEOSGeometry *collection = nullptr; collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOLYGON, geomArray, geomList.size() ); qDebug() << "performing cascaded union..might take time..-"; GEOSGeometry *unionGeom = GEOSUnionCascaded_r( geosctxt, collection ); //delete[] geomArray; QgsGeometry test = QgsGeos::geometryFromGeos( unionGeom ); //qDebug() << "wktmerged - " << test.exportToWkt(); QString extentWkt = test.boundingBox().asWktPolygon(); QgsGeometry extentGeom = QgsGeometry::fromWkt( extentWkt ); QgsGeometry bufferExtent = extentGeom.buffer( 2, 3 ); //qDebug() << "extent wkt - " << bufferExtent->exportToWkt(); QgsGeometry diffGeoms = bufferExtent.difference( test ); if ( !diffGeoms ) { qDebug() << "difference result 0-"; return errorList; } //qDebug() << "difference gometry - " << diffGeoms->exportToWkt(); QVector<QgsGeometry> geomColl = diffGeoms.asGeometryCollection(); QgsGeometry canvasExtentPoly = QgsGeometry::fromWkt( qgsInterface->mapCanvas()->extent().asWktPolygon() ); for ( int i = 1; i < geomColl.count() ; ++i ) { QgsGeometry conflictGeom = geomColl[i]; if ( isExtent ) { if ( canvasExtentPoly.disjoint( conflictGeom ) ) { continue; } if ( canvasExtentPoly.crosses( conflictGeom ) ) { conflictGeom = conflictGeom.intersection( canvasExtentPoly ); } } QgsRectangle bBox = conflictGeom.boundingBox(); FeatureLayer ftrLayer1; ftrLayer1.layer = layer1; QList<FeatureLayer> errorFtrLayers; errorFtrLayers << ftrLayer1 << ftrLayer1; TopolErrorGaps *err = new TopolErrorGaps( bBox, conflictGeom, errorFtrLayers ); errorList << err; } return errorList; }
Polygon Polygon::simplify(double distance_tolerance, double area_tolerance) const { GEOSGeometry *smoothed = GEOSTopologyPreserveSimplify_r(m_ctx, m_geom, distance_tolerance); if (!smoothed) throw pdal_error("Unable to simplify input geometry!"); std::vector<GEOSGeometry*> geometries; int numGeom = GEOSGetNumGeometries_r(m_ctx, smoothed); for (int n = 0; n < numGeom; ++n) { const GEOSGeometry* m = GEOSGetGeometryN_r(m_ctx, smoothed, n); if (!m) throw pdal::pdal_error("Unable to Get GeometryN"); const GEOSGeometry* ering = GEOSGetExteriorRing_r(m_ctx, m); if (!ering) throw pdal::pdal_error("Unable to Get Exterior Ring"); GEOSGeometry* exterior = GEOSGeom_clone_r(m_ctx, GEOSGetExteriorRing_r(m_ctx, m)); if (!exterior) throw pdal::pdal_error("Unable to clone exterior ring!"); std::vector<GEOSGeometry*> keep_rings; int numRings = GEOSGetNumInteriorRings_r(m_ctx, m); for (int i = 0; i < numRings; ++i) { double area(0.0); const GEOSGeometry* iring = GEOSGetInteriorRingN_r(m_ctx, m, i); if (!iring) throw pdal::pdal_error("Unable to Get Interior Ring"); GEOSGeometry* cring = GEOSGeom_clone_r(m_ctx, iring); if (!cring) throw pdal::pdal_error("Unable to clone interior ring!"); GEOSGeometry* aring = GEOSGeom_createPolygon_r(m_ctx, cring, NULL, 0); int errored = GEOSArea_r(m_ctx, aring, &area); if (errored == 0) throw pdal::pdal_error("Unable to get area of ring!"); if (area > area_tolerance) { keep_rings.push_back(cring); } } GEOSGeometry* p = GEOSGeom_createPolygon_r(m_ctx, exterior, keep_rings.data(), keep_rings.size()); if (p == NULL) throw pdal::pdal_error("smooth polygon could not be created!" ); geometries.push_back(p); } GEOSGeometry* o = GEOSGeom_createCollection_r(m_ctx, GEOS_MULTIPOLYGON, geometries.data(), geometries.size()); Polygon p(o, m_srs, m_ctx); GEOSGeom_destroy_r(m_ctx, smoothed); GEOSGeom_destroy_r(m_ctx, o); return p; }
static GEOSGeometry *LWGEOM_GEOS_makeValidMultiLine( const GEOSGeometry *gin, QString &errorMessage ) { GEOSContextHandle_t handle = QgsGeos::getGEOSHandler(); int ngeoms = GEOSGetNumGeometries_r( handle, gin ); uint32_t nlines_alloc = ngeoms; QVector<GEOSGeometry *> lines; QVector<GEOSGeometry *> points; lines.reserve( nlines_alloc ); points.reserve( ngeoms ); for ( int i = 0; i < ngeoms; ++i ) { const GEOSGeometry *g = GEOSGetGeometryN_r( handle, gin, i ); GEOSGeometry *vg = LWGEOM_GEOS_makeValidLine( g, errorMessage ); if ( GEOSisEmpty_r( handle, vg ) ) { // we don't care about this one GEOSGeom_destroy_r( handle, vg ); } if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_POINT ) { points.append( vg ); } else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_LINESTRING ) { lines.append( vg ); } else if ( GEOSGeomTypeId_r( handle, vg ) == GEOS_MULTILINESTRING ) { int nsubgeoms = GEOSGetNumGeometries_r( handle, vg ); nlines_alloc += nsubgeoms; lines.reserve( nlines_alloc ); for ( int j = 0; j < nsubgeoms; ++j ) { // NOTE: ownership of the cloned geoms will be taken by final collection lines.append( GEOSGeom_clone_r( handle, GEOSGetGeometryN_r( handle, vg, j ) ) ); } } else { // NOTE: return from GEOSGeomType will leak but we really don't expect this to happen errorMessage = QStringLiteral( "unexpected geom type returned by LWGEOM_GEOS_makeValid: %1" ).arg( GEOSGeomTypeId_r( handle, vg ) ); } } GEOSGeometry *mpoint_out = nullptr; if ( points.count() ) { if ( points.count() > 1 ) { mpoint_out = GEOSGeom_createCollection_r( handle, GEOS_MULTIPOINT, points.data(), points.count() ); } else { mpoint_out = points[0]; } } GEOSGeometry *mline_out = nullptr; if ( lines.count() ) { if ( lines.count() > 1 ) { mline_out = GEOSGeom_createCollection_r( handle, GEOS_MULTILINESTRING, lines.data(), lines.count() ); } else { mline_out = lines[0]; } } if ( mline_out && mpoint_out ) { GEOSGeometry *collection[2]; collection[0] = mline_out; collection[1] = mpoint_out; return GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, collection, 2 ); } else if ( mline_out ) { return mline_out; } else if ( mpoint_out ) { return mpoint_out; } else { return nullptr; } }
// Will return NULL on error (expect error handler being called by then) Q_NOWARN_UNREACHABLE_PUSH static GEOSGeometry *LWGEOM_GEOS_makeValidPolygon( const GEOSGeometry *gin, QString &errorMessage ) { GEOSContextHandle_t handle = QgsGeos::getGEOSHandler(); GEOSGeom gout; GEOSGeom geos_bound; GEOSGeom geos_cut_edges, geos_area, collapse_points; GEOSGeometry *vgeoms[3]; // One for area, one for cut-edges unsigned int nvgeoms = 0; Q_ASSERT( GEOSGeomTypeId_r( handle, gin ) == GEOS_POLYGON || GEOSGeomTypeId_r( handle, gin ) == GEOS_MULTIPOLYGON ); geos_bound = GEOSBoundary_r( handle, gin ); if ( !geos_bound ) return nullptr; // Use noded boundaries as initial "cut" edges geos_cut_edges = LWGEOM_GEOS_nodeLines( geos_bound ); if ( !geos_cut_edges ) { GEOSGeom_destroy_r( handle, geos_bound ); errorMessage = QStringLiteral( "LWGEOM_GEOS_nodeLines() failed" ); return nullptr; } // NOTE: the noding process may drop lines collapsing to points. // We want to retrieve any of those { GEOSGeometry *pi = nullptr; GEOSGeometry *po = nullptr; try { pi = GEOSGeom_extractUniquePoints_r( handle, geos_bound ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_bound ); errorMessage = QStringLiteral( "GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() ); return nullptr; } try { po = GEOSGeom_extractUniquePoints_r( handle, geos_cut_edges ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_bound ); GEOSGeom_destroy_r( handle, pi ); errorMessage = QStringLiteral( "GEOSGeom_extractUniquePoints(): %1" ).arg( e.what() ); return nullptr; } try { collapse_points = GEOSDifference_r( handle, pi, po ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_bound ); GEOSGeom_destroy_r( handle, pi ); GEOSGeom_destroy_r( handle, po ); errorMessage = QStringLiteral( "GEOSDifference(): %1" ).arg( e.what() ); return nullptr; } GEOSGeom_destroy_r( handle, pi ); GEOSGeom_destroy_r( handle, po ); } GEOSGeom_destroy_r( handle, geos_bound ); // And use an empty geometry as initial "area" try { geos_area = GEOSGeom_createEmptyPolygon_r( handle ); } catch ( GEOSException &e ) { errorMessage = QStringLiteral( "GEOSGeom_createEmptyPolygon(): %1" ).arg( e.what() ); GEOSGeom_destroy_r( handle, geos_cut_edges ); return nullptr; } // See if an area can be build with the remaining edges // and if it can, symdifference with the original area. // Iterate this until no more polygons can be created // with left-over edges. while ( GEOSGetNumGeometries_r( handle, geos_cut_edges ) ) { GEOSGeometry *new_area = nullptr; GEOSGeometry *new_area_bound = nullptr; GEOSGeometry *symdif = nullptr; GEOSGeometry *new_cut_edges = nullptr; // ASSUMPTION: cut_edges should already be fully noded try { new_area = LWGEOM_GEOS_buildArea( geos_cut_edges, errorMessage ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_cut_edges ); GEOSGeom_destroy_r( handle, geos_area ); errorMessage = QStringLiteral( "LWGEOM_GEOS_buildArea() threw an error: %1" ).arg( e.what() ); return nullptr; } if ( GEOSisEmpty_r( handle, new_area ) ) { // no more rings can be build with thes edges GEOSGeom_destroy_r( handle, new_area ); break; } // We succeeded in building a ring ! // Save the new ring boundaries first (to compute // further cut edges later) try { new_area_bound = GEOSBoundary_r( handle, new_area ); } catch ( GEOSException &e ) { // We did check for empty area already so // this must be some other error errorMessage = QStringLiteral( "GEOSBoundary() threw an error: %1" ).arg( e.what() ); GEOSGeom_destroy_r( handle, new_area ); GEOSGeom_destroy_r( handle, geos_area ); return nullptr; } // Now symdiff new and old area try { symdif = GEOSSymDifference_r( handle, geos_area, new_area ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_cut_edges ); GEOSGeom_destroy_r( handle, new_area ); GEOSGeom_destroy_r( handle, new_area_bound ); GEOSGeom_destroy_r( handle, geos_area ); errorMessage = QStringLiteral( "GEOSSymDifference() threw an error: %1" ).arg( e.what() ); return nullptr; } GEOSGeom_destroy_r( handle, geos_area ); GEOSGeom_destroy_r( handle, new_area ); geos_area = symdif; symdif = nullptr; // Now let's re-set geos_cut_edges with what's left // from the original boundary. // ASSUMPTION: only the previous cut-edges can be // left, so we don't need to reconsider // the whole original boundaries // // NOTE: this is an expensive operation. try { new_cut_edges = GEOSDifference_r( handle, geos_cut_edges, new_area_bound ); } catch ( GEOSException &e ) { GEOSGeom_destroy_r( handle, geos_cut_edges ); GEOSGeom_destroy_r( handle, new_area_bound ); GEOSGeom_destroy_r( handle, geos_area ); errorMessage = QStringLiteral( "GEOSDifference() threw an error: %1" ).arg( e.what() ); return nullptr; } GEOSGeom_destroy_r( handle, geos_cut_edges ); GEOSGeom_destroy_r( handle, new_area_bound ); geos_cut_edges = new_cut_edges; } if ( !GEOSisEmpty_r( handle, geos_area ) ) { vgeoms[nvgeoms++] = geos_area; } else { GEOSGeom_destroy_r( handle, geos_area ); } if ( !GEOSisEmpty_r( handle, geos_cut_edges ) ) { vgeoms[nvgeoms++] = geos_cut_edges; } else { GEOSGeom_destroy_r( handle, geos_cut_edges ); } if ( !GEOSisEmpty_r( handle, collapse_points ) ) { vgeoms[nvgeoms++] = collapse_points; } else { GEOSGeom_destroy_r( handle, collapse_points ); } if ( 1 == nvgeoms ) { // Return cut edges gout = vgeoms[0]; } else { // Collect areas and lines (if any line) try { gout = GEOSGeom_createCollection_r( handle, GEOS_GEOMETRYCOLLECTION, vgeoms, nvgeoms ); } catch ( GEOSException &e ) { errorMessage = QStringLiteral( "GEOSGeom_createCollection() threw an error: %1" ).arg( e.what() ); // TODO: cleanup! return nullptr; } } return gout; }
bool QgsGeometryAnalyzer::createOffsetGeometry( QgsGeometry* geom, QgsGeometry* lineGeom, double offset ) { if ( !geom || !lineGeom ) { return false; } QList<QgsGeometry*> inputGeomList; if ( geom->isMultipart() ) { inputGeomList = geom->asGeometryCollection(); } else { inputGeomList.push_back( geom ); } QList<GEOSGeometry*> outputGeomList; QList<QgsGeometry*>::const_iterator inputGeomIt = inputGeomList.constBegin(); GEOSContextHandle_t geosctxt = QgsGeometry::getGEOSHandler(); for ( ; inputGeomIt != inputGeomList.constEnd(); ++inputGeomIt ) { if ( geom->type() == QGis::Line ) { //geos 3.3 needed for line offsets #if defined(GEOS_VERSION_MAJOR) && defined(GEOS_VERSION_MINOR) && \ ((GEOS_VERSION_MAJOR>3) || ((GEOS_VERSION_MAJOR==3) && (GEOS_VERSION_MINOR>=3))) GEOSGeometry* offsetGeom = GEOSOffsetCurve_r( geosctxt, ( *inputGeomIt )->asGeos(), -offset, 8 /*quadSegments*/, 0 /*joinStyle*/, 5.0 /*mitreLimit*/ ); if ( !offsetGeom || !GEOSisValid_r( geosctxt, offsetGeom ) ) { return false; } if ( !GEOSisValid_r( geosctxt, offsetGeom ) || GEOSGeomTypeId_r( geosctxt, offsetGeom ) != GEOS_LINESTRING || GEOSGeomGetNumPoints_r( geosctxt, offsetGeom ) < 1 ) { GEOSGeom_destroy_r( geosctxt, offsetGeom ); return false; } outputGeomList.push_back( offsetGeom ); #else outputGeomList.push_back( GEOSGeom_clone_r( geosctxt, ( *inputGeomIt )->asGeos() ) ); #endif } else if ( geom->type() == QGis::Point ) { QgsPoint p = ( *inputGeomIt )->asPoint(); p = createPointOffset( p.x(), p.y(), offset, lineGeom ); GEOSCoordSequence* ptSeq = GEOSCoordSeq_create_r( geosctxt, 1, 2 ); GEOSCoordSeq_setX_r( geosctxt, ptSeq, 0, p.x() ); GEOSCoordSeq_setY_r( geosctxt, ptSeq, 0, p.y() ); GEOSGeometry* geosPt = GEOSGeom_createPoint_r( geosctxt, ptSeq ); outputGeomList.push_back( geosPt ); } } if ( !geom->isMultipart() ) { GEOSGeometry* outputGeom = outputGeomList.at( 0 ); if ( outputGeom ) { geom->fromGeos( outputGeom ); } } else { GEOSGeometry** geomArray = new GEOSGeometry*[outputGeomList.size()]; for ( int i = 0; i < outputGeomList.size(); ++i ) { geomArray[i] = outputGeomList.at( i ); } GEOSGeometry* collection = 0; if ( geom->type() == QGis::Point ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTIPOINT, geomArray, outputGeomList.size() ); } else if ( geom->type() == QGis::Line ) { collection = GEOSGeom_createCollection_r( geosctxt, GEOS_MULTILINESTRING, geomArray, outputGeomList.size() ); } geom->fromGeos( collection ); delete[] geomArray; } return true; }
SEXP rgeos_convert_geos2R(SEXP env, GEOSGeom geom, SEXP p4s, SEXP id) { GEOSContextHandle_t GEOShandle = getContextHandle(env); int type = GEOSGeomTypeId_r(GEOShandle, geom); int ng = GEOSGetNumGeometries_r(GEOShandle, geom); if (ng == -1) error("rgeos_convert_geos2R: invalid number of subgeometries"); if (type == GEOS_GEOMETRYCOLLECTION && ng==0 && GEOSisEmpty_r(GEOShandle,geom)) { GEOSGeom_destroy_r(GEOShandle, geom); return(R_NilValue); } ng = ng ? ng : 1; // Empty MULTI type geometries return size 0 int pc=0; SEXP ans=NULL; switch(type) { // Determine appropriate conversion for the collection case -1: error("rgeos_convert_geos2R: unknown geometry type"); break; case GEOS_POINT: case GEOS_MULTIPOINT: PROTECT( ans = rgeos_geospoint2SpatialPoints(env, geom, p4s, id, ng) ); pc++; break; case GEOS_LINEARRING: PROTECT( ans = rgeos_geosring2SpatialRings(env, geom, p4s, id, ng)); pc++; break; case GEOS_LINESTRING: case GEOS_MULTILINESTRING: PROTECT( ans = rgeos_geosline2SpatialLines(env, geom, p4s, id, 1) ); pc++; break; case GEOS_POLYGON: case GEOS_MULTIPOLYGON: PROTECT( ans = rgeos_geospolygon2SpatialPolygons(env, geom,p4s, id, 1) ); pc++; break; case GEOS_GEOMETRYCOLLECTION: { int gctypes[] = {0,0,0,0,0,0,0,0}; int gctypen[] = {0,0,0,0,0,0,0,0}; int n=0; int *types = (int *) R_alloc((size_t) ng, sizeof(int)); for (int i=0; i<ng; i++) { const GEOSGeometry *subgeom = GEOSGetGeometryN_r(GEOShandle, geom, i); if (subgeom == NULL) error("rgeos_convert_geos2R: unable to retrieve subgeometry"); int ns = GEOSGetNumGeometries_r(GEOShandle, subgeom); if (ns == -1) error("rgeos_convert_geos2R: invalid number of geometries in subgeometry"); ns = ns ? ns : 1; n += ns; types[i] = GEOSGeomTypeId_r(GEOShandle, subgeom); if (types[i] == GEOS_GEOMETRYCOLLECTION) { Rprintf("output subgeometry %d, row.name: %s\n", i, CHAR(STRING_ELT(id, i))); for (int ii=0; ii<ns; ii++) Rprintf("subsubgeometry %d: %s\n", ii, GEOSGeomType_r(GEOShandle, GEOSGetGeometryN_r(GEOShandle, subgeom, ii))); error("Geometry collections may not contain other geometry collections"); } gctypes[ types[i] ] += 1; gctypen[ types[i] ] += ns; } int isPoint = gctypes[GEOS_POINT] + gctypes[GEOS_MULTIPOINT]; int isLine = gctypes[GEOS_LINESTRING] + gctypes[GEOS_MULTILINESTRING]; int isPoly = gctypes[GEOS_POLYGON] + gctypes[GEOS_MULTIPOLYGON]; int isRing = gctypes[GEOS_LINEARRING]; int isGC = gctypes[GEOS_GEOMETRYCOLLECTION]; if ( isPoint && !isLine && !isPoly && !isRing && !isGC ) { PROTECT( ans = rgeos_geospoint2SpatialPoints(env, geom, p4s, id, n) ); pc++; } else if ( isLine && !isPoint && !isPoly && !isRing && !isGC ) { PROTECT( ans = rgeos_geosline2SpatialLines(env, geom, p4s, id, ng) ); pc++; } else if ( isPoly && !isPoint && !isLine && !isRing && !isGC ) { PROTECT( ans = rgeos_geospolygon2SpatialPolygons(env, geom, p4s,id, ng) ); pc++; } else if ( isRing && !isPoint && !isLine && !isPoly && !isGC ) { PROTECT( ans = rgeos_geosring2SpatialRings(env, geom, p4s, id, ng) ); pc++; } else { //Rprintf("isPoint: %d isLine: %d isPoly: %d isRing: %d isGC: %d\n",isPoint, isLine, isPoly, isRing, isGC); int m = MAX(MAX(MAX(isPoint,isLine),isPoly),isRing); if (length(id) < m) { char buf[BUFSIZ]; PROTECT(id = NEW_CHARACTER(m)); pc++; for (int i=0;i<m;i++) { sprintf(buf,"%d",i); SET_STRING_ELT(id, i, COPY_TO_USER_STRING(buf)); } } GEOSGeom *GCS[4]; GCS[0] = (GEOSGeom *) R_alloc((size_t) isPoint, sizeof(GEOSGeom)); GCS[1] = (GEOSGeom *) R_alloc((size_t) isLine, sizeof(GEOSGeom)); GCS[2] = (GEOSGeom *) R_alloc((size_t) isRing, sizeof(GEOSGeom)); GCS[3] = (GEOSGeom *) R_alloc((size_t) isPoly, sizeof(GEOSGeom)); SEXP ptID, lID, rID, pID; PROTECT(ptID = NEW_CHARACTER(isPoint)); pc++; PROTECT(lID = NEW_CHARACTER(isLine)); pc++; PROTECT(rID = NEW_CHARACTER(isRing)); pc++; PROTECT(pID = NEW_CHARACTER(isPoly)); pc++; int typei[] = {0,0,0,0}; for (int i=0; i<ng; i++) { const GEOSGeometry *subgeom = GEOSGetGeometryN_r(GEOShandle, geom, i); if (subgeom == NULL) error("rgeos_convert_geos2R: unable to retrieve subgeometry"); int j = -1; SEXP cur_id=NULL; if (types[i]==GEOS_POINT || types[i]==GEOS_MULTIPOINT) { j=0; cur_id=ptID; } else if (types[i]==GEOS_LINESTRING || types[i]==GEOS_MULTILINESTRING) { j=1; cur_id=lID; } else if (types[i]==GEOS_LINEARRING) { j=2; cur_id=rID; } else if (types[i]==GEOS_POLYGON || types[i]==GEOS_MULTIPOLYGON) { j=3; cur_id=pID; } if (GCS[j] == NULL) error("rgeos_convert_geos2R: GCS element is NULL (this should never happen)."); GCS[j][ typei[j] ] = GEOSGeom_clone_r(GEOShandle, subgeom); SET_STRING_ELT(cur_id, typei[j], STRING_ELT(id,typei[j])); typei[j]++; } SEXP points = R_NilValue; SEXP lines = R_NilValue; SEXP rings = R_NilValue; SEXP polys = R_NilValue; if (isPoint) { GEOSGeom ptGC = GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, GCS[0], (unsigned int) isPoint); PROTECT( points = rgeos_convert_geos2R(env, ptGC, p4s, ptID) ); pc++; } if (isLine) { GEOSGeom lGC = GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, GCS[1], (unsigned int) isLine); PROTECT( lines = rgeos_convert_geos2R(env, lGC, p4s, lID) ); pc++; } if (isRing) { GEOSGeom rGC = GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, GCS[2], (unsigned int) isRing); PROTECT( rings = rgeos_convert_geos2R(env, rGC, p4s, rID) ); pc++; } if (isPoly) { GEOSGeom pGC = GEOSGeom_createCollection_r(GEOShandle, GEOS_GEOMETRYCOLLECTION, GCS[3], (unsigned int) isPoly); PROTECT( polys = rgeos_convert_geos2R(env, pGC, p4s, pID) ); pc++; } PROTECT(ans = NEW_OBJECT(MAKE_CLASS("SpatialCollections"))); pc++; SET_SLOT(ans, install("proj4string"), p4s); SET_SLOT(ans, install("pointobj"), points); SET_SLOT(ans, install("lineobj"), lines); SET_SLOT(ans, install("ringobj"), rings); SET_SLOT(ans, install("polyobj"), polys); SEXP plotOrder; PROTECT(plotOrder = NEW_INTEGER(4)); pc++; INTEGER_POINTER(plotOrder)[0] = 4; INTEGER_POINTER(plotOrder)[1] = 3; INTEGER_POINTER(plotOrder)[2] = 2; INTEGER_POINTER(plotOrder)[3] = 1; SET_SLOT(ans, install("plotOrder"), plotOrder); SEXP bbox; PROTECT(bbox = rgeos_geom2bbox(env, geom)); pc++; SET_SLOT(ans, install("bbox"), bbox); } break; } default: error("rgeos_convert_geos2R: Unknown geometry type"); } GEOSGeom_destroy_r(GEOShandle, geom); UNPROTECT(pc); return(ans); }