//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context
QgsGeometry* QgsMapToPixelSimplifier::simplify( QgsGeometry* geometry ) const
{
  QgsGeometry* g = new QgsGeometry();

  size_t wkbSize = geometry->wkbSize();
  unsigned char* wkb = ( unsigned char* )malloc( wkbSize );
  memcpy( wkb, geometry->asWkb(), wkbSize );
  g->fromWkb( wkb, wkbSize );
  simplifyGeometry( g, mSimplifyFlags, mTolerance );

  return g;
}
Ejemplo n.º 2
0
//! Returns a simplified version the specified geometry (Removing duplicated points) when is applied the specified map2pixel context
QgsGeometry QgsMapToPixelSimplifier::simplify( const QgsGeometry& geometry ) const
{
  if ( geometry.isEmpty() )
  {
    return QgsGeometry();
  }
  if ( mSimplifyFlags == QgsMapToPixelSimplifier::NoFlags )
  {
    return geometry;
  }

  // Check whether the geometry can be simplified using the map2pixel context
  const QgsWkbTypes::Type singleType = QgsWkbTypes::singleType( geometry.wkbType() );
  const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( singleType );
  if ( flatType == QgsWkbTypes::Point )
  {
    return geometry;
  }

  const bool isaLinearRing = flatType == QgsWkbTypes::Polygon;
  const int numPoints = geometry.geometry()->nCoordinates();

  if ( numPoints <= ( isaLinearRing ? 6 : 3 ) )
  {
    // No simplify simple geometries
    return geometry;
  }

  const QgsRectangle envelope = geometry.boundingBox();
  if ( qMax( envelope.width(), envelope.height() ) / numPoints > mTolerance * 2.0 )
  {
    //points are in average too far apart to lead to any significant simplification
    return geometry;
  }

  return simplifyGeometry( mSimplifyFlags, mSimplifyAlgorithm, geometry.wkbType(), *geometry.geometry(), envelope, mTolerance, false );
}
Ejemplo n.º 3
0
//! Simplify the WKB-geometry using the specified tolerance
QgsGeometry QgsMapToPixelSimplifier::simplifyGeometry(
  int simplifyFlags,
  SimplifyAlgorithm simplifyAlgorithm,
  QgsWkbTypes::Type wkbType,
  const QgsAbstractGeometry& geometry,
  const QgsRectangle &envelope, double map2pixelTol,
  bool isaLinearRing )
{
  bool isGeneralizable = true;

  // Can replace the geometry by its BBOX ?
  if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) &&
      isGeneralizableByMapBoundingBox( envelope, map2pixelTol ) )
  {
    return generalizeWkbGeometryByBoundingBox( wkbType, geometry, envelope );
  }

  if ( !( simplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry ) )
    isGeneralizable = false;

  const QgsWkbTypes::Type flatType = QgsWkbTypes::flatType( wkbType );

  // Write the geometry
  if ( flatType == QgsWkbTypes::LineString || flatType == QgsWkbTypes::CircularString )
  {
    const QgsCurve& srcCurve = dynamic_cast<const QgsCurve&>( geometry );
    QScopedPointer<QgsCurve> output( createEmptySameTypeGeom( srcCurve ) );
    double x = 0.0, y = 0.0, lastX = 0.0, lastY = 0.0;
    QgsRectangle r;
    r.setMinimal();

    const int numPoints = srcCurve.numPoints();

    if ( numPoints <= ( isaLinearRing ? 4 : 2 ) )
      isGeneralizable = false;

    bool isLongSegment;
    bool hasLongSegments = false; //-> To avoid replace the simplified geometry by its BBOX when there are 'long' segments.

    // Check whether the LinearRing is really closed.
    if ( isaLinearRing )
    {
      isaLinearRing = qgsDoubleNear( srcCurve.xAt( 0 ), srcCurve.xAt( numPoints - 1 ) ) &&
                      qgsDoubleNear( srcCurve.yAt( 0 ), srcCurve.yAt( numPoints - 1 ) );
    }

    // Process each vertex...
    switch ( simplifyAlgorithm )
    {
      case SnapToGrid:
      {
        double gridOriginX = envelope.xMinimum();
        double gridOriginY = envelope.yMinimum();

        // Use a factor for the maximum displacement distance for simplification, similar as GeoServer does
        float gridInverseSizeXY = map2pixelTol != 0 ? ( float )( 1.0f / ( 0.8 * map2pixelTol ) ) : 0.0f;

        for ( int i = 0; i < numPoints; ++i )
        {
          x = srcCurve.xAt( i );
          y = srcCurve.yAt( i );

          if ( i == 0 ||
               !isGeneralizable ||
               !equalSnapToGrid( x, y, lastX, lastY, gridOriginX, gridOriginY, gridInverseSizeXY ) ||
               ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
          {
            output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( x, y ) );
            lastX = x;
            lastY = y;
          }

          r.combineExtentWith( x, y );
        }
        break;
      }

      case Visvalingam:
      {
        map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'Area' calculations.

        EFFECTIVE_AREAS ea( srcCurve );

        int set_area = 0;
        ptarray_calc_areas( &ea, isaLinearRing ? 4 : 2, set_area, map2pixelTol );

        for ( int i = 0; i < numPoints; ++i )
        {
          if ( ea.res_arealist[ i ] > map2pixelTol )
          {
            output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), ea.inpts.at( i ) );
          }
        }
        break;
      }

      case Distance:
      {
        map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

        for ( int i = 0; i < numPoints; ++i )
        {
          x = srcCurve.xAt( i );
          y = srcCurve.yAt( i );

          isLongSegment = false;

          if ( i == 0 ||
               !isGeneralizable ||
               ( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
               ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
          {
            output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( x, y ) );
            lastX = x;
            lastY = y;

            hasLongSegments |= isLongSegment;
          }

          r.combineExtentWith( x, y );
        }
      }
    }

    if ( output->numPoints() < ( isaLinearRing ? 4 : 2 ) )
    {
      // we simplified the geometry too much!
      if ( !hasLongSegments )
      {
        // approximate the geometry's shape by its bounding box
        // (rect for linear ring / one segment for line string)
        return generalizeWkbGeometryByBoundingBox( wkbType, geometry, r );
      }
      else
      {
        // Bad luck! The simplified geometry is invalid and approximation by bounding box
        // would create artifacts due to long segments.
        // We will return the original geometry
        return QgsGeometry( geometry.clone() );
      }
    }

    if ( isaLinearRing )
    {
      // make sure we keep the linear ring closed
      if ( !qgsDoubleNear( lastX, output->xAt( 0 ) ) || !qgsDoubleNear( lastY, output->yAt( 0 ) ) )
      {
        output->insertVertex( QgsVertexId( 0, 0, output->numPoints() ), QgsPointV2( output->xAt( 0 ), output->yAt( 0 ) ) );
      }
    }

    return QgsGeometry( output.take() );
  }
  else if ( flatType == QgsWkbTypes::Polygon )
  {
    const QgsPolygonV2& srcPolygon = dynamic_cast<const QgsPolygonV2&>( geometry );
    QScopedPointer<QgsPolygonV2> polygon( new QgsPolygonV2() );
    polygon->setExteriorRing( dynamic_cast<QgsCurve*>( simplifyGeometry( simplifyFlags, simplifyAlgorithm, srcPolygon.exteriorRing()->wkbType(), *srcPolygon.exteriorRing(), envelope, map2pixelTol, true ).geometry()->clone() ) );
    for ( int i = 0; i < srcPolygon.numInteriorRings(); ++i )
    {
      const QgsCurve* sub = srcPolygon.interiorRing( i );
      polygon->addInteriorRing( dynamic_cast<QgsCurve*>( simplifyGeometry( simplifyFlags, simplifyAlgorithm, sub->wkbType(), *sub, envelope, map2pixelTol, true ).geometry()->clone() ) );
    }
    return QgsGeometry( polygon.take() );
  }
  else if ( QgsWkbTypes::isMultiType( flatType ) )
  {
    const QgsGeometryCollection& srcCollection = dynamic_cast<const QgsGeometryCollection&>( geometry );
    QScopedPointer<QgsGeometryCollection> collection( createEmptySameTypeGeom( srcCollection ) );
    const int numGeoms = srcCollection.numGeometries();
    for ( int i = 0; i < numGeoms; ++i )
    {
      const QgsAbstractGeometry* sub = srcCollection.geometryN( i );
      collection->addGeometry( simplifyGeometry( simplifyFlags, simplifyAlgorithm, sub->wkbType(), *sub, envelope, map2pixelTol, false ).geometry()->clone() );
    }
    return QgsGeometry( collection.take() );
  }
  return QgsGeometry( geometry.clone() );
}
Ejemplo n.º 4
0
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry ) const
{
    return simplifyGeometry( geometry, mSimplifyFlags, mTolerance );
}