Пример #1
0
static VALUE method_geometry_sym_difference(VALUE self, VALUE rhs)
{
  VALUE result;
  RGeo_GeometryData* self_data;
  const GEOSGeometry* self_geom;
  VALUE factory;
  const GEOSGeometry* rhs_geom;

  result = Qnil;
  self_data = RGEO_GEOMETRY_DATA_PTR(self);
  self_geom = self_data->geom;
  if (self_geom) {
    factory = self_data->factory;
    rhs_geom = rgeo_convert_to_geos_geometry(factory, rhs, Qnil);
    if (rhs_geom) {
      result = rgeo_wrap_geos_geometry(factory, GEOSSymDifference_r(self_data->geos_context, self_geom, rhs_geom), Qnil);
    }
  }
  return result;
}
Пример #2
0
// 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;

}