Example #1
0
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance )
{
    size_t targetWkbSize = 0;

    // Check whether the geometry can be simplified using the map2pixel context
    QGis::GeometryType geometryType = geometry->type();
    if ( !( geometryType == QGis::Line || geometryType == QGis::Polygon ) )
        return false;

    QgsRectangle envelope = geometry->boundingBox();
    QGis::WkbType wkbType = geometry->wkbType();

    unsigned char* wkb = ( unsigned char* )geometry->asWkb( );
    size_t wkbSize = geometry->wkbSize( );

    // Simplify the geometry rewriting temporally its WKB-stream for saving calloc's.
    if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, wkb, targetWkbSize, envelope, tolerance ) )
    {
        unsigned char* targetWkb = new unsigned char[targetWkbSize];
        memcpy( targetWkb, wkb, targetWkbSize );
        geometry->fromWkb( targetWkb, targetWkbSize );
        return true;
    }
    return false;
}
//! Simplifies the geometry (Removing duplicated points) when is applied the specified map2pixel context
bool QgsMapToPixelSimplifier::simplifyGeometry( QgsGeometry* geometry, int simplifyFlags, double tolerance )
{
  size_t finalWkbSize = 0;

  // Check whether the geometry can be simplified using the map2pixel context
  QGis::GeometryType geometryType = geometry->type();
  if ( !( geometryType == QGis::Line || geometryType == QGis::Polygon ) )
    return false;

  QgsRectangle envelope = geometry->boundingBox();
  QGis::WkbType wkbType = geometry->wkbType();

  const unsigned char* wkb = geometry->asWkb();
  size_t wkbSize = geometry->wkbSize();

  unsigned char* targetWkb = new unsigned char[wkbSize];
  memcpy( targetWkb, wkb, wkbSize );

  if ( simplifyWkbGeometry( simplifyFlags, wkbType, wkb, wkbSize, targetWkb, finalWkbSize, envelope, tolerance ) )
  {
    unsigned char* finalWkb = new unsigned char[finalWkbSize];
    memcpy( finalWkb, targetWkb, finalWkbSize );
    geometry->fromWkb( finalWkb, finalWkbSize );
    delete [] targetWkb;
    return true;
  }
  delete [] targetWkb;
  return false;
}
Example #3
0
//! Simplify the WKB-geometry using the specified tolerance
bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
    int simplifyFlags, QGis::WkbType wkbType,
    unsigned char* sourceWkb, size_t sourceWkbSize,
    unsigned char* targetWkb, size_t& targetWkbSize,
    const QgsRectangle& envelope, double map2pixelTol,
    bool writeHeader, bool isaLinearRing )
{
    bool isGeneralizable = true;
    bool hasZValue = QGis::wkbDimensions( wkbType ) == 3;
    bool result = false;

    // Save initial WKB settings to use when the simplification creates invalid geometries
    unsigned char* sourcePrevWkb = sourceWkb;
    unsigned char* targetPrevWkb = targetWkb;
    size_t targetWkbPrevSize = targetWkbSize;

    // Can replace the geometry by its BBOX ?
    if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) &&
            isGeneralizableByMapBoundingBox( envelope, map2pixelTol ) )
    {
        isGeneralizable = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
        if ( isGeneralizable )
            return true;
    }

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

    // Write the main header of the geometry
    if ( writeHeader )
    {
        targetWkb[0] = sourceWkb[0]; // byteOrder
        sourceWkb += 1;
        targetWkb += 1;

        int geometryType;
        memcpy( &geometryType, sourceWkb, 4 );
        int flatType = QGis::flatType(( QGis::WkbType )geometryType );
        memcpy( targetWkb, &flatType, 4 ); // type
        sourceWkb += 4;
        targetWkb += 4;

        targetWkbSize += 5;
    }

    unsigned char* wkb1 = sourceWkb;
    unsigned char* wkb2 = targetWkb;
    unsigned int flatType = QGis::flatType( wkbType );

    // Write the geometry
    if ( flatType == QGis::WKBLineString || isaLinearRing )
    {
        double x, y, lastX = 0, lastY = 0;
        QgsRectangle r;
        r.setMinimal();

        int sizeOfDoubleX = sizeof( double );
        int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );

        int numPoints;
        memcpy( &numPoints, sourceWkb, 4 );
        sourceWkb += 4;
        if ( numPoints <= ( isaLinearRing ? 5 : 2 ) )
            isGeneralizable = false;

        int numTargetPoints = 0;
        memcpy( targetWkb, &numTargetPoints, 4 );
        targetWkb += 4;
        targetWkbSize += 4;

        double* ptr = ( double* )targetWkb;
        map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

        // Check whether the LinearRing is really closed.
        if ( isaLinearRing )
        {
            double x1, y1, x2, y2;

            unsigned char* startWkbX = sourceWkb;
            unsigned char* startWkbY = startWkbX + sizeOfDoubleX;
            unsigned char* finalWkbX = sourceWkb + ( numPoints - 1 ) * ( sizeOfDoubleX + sizeOfDoubleY );
            unsigned char* finalWkbY = finalWkbX + sizeOfDoubleX;

            memcpy( &x1, startWkbX, sizeof( double ) );
            memcpy( &y1, startWkbY, sizeof( double ) );
            memcpy( &x2, finalWkbX, sizeof( double ) );
            memcpy( &y2, finalWkbY, sizeof( double ) );

            isaLinearRing = ( x1 == x2 ) && ( y1 == y2 );
        }

        // Process each vertex...
        for ( int i = 0; i < numPoints; ++i )
        {
            memcpy( &x, sourceWkb, sizeof( double ) );
            sourceWkb += sizeOfDoubleX;
            memcpy( &y, sourceWkb, sizeof( double ) );
            sourceWkb += sizeOfDoubleY;

            if ( i == 0 ||
                    !isGeneralizable ||
                    calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ||
                    ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
            {
                memcpy( ptr, &x, sizeof( double ) );
                lastX = x;
                ptr++;
                memcpy( ptr, &y, sizeof( double ) );
                lastY = y;
                ptr++;
                numTargetPoints++;
            }

            r.combineExtentWith( x, y );
        }
        targetWkb = wkb2 + 4;

        // Fix the topology of the geometry
        if ( numTargetPoints <= ( isaLinearRing ? 2 : 1 ) )
        {
            unsigned char* targetTempWkb = targetWkb;
            int targetWkbTempSize = targetWkbSize;

            sourceWkb = sourcePrevWkb;
            targetWkb = targetPrevWkb;
            targetWkbSize = targetWkbPrevSize;
            if ( generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, r, writeHeader ) )
                return true;

            targetWkb = targetTempWkb;
            targetWkbSize = targetWkbTempSize;
        }
        if ( isaLinearRing )
        {
            memcpy( &x, targetWkb + 0, sizeof( double ) );
            memcpy( &y, targetWkb + sizeof( double ), sizeof( double ) );
            if ( lastX != x || lastY != y )
            {
                memcpy( ptr, &x, sizeof( double ) );
                ptr++;
                memcpy( ptr, &y, sizeof( double ) );
                ptr++;
                numTargetPoints++;
            }
        }
        targetWkbSize += numTargetPoints * sizeof( double ) * 2;
        targetWkb = wkb2;

        memcpy( targetWkb, &numTargetPoints, 4 );
        result = numPoints != numTargetPoints;
    }
    else if ( flatType == QGis::WKBPolygon )
    {
        int numRings;
        memcpy( &numRings, sourceWkb, 4 );
        sourceWkb += 4;

        memcpy( targetWkb, &numRings, 4 );
        targetWkb += 4;
        targetWkbSize += 4;

        for ( int i = 0; i < numRings; ++i )
        {
            int numPoints_i;
            memcpy( &numPoints_i, sourceWkb, 4 );
            QgsRectangle envelope_i = numRings == 1 ? envelope : calculateBoundingBox( wkbType, sourceWkb + 4, numPoints_i );

            size_t sourceWkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
            size_t targetWkbSize_i = 0;

            result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
            sourceWkb += sourceWkbSize_i;
            targetWkb += targetWkbSize_i;

            targetWkbSize += targetWkbSize_i;
        }
    }
    else if ( flatType == QGis::WKBMultiLineString || flatType == QGis::WKBMultiPolygon )
    {
        int numGeoms;
        memcpy( &numGeoms, sourceWkb, 4 );
        sourceWkb += 4;
        wkb1 += 4;

        memcpy( targetWkb, &numGeoms, 4 );
        targetWkb += 4;
        targetWkbSize += 4;

        for ( int i = 0; i < numGeoms; ++i )
        {
            size_t sourceWkbSize_i = 0;
            size_t targetWkbSize_i = 0;

            // ... calculate the wkb-size of the current child complex geometry
            if ( flatType == QGis::WKBMultiLineString )
            {
                int numPoints_i;
                memcpy( &numPoints_i, wkb1 + 5, 4 );
                int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

                sourceWkbSize_i += 5 + wkbSize_i;
                wkb1 += 5 + wkbSize_i;
            }
            else
            {
                int numPrings_i;
                memcpy( &numPrings_i, wkb1 + 5, 4 );
                sourceWkbSize_i = 9;
                wkb1 += 9;

                for ( int j = 0; j < numPrings_i; ++j )
                {
                    int numPoints_i;
                    memcpy( &numPoints_i, wkb1, 4 );
                    int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

                    sourceWkbSize_i += wkbSize_i;
                    wkb1 += wkbSize_i;
                }
            }
            result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
            sourceWkb += sourceWkbSize_i;
            targetWkb += targetWkbSize_i;

            targetWkbSize += targetWkbSize_i;
        }
    }
    return result;
}
//! Simplify the WKB-geometry using the specified tolerance
bool QgsMapToPixelSimplifier::simplifyWkbGeometry( int simplifyFlags, QGis::WkbType wkbType, unsigned char* sourceWkb, size_t sourceWkbSize, unsigned char* targetWkb, size_t& targetWkbSize, const QgsRectangle& envelope, double map2pixelTol, bool writeHeader, bool isaLinearRing )
{
  bool canbeGeneralizable = true;
  bool hasZValue = QGis::wkbDimensions( wkbType ) == 3;
  bool result = false;

  // Can replace the geometry by its BBOX ?
  if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) && ( envelope.xMaximum() - envelope.xMinimum() ) < map2pixelTol && ( envelope.yMaximum() - envelope.yMinimum() ) < map2pixelTol )
  {
    canbeGeneralizable = generalizeWkbGeometry( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
    if ( canbeGeneralizable ) return true;
  }
  if ( !( simplifyFlags & QgsMapToPixelSimplifier::SimplifyGeometry ) ) canbeGeneralizable = false;

  // Write the main header of the geometry
  if ( writeHeader )
  {
    memcpy( targetWkb, sourceWkb, 1 ); // byteOrder
    sourceWkb += 1;
    targetWkb += 1;

    int geometryType;
    memcpy( &geometryType, sourceWkb, 4 );
    int flatType = QGis::flatType(( QGis::WkbType )geometryType );
    memcpy( targetWkb, &flatType, 4 ); // type
    sourceWkb += 4;
    targetWkb += 4;

    targetWkbSize += 5;
  }

  unsigned char* wkb1 = sourceWkb;
  unsigned char* wkb2 = targetWkb;
  unsigned int flatType = QGis::flatType( wkbType );

  // Write the geometry
  if ( flatType == QGis::WKBLineString || isaLinearRing )
  {
    double x, y, lastX = 0, lastY = 0;

    int sizeOfDoubleX = sizeof( double );
    int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );

    int numPoints;
    memcpy( &numPoints, sourceWkb, 4 );
    sourceWkb += 4;
    if ( numPoints <= ( isaLinearRing ? 5 : 2 ) ) canbeGeneralizable = false;

    int numTargetPoints = 0;
    memcpy( targetWkb, &numTargetPoints, 4 );
    targetWkb += 4;
    targetWkbSize += 4;

    double* ptr = ( double* )targetWkb;
    map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

    // Process each vertex...
    for ( int i = 0, numPoints_i = ( isaLinearRing ? numPoints - 1 : numPoints ); i < numPoints_i; ++i )
    {
      memcpy( &x, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleX;
      memcpy( &y, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleY;

      if ( i == 0 || !canbeGeneralizable || QgsMapToPixelSimplifier::calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol || ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
      {
        memcpy( ptr, &x, sizeof( double ) ); lastX = x; ptr++;
        memcpy( ptr, &y, sizeof( double ) ); lastY = y; ptr++;
        numTargetPoints++;
      }
    }
    targetWkb = wkb2 + 4;

    // Fix the topology of the geometry
    if ( isaLinearRing )
    {
      memcpy( &x, targetWkb + 0, sizeof( double ) );
      memcpy( &y, targetWkb + 8, sizeof( double ) );
      memcpy( ptr, &x, sizeof( double ) ); ptr++;
      memcpy( ptr, &y, sizeof( double ) ); ptr++;
      numTargetPoints++;
    }
    targetWkbSize += numTargetPoints * 16;
    targetWkb = wkb2;

    memcpy( targetWkb, &numTargetPoints, 4 );
    result = numPoints != numTargetPoints;
  }
  else
    if ( flatType == QGis::WKBPolygon )
    {
      int numRings;
      memcpy( &numRings, sourceWkb, 4 );
      sourceWkb += 4;

      memcpy( targetWkb, &numRings, 4 );
      targetWkb += 4;
      targetWkbSize += 4;

      for ( int i = 0; i < numRings; ++i )
      {
        int numPoints_i;
        memcpy( &numPoints_i, sourceWkb, 4 );
        QgsRectangle envelope_i = numRings == 1 ? envelope : calculateBoundingBox( wkbType, sourceWkb + 4, numPoints_i );

        size_t sourceWkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
        size_t targetWkbSize_i = 0;

        result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
        sourceWkb += sourceWkbSize_i;
        targetWkb += targetWkbSize_i;

        targetWkbSize += targetWkbSize_i;
      }
    }
    else
      if ( flatType == QGis::WKBMultiLineString || flatType == QGis::WKBMultiPolygon )
      {
        int numGeoms;
        memcpy( &numGeoms, sourceWkb, 4 );
        sourceWkb += 4;
        wkb1 += 4;

        memcpy( targetWkb, &numGeoms, 4 );
        targetWkb += 4;
        targetWkbSize += 4;

        for ( int i = 0; i < numGeoms; ++i )
        {
          size_t sourceWkbSize_i = 0;
          size_t targetWkbSize_i = 0;

          // ... calculate the wkb-size of the current child complex geometry
          if ( flatType == QGis::WKBMultiLineString )
          {
            int numPoints_i;
            memcpy( &numPoints_i, wkb1 + 5, 4 );
            int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

            sourceWkbSize_i += 5 + wkbSize_i;
            wkb1 += 5 + wkbSize_i;
          }
          else
          {
            int numPrings_i;
            memcpy( &numPrings_i, wkb1 + 5, 4 );
            sourceWkbSize_i = 9;
            wkb1 += 9;

            for ( int j = 0; j < numPrings_i; ++j )
            {
              int numPoints_i;
              memcpy( &numPoints_i, wkb1, 4 );
              int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

              sourceWkbSize_i += wkbSize_i;
              wkb1 += wkbSize_i;
            }
          }
          result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
          sourceWkb += sourceWkbSize_i;
          targetWkb += targetWkbSize_i;

          targetWkbSize += targetWkbSize_i;
        }
      }
  return result;
}
//! Simplify the WKB-geometry using the specified tolerance
bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
  int simplifyFlags, QGis::WkbType wkbType,
  const unsigned char* sourceWkb, size_t sourceWkbSize,
  unsigned char* targetWkb, size_t& targetWkbSize,
  const QgsRectangle& envelope, double map2pixelTol,
  bool writeHeader, bool isaLinearRing )
{
  bool isGeneralizable = true;
  bool hasZValue = QGis::wkbDimensions( wkbType ) == 3;
  bool result = false;

  // Save initial WKB settings to use when the simplification creates invalid geometries
  const unsigned char* sourcePrevWkb = sourceWkb;
  unsigned char* targetPrevWkb = targetWkb;
  size_t targetWkbPrevSize = targetWkbSize;

  // Can replace the geometry by its BBOX ?
  if (( simplifyFlags & QgsMapToPixelSimplifier::SimplifyEnvelope ) &&
      isGeneralizableByMapBoundingBox( envelope, map2pixelTol ) )
  {
    isGeneralizable = generalizeWkbGeometryByBoundingBox( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, envelope, writeHeader );
    if ( isGeneralizable )
      return true;
  }

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

  // Write the main header of the geometry
  if ( writeHeader )
  {
    targetWkb[0] = sourceWkb[0]; // byteOrder
    sourceWkb += 1;
    targetWkb += 1;

    int geometryType;
    memcpy( &geometryType, sourceWkb, 4 );
    int flatType = QGis::flatType(( QGis::WkbType )geometryType );
    memcpy( targetWkb, &flatType, 4 ); // type
    sourceWkb += 4;
    targetWkb += 4;

    targetWkbSize += 5;
  }

  const unsigned char* wkb1 = sourceWkb;
  unsigned char* wkb2 = targetWkb;
  unsigned int flatType = QGis::flatType( wkbType );

  // Write the geometry
  if ( flatType == QGis::WKBLineString || isaLinearRing )
  {
    double x, y, lastX = 0, lastY = 0;
    QgsRectangle r;
    r.setMinimal();

    int sizeOfDoubleX = sizeof( double );
    int sizeOfDoubleY = QGis::wkbDimensions( wkbType ) == 3 /*hasZValue*/ ? 2 * sizeof( double ) : sizeof( double );

    int numPoints;
    memcpy( &numPoints, sourceWkb, 4 );
    sourceWkb += 4;
    if ( numPoints <= ( isaLinearRing ? 5 : 2 ) )
      isGeneralizable = false;

    int numTargetPoints = 0;
    memcpy( targetWkb, &numTargetPoints, 4 );
    targetWkb += 4;
    targetWkbSize += 4;

    double* ptr = ( double* )targetWkb;
    map2pixelTol *= map2pixelTol; //-> Use mappixelTol for 'LengthSquare' calculations.

    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 )
    {
      double x1, y1, x2, y2;

      const unsigned char* startWkbX = sourceWkb;
      const unsigned char* startWkbY = startWkbX + sizeOfDoubleX;
      const unsigned char* finalWkbX = sourceWkb + ( numPoints - 1 ) * ( sizeOfDoubleX + sizeOfDoubleY );
      const unsigned char* finalWkbY = finalWkbX + sizeOfDoubleX;

      memcpy( &x1, startWkbX, sizeof( double ) );
      memcpy( &y1, startWkbY, sizeof( double ) );
      memcpy( &x2, finalWkbX, sizeof( double ) );
      memcpy( &y2, finalWkbY, sizeof( double ) );

      isaLinearRing = ( x1 == x2 ) && ( y1 == y2 );
    }

    // Process each vertex...
    for ( int i = 0; i < numPoints; ++i )
    {
      memcpy( &x, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleX;
      memcpy( &y, sourceWkb, sizeof( double ) ); sourceWkb += sizeOfDoubleY;

      isLongSegment = false;

      if ( i == 0 ||
           !isGeneralizable ||
           ( isLongSegment = ( calculateLengthSquared2D( x, y, lastX, lastY ) > map2pixelTol ) ) ||
           ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
      {
        memcpy( ptr, &x, sizeof( double ) ); lastX = x; ptr++;
        memcpy( ptr, &y, sizeof( double ) ); lastY = y; ptr++;
        numTargetPoints++;

        hasLongSegments |= isLongSegment;
      }

      r.combineExtentWith( x, y );
    }
    targetWkb = wkb2 + 4;

    if ( numTargetPoints < ( 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)
        unsigned char* targetTempWkb = targetWkb;
        size_t targetWkbTempSize = targetWkbSize;

        sourceWkb = sourcePrevWkb;
        targetWkb = targetPrevWkb;
        targetWkbSize = targetWkbPrevSize;
        if ( generalizeWkbGeometryByBoundingBox( wkbType, sourceWkb, sourceWkbSize, targetWkb, targetWkbSize, r, writeHeader ) )
          return true;

        targetWkb = targetTempWkb;
        targetWkbSize = targetWkbTempSize;
      }
      else
      {
        // Bad luck! The simplified geometry is invalid and approximation by bounding box
        // would create artifacts due to long segments. Worst of all, we may have overwritten
        // the original coordinates by the simplified ones (source and target WKB ptr can be the same)
        // so we cannot even undo the changes here. We will return invalid geometry and hope that
        // other pieces of QGIS will survive that :-/
      }
    }
    if ( isaLinearRing )
    {
      // make sure we keep the linear ring closed
      memcpy( &x, targetWkb + 0, sizeof( double ) );
      memcpy( &y, targetWkb + sizeof( double ), sizeof( double ) );
      if ( lastX != x || lastY != y )
      {
        memcpy( ptr, &x, sizeof( double ) ); ptr++;
        memcpy( ptr, &y, sizeof( double ) ); ptr++;
        numTargetPoints++;
      }
    }
    targetWkbSize += numTargetPoints * sizeof( double ) * 2;
    targetWkb = wkb2;

    memcpy( targetWkb, &numTargetPoints, 4 );
    result = numPoints != numTargetPoints;
  }
  else if ( flatType == QGis::WKBPolygon )
  {
    int numRings;
    memcpy( &numRings, sourceWkb, 4 );
    sourceWkb += 4;

    memcpy( targetWkb, &numRings, 4 );
    targetWkb += 4;
    targetWkbSize += 4;

    for ( int i = 0; i < numRings; ++i )
    {
      int numPoints_i;
      memcpy( &numPoints_i, sourceWkb, 4 );
      QgsRectangle envelope_i = numRings == 1 ? envelope : calculateBoundingBox( wkbType, sourceWkb + 4, numPoints_i );

      size_t sourceWkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );
      size_t targetWkbSize_i = 0;

      result |= simplifyWkbGeometry( simplifyFlags, wkbType, sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope_i, map2pixelTol, false, true );
      sourceWkb += sourceWkbSize_i;
      targetWkb += targetWkbSize_i;

      targetWkbSize += targetWkbSize_i;
    }
  }
  else if ( flatType == QGis::WKBMultiLineString || flatType == QGis::WKBMultiPolygon )
  {
    int numGeoms;
    memcpy( &numGeoms, sourceWkb, 4 );
    sourceWkb += 4;
    wkb1 += 4;

    memcpy( targetWkb, &numGeoms, 4 );
    targetWkb += 4;
    targetWkbSize += 4;

    for ( int i = 0; i < numGeoms; ++i )
    {
      size_t sourceWkbSize_i = 0;
      size_t targetWkbSize_i = 0;

      // ... calculate the wkb-size of the current child complex geometry
      if ( flatType == QGis::WKBMultiLineString )
      {
        int numPoints_i;
        memcpy( &numPoints_i, wkb1 + 5, 4 );
        int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

        sourceWkbSize_i += 5 + wkbSize_i;
        wkb1 += 5 + wkbSize_i;
      }
      else
      {
        int numPrings_i;
        memcpy( &numPrings_i, wkb1 + 5, 4 );
        sourceWkbSize_i = 9;
        wkb1 += 9;

        for ( int j = 0; j < numPrings_i; ++j )
        {
          int numPoints_i;
          memcpy( &numPoints_i, wkb1, 4 );
          int wkbSize_i = 4 + numPoints_i * ( hasZValue ? 3 : 2 ) * sizeof( double );

          sourceWkbSize_i += wkbSize_i;
          wkb1 += wkbSize_i;
        }
      }
      result |= simplifyWkbGeometry( simplifyFlags, QGis::singleType( wkbType ), sourceWkb, sourceWkbSize_i, targetWkb, targetWkbSize_i, envelope, map2pixelTol, true, false );
      sourceWkb += sourceWkbSize_i;
      targetWkb += targetWkbSize_i;

      targetWkbSize += targetWkbSize_i;
    }
  }
  return result;
}