Example #1
0
void QgsCoordinateTransform::transformCoords( const int& numPoints, double *x, double *y, double *z, TransformDirection direction ) const
{
    // Refuse to transform the points if the srs's are invalid
    if ( !mSourceCRS.isValid() )
    {
        QgsMessageLog::logMessage( tr( "The source spatial reference system (CRS) is not valid. "
                                       "The coordinates can not be reprojected. The CRS is: %1" )
                                   .arg( mSourceCRS.toProj4() ), tr( "CRS" ) );
        return;
    }
    if ( !mDestCRS.isValid() )
    {
        QgsMessageLog::logMessage( tr( "The destination spatial reference system (CRS) is not valid. "
                                       "The coordinates can not be reprojected. The CRS is: %1" ).arg( mDestCRS.toProj4() ), tr( "CRS" ) );
        return;
    }

#ifdef COORDINATE_TRANSFORM_VERBOSE
    double xorg = *x;
    double yorg = *y;
    QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
#endif

    // use proj4 to do the transform
    QString dir;
    // if the source/destination projection is lat/long, convert the points to radians
    // prior to transforming
    if (( pj_is_latlong( mDestinationProjection ) && ( direction == ReverseTransform ) )
            || ( pj_is_latlong( mSourceProjection ) && ( direction == ForwardTransform ) ) )
    {
        for ( int i = 0; i < numPoints; ++i )
        {
            x[i] *= DEG_TO_RAD;
            y[i] *= DEG_TO_RAD;
            z[i] *= DEG_TO_RAD;
        }

    }
    int projResult;
    if ( direction == ReverseTransform )
    {
        projResult = pj_transform( mDestinationProjection, mSourceProjection, numPoints, 0, x, y, z );
    }
    else
    {
        Q_ASSERT( mSourceProjection != 0 );
        Q_ASSERT( mDestinationProjection != 0 );
        projResult = pj_transform( mSourceProjection, mDestinationProjection, numPoints, 0, x, y, z );
    }

    if ( projResult != 0 )
    {
        //something bad happened....
        QString points;

        for ( int i = 0; i < numPoints; ++i )
        {
            if ( direction == ForwardTransform )
            {
                points += QString( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
            }
            else
            {
                points += QString( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
            }
        }

        dir = ( direction == ForwardTransform ) ? tr( "forward transform" ) : tr( "inverse transform" );

        QString msg = tr( "%1 of\n"
                          "%2"
                          "PROJ.4: %3 +to %4\n"
                          "Error: %5" )
                      .arg( dir )
                      .arg( points )
                      .arg( mSourceCRS.toProj4() ).arg( mDestCRS.toProj4() )
                      .arg( QString::fromUtf8( pj_strerrno( projResult ) ) );

        QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );

        emit invalidTransformInput();

        QgsDebugMsg( "throwing exception" );

        throw QgsCsException( msg );
    }

    // if the result is lat/long, convert the results from radians back
    // to degrees
    if (( pj_is_latlong( mDestinationProjection ) && ( direction == ForwardTransform ) )
            || ( pj_is_latlong( mSourceProjection ) && ( direction == ReverseTransform ) ) )
    {
        for ( int i = 0; i < numPoints; ++i )
        {
            x[i] *= RAD_TO_DEG;
            y[i] *= RAD_TO_DEG;
            z[i] *= RAD_TO_DEG;
        }
    }
#ifdef COORDINATE_TRANSFORM_VERBOSE
    QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
                 .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
                 .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
#endif
}
QgsRectangle QgsCoordinateTransform::transformBoundingBox( const QgsRectangle &rect, TransformDirection direction, const bool handle180Crossover ) const
{
  // Calculate the bounding box of a QgsRectangle in the source CRS
  // when projected to the destination CRS (or the inverse).
  // This is done by looking at a number of points spread evenly
  // across the rectangle

  if ( !d->mIsValid || d->mShortCircuit )
    return rect;

  if ( rect.isEmpty() )
  {
    QgsPointXY p = transform( rect.xMinimum(), rect.yMinimum(), direction );
    return QgsRectangle( p, p );
  }

  // 64 points (<=2.12) is not enough, see #13665, for EPSG:4326 -> EPSG:3574 (say that it is a hard one),
  // are decent result from about 500 points and more. This method is called quite often, but
  // even with 1000 points it takes < 1ms
  // TODO: how to effectively and precisely reproject bounding box?
  const int nPoints = 1000;
  double d = std::sqrt( ( rect.width() * rect.height() ) / std::pow( std::sqrt( static_cast< double >( nPoints ) ) - 1, 2.0 ) );
  int nXPoints = static_cast< int >( std::ceil( rect.width() / d ) ) + 1;
  int nYPoints = static_cast< int >( std::ceil( rect.height() / d ) ) + 1;

  QgsRectangle bb_rect;
  bb_rect.setMinimal();

  // We're interfacing with C-style vectors in the
  // end, so let's do C-style vectors here too.

  QVector<double> x( nXPoints * nYPoints );
  QVector<double> y( nXPoints * nYPoints );
  QVector<double> z( nXPoints * nYPoints );

  QgsDebugMsgLevel( QStringLiteral( "Entering transformBoundingBox..." ), 4 );

  // Populate the vectors

  double dx = rect.width()  / static_cast< double >( nXPoints - 1 );
  double dy = rect.height() / static_cast< double >( nYPoints - 1 );

  double pointY = rect.yMinimum();

  for ( int i = 0; i < nYPoints ; i++ )
  {

    // Start at right edge
    double pointX = rect.xMinimum();

    for ( int j = 0; j < nXPoints; j++ )
    {
      x[( i * nXPoints ) + j] = pointX;
      y[( i * nXPoints ) + j] = pointY;
      // and the height...
      z[( i * nXPoints ) + j] = 0.0;
      // QgsDebugMsg(QString("BBox coord: (%1, %2)").arg(x[(i*numP) + j]).arg(y[(i*numP) + j]));
      pointX += dx;
    }
    pointY += dy;
  }

  // Do transformation. Any exception generated must
  // be handled in above layers.
  try
  {
    transformCoords( nXPoints * nYPoints, x.data(), y.data(), z.data(), direction );
  }
  catch ( const QgsCsException & )
  {
    // rethrow the exception
    QgsDebugMsg( QStringLiteral( "rethrowing exception" ) );
    throw;
  }

  // Calculate the bounding box and use that for the extent

  for ( int i = 0; i < nXPoints * nYPoints; i++ )
  {
    if ( !std::isfinite( x[i] ) || !std::isfinite( y[i] ) )
    {
      continue;
    }

    if ( handle180Crossover )
    {
      //if crossing the date line, temporarily add 360 degrees to -ve longitudes
      bb_rect.combineExtentWith( x[i] >= 0.0 ? x[i] : x[i] + 360.0, y[i] );
    }
    else
    {
      bb_rect.combineExtentWith( x[i], y[i] );
    }
  }

  if ( bb_rect.isNull() )
  {
    // something bad happened when reprojecting the filter rect... no finite points were left!
    throw QgsCsException( QObject::tr( "Could not transform bounding box to target CRS" ) );
  }

  if ( handle180Crossover )
  {
    //subtract temporary addition of 360 degrees from longitudes
    if ( bb_rect.xMinimum() > 180.0 )
      bb_rect.setXMinimum( bb_rect.xMinimum() - 360.0 );
    if ( bb_rect.xMaximum() > 180.0 )
      bb_rect.setXMaximum( bb_rect.xMaximum() - 360.0 );
  }

  QgsDebugMsgLevel( "Projected extent: " + bb_rect.toString(), 4 );

  if ( bb_rect.isEmpty() )
  {
    QgsDebugMsgLevel( "Original extent: " + rect.toString(), 4 );
  }

  return bb_rect;
}
void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
{
  if ( !d->mIsValid || d->mShortCircuit )
    return;
  // Refuse to transform the points if the srs's are invalid
  if ( !d->mSourceCRS.isValid() )
  {
    QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
                                            "The coordinates can not be reprojected. The CRS is: %1" )
                               .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
    return;
  }
  if ( !d->mDestCRS.isValid() )
  {
    QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
                                            "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
    return;
  }

#ifdef COORDINATE_TRANSFORM_VERBOSE
  double xorg = *x;
  double yorg = *y;
  QgsDebugMsg( QStringLiteral( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
#endif

#ifdef QGISDEBUG
  if ( !mHasContext )
    QgsDebugMsgLevel( QStringLiteral( "No QgsCoordinateTransformContext context set for transform" ), 4 );
#endif

  // use proj4 to do the transform

  // if the source/destination projection is lat/long, convert the points to radians
  // prior to transforming
  ProjData projData = d->threadLocalProjData();
#if PROJ_VERSION_MAJOR<6
  bool sourceIsLatLong = false;
  bool destIsLatLong = false;

  projPJ sourceProj = projData.first;
  projPJ destProj = projData.second;
  sourceIsLatLong = pj_is_latlong( sourceProj );
  destIsLatLong = pj_is_latlong( destProj );

  if ( ( destIsLatLong && ( direction == ReverseTransform ) )
       || ( sourceIsLatLong && ( direction == ForwardTransform ) ) )
  {
    for ( int i = 0; i < numPoints; ++i )
    {
      x[i] *= DEG_TO_RAD;
      y[i] *= DEG_TO_RAD;
    }
  }
#endif

  int projResult = 0;
#if PROJ_VERSION_MAJOR>=6
  const bool sourceAxisOrderSwapped =  direction == ForwardTransform ? d->mSourceAxisOrderSwapped : d->mDestAxisOrderSwapped;

  proj_trans_generic( projData, direction == ForwardTransform ? PJ_FWD : PJ_INV,
                      !sourceAxisOrderSwapped ? x : y, sizeof( double ), numPoints,
                      !sourceAxisOrderSwapped ? y : x, sizeof( double ), numPoints,
                      z, sizeof( double ), numPoints,
                      nullptr, sizeof( double ), 0 );
  projResult = proj_errno( projData );
  // ewww - this logic is gross. We should drop support for PROJ 6.0 as quickly as possible and dump this code (in favour of built in methods used for >=6.1 builds)
  if ( projResult == 0 && ( d->mSourceAxisOrderSwapped != d->mDestAxisOrderSwapped ) )
  {
    size_t size = sizeof( double ) * numPoints;
    void *tmp = malloc( size );
    memcpy( tmp, x, size );
    memcpy( x, y, size );
    memcpy( y, tmp, size );
    free( tmp );
  }
#else
  if ( direction == ReverseTransform )
  {
    projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
  }
  else
  {
    Q_ASSERT( sourceProj );
    Q_ASSERT( destProj );
    projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
  }
#endif

  if ( projResult != 0 )
  {
    //something bad happened....
    QString points;

    for ( int i = 0; i < numPoints; ++i )
    {
      if ( direction == ForwardTransform )
      {
        points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
      }
      else
      {
#if PROJ_VERSION_MAJOR>=6
        points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
#else
        points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
#endif
      }
    }

    QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );

#if PROJ_VERSION_MAJOR>=6
    QgsProjUtils::proj_pj_unique_ptr src( proj_get_source_crs( QgsProjContext::get(), projData ) );
    QgsProjUtils::proj_pj_unique_ptr dest( proj_get_source_crs( QgsProjContext::get(), projData ) );
    QString msg = QObject::tr( "%1 of\n"
                               "%2"
                               "PROJ: %3\n"
                               "Error: %4" )
                  .arg( dir,
                        points,
                        proj_as_proj_string( QgsProjContext::get(), projData, PJ_PROJ_5, nullptr ),
                        QString::fromUtf8( proj_errno_string( projResult ) ) );
#else
    char *srcdef = pj_get_def( sourceProj, 0 );
    char *dstdef = pj_get_def( destProj, 0 );

    QString msg = QObject::tr( "%1 of\n"
                               "%2"
                               "PROJ: %3 +to %4\n"
                               "Error: %5" )
                  .arg( dir,
                        points,
                        srcdef, dstdef,
                        QString::fromUtf8( pj_strerrno( projResult ) ) );

    pj_dalloc( srcdef );
    pj_dalloc( dstdef );
#endif

    QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
    QgsDebugMsg( QStringLiteral( "throwing exception" ) );

    throw QgsCsException( msg );
  }

#if PROJ_VERSION_MAJOR<6
  // if the result is lat/long, convert the results from radians back
  // to degrees
  if ( ( destIsLatLong && ( direction == ForwardTransform ) )
       || ( sourceIsLatLong && ( direction == ReverseTransform ) ) )
  {
    for ( int i = 0; i < numPoints; ++i )
    {
      x[i] *= RAD_TO_DEG;
      y[i] *= RAD_TO_DEG;
    }
  }
#endif
#ifdef COORDINATE_TRANSFORM_VERBOSE
  QgsDebugMsg( QStringLiteral( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
               .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
               .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
#endif
}
Example #4
0
void QgsCoordinateTransform::transformCoords( int numPoints, double *x, double *y, double *z, TransformDirection direction ) const
{
  if ( !d->mIsValid || d->mShortCircuit )
    return;
  // Refuse to transform the points if the srs's are invalid
  if ( !d->mSourceCRS.isValid() )
  {
    QgsMessageLog::logMessage( QObject::tr( "The source spatial reference system (CRS) is not valid. "
                                            "The coordinates can not be reprojected. The CRS is: %1" )
                               .arg( d->mSourceCRS.toProj4() ), QObject::tr( "CRS" ) );
    return;
  }
  if ( !d->mDestCRS.isValid() )
  {
    QgsMessageLog::logMessage( QObject::tr( "The destination spatial reference system (CRS) is not valid. "
                                            "The coordinates can not be reprojected. The CRS is: %1" ).arg( d->mDestCRS.toProj4() ), QObject::tr( "CRS" ) );
    return;
  }

#ifdef COORDINATE_TRANSFORM_VERBOSE
  double xorg = *x;
  double yorg = *y;
  QgsDebugMsg( QString( "[[[[[[ Number of points to transform: %1 ]]]]]]" ).arg( numPoints ) );
#endif

  // use proj4 to do the transform

  // if the source/destination projection is lat/long, convert the points to radians
  // prior to transforming
  QPair<projPJ, projPJ> projData = d->threadLocalProjData();
  projPJ sourceProj = projData.first;
  projPJ destProj = projData.second;

  if ( ( pj_is_latlong( destProj ) && ( direction == ReverseTransform ) )
       || ( pj_is_latlong( sourceProj ) && ( direction == ForwardTransform ) ) )
  {
    for ( int i = 0; i < numPoints; ++i )
    {
      x[i] *= DEG_TO_RAD;
      y[i] *= DEG_TO_RAD;
    }

  }
  int projResult;
  if ( direction == ReverseTransform )
  {
    projResult = pj_transform( destProj, sourceProj, numPoints, 0, x, y, z );
  }
  else
  {
    Q_ASSERT( sourceProj );
    Q_ASSERT( destProj );
    projResult = pj_transform( sourceProj, destProj, numPoints, 0, x, y, z );
  }

  if ( projResult != 0 )
  {
    //something bad happened....
    QString points;

    for ( int i = 0; i < numPoints; ++i )
    {
      if ( direction == ForwardTransform )
      {
        points += QStringLiteral( "(%1, %2)\n" ).arg( x[i], 0, 'f' ).arg( y[i], 0, 'f' );
      }
      else
      {
        points += QStringLiteral( "(%1, %2)\n" ).arg( x[i] * RAD_TO_DEG, 0, 'f' ).arg( y[i] * RAD_TO_DEG, 0, 'f' );
      }
    }

    QString dir = ( direction == ForwardTransform ) ? QObject::tr( "forward transform" ) : QObject::tr( "inverse transform" );

    char *srcdef = pj_get_def( sourceProj, 0 );
    char *dstdef = pj_get_def( destProj, 0 );

    QString msg = QObject::tr( "%1 of\n"
                               "%2"
                               "PROJ.4: %3 +to %4\n"
                               "Error: %5" )
                  .arg( dir,
                        points,
                        srcdef, dstdef,
                        QString::fromUtf8( pj_strerrno( projResult ) ) );

    pj_dalloc( srcdef );
    pj_dalloc( dstdef );

    QgsDebugMsg( "Projection failed emitting invalid transform signal: " + msg );
    QgsDebugMsg( "throwing exception" );

    throw QgsCsException( msg );
  }

  // if the result is lat/long, convert the results from radians back
  // to degrees
  if ( ( pj_is_latlong( destProj ) && ( direction == ForwardTransform ) )
       || ( pj_is_latlong( sourceProj ) && ( direction == ReverseTransform ) ) )
  {
    for ( int i = 0; i < numPoints; ++i )
    {
      x[i] *= RAD_TO_DEG;
      y[i] *= RAD_TO_DEG;
    }
  }
#ifdef COORDINATE_TRANSFORM_VERBOSE
  QgsDebugMsg( QString( "[[[[[[ Projected %1, %2 to %3, %4 ]]]]]]" )
               .arg( xorg, 0, 'g', 15 ).arg( yorg, 0, 'g', 15 )
               .arg( *x, 0, 'g', 15 ).arg( *y, 0, 'g', 15 ) );
#endif
}