Exemple #1
0
QgsRectangle QgsMapSettings::fullExtent() const
{
  // reset the map canvas extent since the extent may now be smaller
  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
  QgsRectangle fullExtent;
  fullExtent.setMinimal();

  // iterate through the map layers and test each layers extent
  // against the current min and max values
  QgsDebugMsgLevel( QStringLiteral( "Layer count: %1" ).arg( mLayers.count() ), 5 );
  const auto constMLayers = mLayers;
  for ( const QgsWeakMapLayerPointer &layerPtr : constMLayers )
  {
    if ( QgsMapLayer *lyr = layerPtr.data() )
    {
      QgsDebugMsgLevel( "Updating extent using " + lyr->name(), 5 );
      QgsDebugMsgLevel( "Input extent: " + lyr->extent().toString(), 5 );

      if ( lyr->extent().isNull() )
        continue;

      // Layer extents are stored in the coordinate system (CS) of the
      // layer. The extent must be projected to the canvas CS
      QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() );

      QgsDebugMsgLevel( "Output extent: " + extent.toString(), 5 );
      fullExtent.combineExtentWith( extent );
    }
  }

  if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 )
  {
    // If all of the features are at the one point, buffer the
    // rectangle a bit. If they are all at zero, do something a bit
    // more crude.

    if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 &&
         fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 )
    {
      fullExtent.set( -1.0, -1.0, 1.0, 1.0 );
    }
    else
    {
      const double padFactor = 1e-8;
      double widthPad = fullExtent.xMinimum() * padFactor;
      double heightPad = fullExtent.yMinimum() * padFactor;
      double xmin = fullExtent.xMinimum() - widthPad;
      double xmax = fullExtent.xMaximum() + widthPad;
      double ymin = fullExtent.yMinimum() - heightPad;
      double ymax = fullExtent.yMaximum() + heightPad;
      fullExtent.set( xmin, ymin, xmax, ymax );
    }
  }

  QgsDebugMsgLevel( "Full extent: " + fullExtent.toString(), 5 );
  return fullExtent;
}
Exemple #2
0
QgsRectangle QgsMeshLayer::extent() const
{
  if ( mDataProvider )
    return mDataProvider->extent();
  else
  {
    QgsRectangle rec;
    rec.setMinimal();
    return rec;
  }
}
//! Returns whether the device-geometry can be replaced by its BBOX when is applied the specified tolerance
bool QgsAbstractGeometrySimplifier::isGeneralizableByDeviceBoundingBox( const QVector<QPointF>& points, float mapToPixelTol )
{
  QgsRectangle r;
  r.setMinimal();

  for ( int i = 0, numPoints = points.size(); i < numPoints; ++i )
  {
    r.combineExtentWith( points[i].x(), points[i].y() );
  }
  return isGeneralizableByDeviceBoundingBox( r, mapToPixelTol );
}
void QgsExpressionSelectionDialog::mButtonZoomToFeatures_clicked()
{
  if ( mExpressionBuilder->expressionText().isEmpty() || !mMapCanvas )
    return;

  QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );

  QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( mExpressionBuilder->expressionText() )
                              .setExpressionContext( context )
                              .setNoAttributes();

  QgsFeatureIterator features = mLayer->getFeatures( request );

  QgsRectangle bbox;
  bbox.setMinimal();
  QgsFeature feat;
  int featureCount = 0;
  while ( features.nextFeature( feat ) )
  {
    QgsGeometry geom = feat.geometry();
    if ( geom.isNull() || geom.constGet()->isEmpty() )
      continue;

    QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() );
    bbox.combineExtentWith( r );
    featureCount++;
  }
  features.close();

  QgsSettings settings;
  int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
  if ( featureCount > 0 )
  {
    mMapCanvas->zoomToFeatureExtent( bbox );
    if ( mMessageBar )
    {
      mMessageBar->pushMessage( QString(),
                                tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ),
                                Qgis::Info,
                                timeout );
    }
  }
  else if ( mMessageBar )
  {
    mMessageBar->pushMessage( QString(),
                              tr( "No matching features found" ),
                              Qgis::Info,
                              timeout );
  }
  saveRecent();
}
void QgsSelectByFormDialog::zoomToFeatures( const QString &filter )
{
  QgsFeatureIds ids;

  QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( mLayer ) );

  QgsFeatureRequest request = QgsFeatureRequest().setFilterExpression( filter )
                              .setExpressionContext( context )
                              .setSubsetOfAttributes( QgsAttributeList() );

  QgsFeatureIterator features = mLayer->getFeatures( request );

  QgsRectangle bbox;
  bbox.setMinimal();
  QgsFeature feat;
  int featureCount = 0;
  while ( features.nextFeature( feat ) )
  {
    QgsGeometry geom = feat.geometry();
    if ( geom.isNull() || geom.geometry()->isEmpty() )
      continue;

    QgsRectangle r = mMapCanvas->mapSettings().layerExtentToOutputExtent( mLayer, geom.boundingBox() );
    bbox.combineExtentWith( r );
    featureCount++;
  }
  features.close();

  QgsSettings settings;
  int timeout = settings.value( QStringLiteral( "qgis/messageTimeout" ), 5 ).toInt();
  if ( featureCount > 0 )
  {
    mMapCanvas->zoomToFeatureExtent( bbox );
    if ( mMessageBar )
    {
      mMessageBar->pushMessage( QString(),
                                tr( "Zoomed to %n matching feature(s)", "number of matching features", featureCount ),
                                QgsMessageBar::INFO,
                                timeout );
    }
  }
  else if ( mMessageBar )
  {
    mMessageBar->pushMessage( QString(),
                              tr( "No matching features found" ),
                              QgsMessageBar::INFO,
                              timeout );
  }
}
Exemple #6
0
QgsRectangle QgsMeshDataProvider::extent() const
{
  QgsRectangle rec;
  rec.setMinimal();
  for ( int i = 0; i < vertexCount(); ++i )
  {
    QgsMeshVertex v = vertex( i );
    rec.setXMinimum( std::min( rec.xMinimum(), v.x() ) );
    rec.setYMinimum( std::min( rec.yMinimum(), v.y() ) );
    rec.setXMaximum( std::max( rec.xMaximum(), v.x() ) );
    rec.setYMaximum( std::max( rec.yMaximum(), v.y() ) );
  }
  return rec;

}
//! Returns the BBOX of the specified WKB-point stream
inline static QgsRectangle calculateBoundingBox( QGis::WkbType wkbType, QgsConstWkbPtr wkbPtr, int numPoints )
{
  QgsRectangle r;
  r.setMinimal();

  int skipZM = ( QGis::wkbDimensions( wkbType ) - 2 ) * sizeof( double );
  Q_ASSERT( skipZM >= 0 );

  for ( int i = 0; i < numPoints; ++i )
  {
    double x, y;
    wkbPtr >> x >> y;
    wkbPtr += skipZM;
    r.combineExtentWith( x, y );
  }

  return r;
}
//! Returns the BBOX of the specified WKB-point stream
inline static QgsRectangle calculateBoundingBox( QGis::WkbType wkbType, const unsigned char* wkb, size_t numPoints )
{
  double x, y;
  QgsRectangle r;
  r.setMinimal();

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

  for ( size_t i = 0; i < numPoints; ++i )
  {
    memcpy( &x, wkb, sizeof( double ) ); wkb += sizeOfDoubleX;
    memcpy( &y, wkb, sizeof( double ) ); wkb += sizeOfDoubleY;
    r.combineExtentWith( x, y );
  }

  return r;
}
Exemple #9
0
QgsRectangle QgsMapSettings::fullExtent() const
{
  QgsDebugMsg( "called." );
  QgsMapLayerRegistry* registry = QgsMapLayerRegistry::instance();

  // reset the map canvas extent since the extent may now be smaller
  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
  QgsRectangle fullExtent;
  fullExtent.setMinimal();

  // iterate through the map layers and test each layers extent
  // against the current min and max values
  QStringList::const_iterator it = mLayers.begin();
  QgsDebugMsg( QString( "Layer count: %1" ).arg( mLayers.count() ) );
  while ( it != mLayers.end() )
  {
    QgsMapLayer * lyr = registry->mapLayer( *it );
    if ( !lyr )
    {
      QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
    }
    else
    {
      QgsDebugMsg( "Updating extent using " + lyr->name() );
      QgsDebugMsg( "Input extent: " + lyr->extent().toString() );

      if ( lyr->extent().isNull() )
      {
        ++it;
        continue;
      }

      // Layer extents are stored in the coordinate system (CS) of the
      // layer. The extent must be projected to the canvas CS
      QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() );

      QgsDebugMsg( "Output extent: " + extent.toString() );
      fullExtent.unionRect( extent );

    }
    ++it;
  }

  if ( fullExtent.width() == 0.0 || fullExtent.height() == 0.0 )
  {
    // If all of the features are at the one point, buffer the
    // rectangle a bit. If they are all at zero, do something a bit
    // more crude.

    if ( fullExtent.xMinimum() == 0.0 && fullExtent.xMaximum() == 0.0 &&
         fullExtent.yMinimum() == 0.0 && fullExtent.yMaximum() == 0.0 )
    {
      fullExtent.set( -1.0, -1.0, 1.0, 1.0 );
    }
    else
    {
      const double padFactor = 1e-8;
      double widthPad = fullExtent.xMinimum() * padFactor;
      double heightPad = fullExtent.yMinimum() * padFactor;
      double xmin = fullExtent.xMinimum() - widthPad;
      double xmax = fullExtent.xMaximum() + widthPad;
      double ymin = fullExtent.yMinimum() - heightPad;
      double ymax = fullExtent.yMaximum() + heightPad;
      fullExtent.set( xmin, ymin, xmax, ymax );
    }
  }

  QgsDebugMsg( "Full extent: " + fullExtent.toString() );
  return fullExtent;
}
//! Simplify the WKB-geometry using the specified tolerance
bool QgsMapToPixelSimplifier::simplifyWkbGeometry(
  int simplifyFlags,
  SimplifyAlgorithm simplifyAlgorithm,
  QGis::WkbType wkbType,
  QgsConstWkbPtr sourceWkbPtr,
  QgsWkbPtr targetWkbPtr,
  int &targetWkbSize,
  const QgsRectangle &envelope, double map2pixelTol,
  bool writeHeader, bool isaLinearRing )
{
  bool isGeneralizable = true;
  bool result = false;

  // Save initial WKB settings to use when the simplification creates invalid geometries
  QgsConstWkbPtr sourcePrevWkbPtr( sourceWkbPtr );
  QgsWkbPtr targetPrevWkbPtr( targetWkbPtr );
  int targetWkbPrevSize = targetWkbSize;

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

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

  // Write the main header of the geometry
  if ( writeHeader )
  {
    QgsWKBTypes::Type geometryType = sourceWkbPtr.readHeader();

    targetWkbPtr << ( char ) QgsApplication::endian() << QgsWKBTypes::flatType( geometryType );

    targetWkbSize += targetWkbPtr - targetPrevWkbPtr;
  }

  unsigned int flatType = QGis::flatType( wkbType );

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

    int skipZM = ( QGis::wkbDimensions( wkbType ) - 2 ) * sizeof( double );
    Q_ASSERT( skipZM >= 0 );

    int numPoints;
    sourceWkbPtr >> numPoints;

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

    QgsWkbPtr numPtr( targetWkbPtr );

    int numTargetPoints = 0;
    targetWkbPtr << numTargetPoints;
    targetWkbSize += 4;

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

    // Check whether the LinearRing is really closed.
    if ( isaLinearRing )
    {
      QgsConstWkbPtr checkPtr( sourceWkbPtr );

      double x1, y1, x2, y2;

      checkPtr >> x1 >> y1;
      checkPtr += skipZM + ( numPoints - 2 ) * ( 2 * sizeof( double ) + skipZM );
      checkPtr >> x2 >> y2;

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

    // Process each vertex...
    if ( simplifyAlgorithm == 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 )
      {
        sourceWkbPtr >> x >> y;
        sourceWkbPtr += skipZM;

        if ( i == 0 ||
             !isGeneralizable ||
             !equalSnapToGrid( x, y, lastX, lastY, gridOriginX, gridOriginY, gridInverseSizeXY ) ||
             ( !isaLinearRing && ( i == 1 || i >= numPoints - 2 ) ) )
        {
          targetWkbPtr << x << y;
          lastX = x;
          lastY = y;
          numTargetPoints++;
        }

        r.combineExtentWith( x, y );
      }
    }
QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRenderContext& rendererContext )
    : QgsMapLayerRenderer( layer->id() )
    , mRasterViewPort( nullptr )
    , mPipe( nullptr )
{

  mPainter = rendererContext.painter();
  const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
  mMapToPixel = &theQgsMapToPixel;

  QgsMapToPixel mapToPixel = theQgsMapToPixel;
  if ( mapToPixel.mapRotation() )
  {
    // unset rotation for the sake of local computations.
    // Rotation will be handled by QPainter later
    // TODO: provide a method of QgsMapToPixel to fetch map center
    //       in geographical units
    QgsPoint center = mapToPixel.toMapCoordinates(
                        mapToPixel.mapWidth() / 2.0,
                        mapToPixel.mapHeight() / 2.0
                      );
    mapToPixel.setMapRotation( 0, center.x(), center.y() );
  }

  QgsRectangle myProjectedViewExtent;
  QgsRectangle myProjectedLayerExtent;

  if ( rendererContext.coordinateTransform() )
  {
    QgsDebugMsg( "coordinateTransform set -> project extents." );
    try
    {
      myProjectedViewExtent = rendererContext.coordinateTransform()->transformBoundingBox( rendererContext.extent() );
    }
    catch ( QgsCsException &cs )
    {
      QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
      myProjectedViewExtent.setMinimal();
    }

    try
    {
      myProjectedLayerExtent = rendererContext.coordinateTransform()->transformBoundingBox( layer->extent() );
    }
    catch ( QgsCsException &cs )
    {
      QgsMessageLog::logMessage( QObject::tr( "Could not reproject layer extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) );
      myProjectedLayerExtent.setMinimal();
    }
  }
  else
  {
    QgsDebugMsg( "coordinateTransform not set" );
    myProjectedViewExtent = rendererContext.extent();
    myProjectedLayerExtent = layer->extent();
  }

  // clip raster extent to view extent
  QgsRectangle myRasterExtent = myProjectedViewExtent.intersect( &myProjectedLayerExtent );
  if ( myRasterExtent.isEmpty() )
  {
    QgsDebugMsg( "draw request outside view extent." );
    // nothing to do
    return;
  }

  QgsDebugMsg( "theViewExtent is " + rendererContext.extent().toString() );
  QgsDebugMsg( "myProjectedViewExtent is " + myProjectedViewExtent.toString() );
  QgsDebugMsg( "myProjectedLayerExtent is " + myProjectedLayerExtent.toString() );
  QgsDebugMsg( "myRasterExtent is " + myRasterExtent.toString() );

  //
  // The first thing we do is set up the QgsRasterViewPort. This struct stores all the settings
  // relating to the size (in pixels and coordinate system units) of the raster part that is
  // in view in the map window. It also stores the origin.
  //
  //this is not a class level member because every time the user pans or zooms
  //the contents of the rasterViewPort will change
  mRasterViewPort = new QgsRasterViewPort();

  mRasterViewPort->mDrawnExtent = myRasterExtent;
  if ( rendererContext.coordinateTransform() )
  {
    mRasterViewPort->mSrcCRS = layer->crs();
    mRasterViewPort->mDestCRS = rendererContext.coordinateTransform()->destCRS();
    mRasterViewPort->mSrcDatumTransform = rendererContext.coordinateTransform()->sourceDatumTransform();
    mRasterViewPort->mDestDatumTransform = rendererContext.coordinateTransform()->destinationDatumTransform();
  }
  else
  {
    mRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
    mRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
    mRasterViewPort->mSrcDatumTransform = -1;
    mRasterViewPort->mDestDatumTransform = -1;
  }

  // get dimensions of clipped raster image in device coordinate space (this is the size of the viewport)
  mRasterViewPort->mTopLeftPoint = mapToPixel.transform( myRasterExtent.xMinimum(), myRasterExtent.yMaximum() );
  mRasterViewPort->mBottomRightPoint = mapToPixel.transform( myRasterExtent.xMaximum(), myRasterExtent.yMinimum() );

  // align to output device grid, i.e. floor/ceil to integers
  // TODO: this should only be done if paint device is raster - screen, image
  // for other devices (pdf) it can have floating point origin
  // we could use floating point for raster devices as well, but respecting the
  // output device grid should make it more effective as the resampling is done in
  // the provider anyway
  mRasterViewPort->mTopLeftPoint.setX( floor( mRasterViewPort->mTopLeftPoint.x() ) );
  mRasterViewPort->mTopLeftPoint.setY( floor( mRasterViewPort->mTopLeftPoint.y() ) );
  mRasterViewPort->mBottomRightPoint.setX( ceil( mRasterViewPort->mBottomRightPoint.x() ) );
  mRasterViewPort->mBottomRightPoint.setY( ceil( mRasterViewPort->mBottomRightPoint.y() ) );
  // recalc myRasterExtent to aligned values
  myRasterExtent.set(
    mapToPixel.toMapCoordinatesF( mRasterViewPort->mTopLeftPoint.x(),
                                  mRasterViewPort->mBottomRightPoint.y() ),
    mapToPixel.toMapCoordinatesF( mRasterViewPort->mBottomRightPoint.x(),
                                  mRasterViewPort->mTopLeftPoint.y() )
  );

  //raster viewport top left / bottom right are already rounded to int
  mRasterViewPort->mWidth = static_cast<int>( mRasterViewPort->mBottomRightPoint.x() - mRasterViewPort->mTopLeftPoint.x() );
  mRasterViewPort->mHeight = static_cast<int>( mRasterViewPort->mBottomRightPoint.y() - mRasterViewPort->mTopLeftPoint.y() );

  //the drawable area can start to get very very large when you get down displaying 2x2 or smaller, this is becasue
  //mapToPixel.mapUnitsPerPixel() is less then 1,
  //so we will just get the pixel data and then render these special cases differently in paintImageToCanvas()

  QgsDebugMsgLevel( QString( "mapUnitsPerPixel = %1" ).arg( mapToPixel.mapUnitsPerPixel() ), 3 );
  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( layer->width() ), 3 );
  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( layer->height() ), 3 );
  QgsDebugMsgLevel( QString( "myRasterExtent.xMinimum() = %1" ).arg( myRasterExtent.xMinimum() ), 3 );
  QgsDebugMsgLevel( QString( "myRasterExtent.xMaximum() = %1" ).arg( myRasterExtent.xMaximum() ), 3 );
  QgsDebugMsgLevel( QString( "myRasterExtent.yMinimum() = %1" ).arg( myRasterExtent.yMinimum() ), 3 );
  QgsDebugMsgLevel( QString( "myRasterExtent.yMaximum() = %1" ).arg( myRasterExtent.yMaximum() ), 3 );

  QgsDebugMsgLevel( QString( "mTopLeftPoint.x() = %1" ).arg( mRasterViewPort->mTopLeftPoint.x() ), 3 );
  QgsDebugMsgLevel( QString( "mBottomRightPoint.x() = %1" ).arg( mRasterViewPort->mBottomRightPoint.x() ), 3 );
  QgsDebugMsgLevel( QString( "mTopLeftPoint.y() = %1" ).arg( mRasterViewPort->mTopLeftPoint.y() ), 3 );
  QgsDebugMsgLevel( QString( "mBottomRightPoint.y() = %1" ).arg( mRasterViewPort->mBottomRightPoint.y() ), 3 );

  QgsDebugMsgLevel( QString( "mWidth = %1" ).arg( mRasterViewPort->mWidth ), 3 );
  QgsDebugMsgLevel( QString( "mHeight = %1" ).arg( mRasterViewPort->mHeight ), 3 );

  // /\/\/\ - added to handle zoomed-in rasters

  // TODO R->mLastViewPort = *mRasterViewPort;

  // TODO: is it necessary? Probably WMS only?
  layer->dataProvider()->setDpi( rendererContext.rasterScaleFactor() * 25.4 * rendererContext.scaleFactor() );


  // copy the whole raster pipe!
  mPipe = new QgsRasterPipe( *layer->pipe() );
}
Exemple #12
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() );
}
Exemple #13
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,
  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;
}