int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, int *pabSuccess ) { int err, i; /* -------------------------------------------------------------------- */ /* Potentially transform to radians. */ /* -------------------------------------------------------------------- */ if( bSourceLatLong ) { if( bSourceWrap ) { for( i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { if( x[i] < dfSourceWrapLong - 180.0 ) x[i] += 360.0; else if( x[i] > dfSourceWrapLong + 180 ) x[i] -= 360.0; } } } for( i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL ) { x[i] *= dfSourceToRadians; y[i] *= dfSourceToRadians; } } } /* -------------------------------------------------------------------- */ /* Do the transformation using PROJ.4. */ /* -------------------------------------------------------------------- */ if( !bIdentityTransform && pjctx == NULL ) { /* The mutex has already been created */ CPLAssert(hPROJMutex != NULL); CPLAcquireMutex(hPROJMutex, 1000.0); } if( bIdentityTransform ) err = 0; else if (bCheckWithInvertProj) { /* For some projections, we cannot detect if we are trying to reproject */ /* coordinates outside the validity area of the projection. So let's do */ /* the reverse reprojection and compare with the source coordinates */ if (nCount > nMaxCount) { nMaxCount = nCount; padfOriX = (double*) CPLRealloc(padfOriX, sizeof(double)*nCount); padfOriY = (double*) CPLRealloc(padfOriY, sizeof(double)*nCount); padfOriZ = (double*) CPLRealloc(padfOriZ, sizeof(double)*nCount); padfTargetX = (double*) CPLRealloc(padfTargetX, sizeof(double)*nCount); padfTargetY = (double*) CPLRealloc(padfTargetY, sizeof(double)*nCount); padfTargetZ = (double*) CPLRealloc(padfTargetZ, sizeof(double)*nCount); } memcpy(padfOriX, x, sizeof(double)*nCount); memcpy(padfOriY, y, sizeof(double)*nCount); if (z) { memcpy(padfOriZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); if (err == 0) { memcpy(padfTargetX, x, sizeof(double)*nCount); memcpy(padfTargetY, y, sizeof(double)*nCount); if (z) { memcpy(padfTargetZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJTarget, psPJSource , nCount, 1, padfTargetX, padfTargetY, (z) ? padfTargetZ : NULL); if (err == 0) { for( i = 0; i < nCount; i++ ) { if ( x[i] != HUGE_VAL && y[i] != HUGE_VAL && (fabs(padfTargetX[i] - padfOriX[i]) > dfThreshold || fabs(padfTargetY[i] - padfOriY[i]) > dfThreshold) ) { x[i] = HUGE_VAL; y[i] = HUGE_VAL; } } } } } else { err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); } /* -------------------------------------------------------------------- */ /* Try to report an error through CPL. Get proj.4 error string */ /* if possible. Try to avoid reporting thousands of error */ /* ... supress further error reporting on this OGRProj4CT if we */ /* have already reported 20 errors. */ /* -------------------------------------------------------------------- */ if( err != 0 ) { if( pabSuccess ) memset( pabSuccess, 0, sizeof(int) * nCount ); if( ++nErrorCount < 20 ) { if (pjctx != NULL) /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ CPLAcquireMutex(hPROJMutex, 1000.0); const char *pszError = NULL; if( pfn_pj_strerrno != NULL ) pszError = pfn_pj_strerrno( err ); if( pszError == NULL ) CPLError( CE_Failure, CPLE_AppDefined, "Reprojection failed, err = %d", err ); else CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); if (pjctx != NULL) /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ CPLReleaseMutex(hPROJMutex); } else if( nErrorCount == 20 ) { CPLError( CE_Failure, CPLE_AppDefined, "Reprojection failed, err = %d, further errors will be supressed on the transform object.", err ); } if (pjctx == NULL) CPLReleaseMutex(hPROJMutex); return FALSE; } if( !bIdentityTransform && pjctx == NULL ) CPLReleaseMutex(hPROJMutex); /* -------------------------------------------------------------------- */ /* Potentially transform back to degrees. */ /* -------------------------------------------------------------------- */ if( bTargetLatLong ) { for( i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { x[i] *= dfTargetFromRadians; y[i] *= dfTargetFromRadians; } } if( bTargetWrap ) { for( i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { if( x[i] < dfTargetWrapLong - 180.0 ) x[i] += 360.0; else if( x[i] > dfTargetWrapLong + 180 ) x[i] -= 360.0; } } } } /* -------------------------------------------------------------------- */ /* Establish error information if pabSuccess provided. */ /* -------------------------------------------------------------------- */ if( pabSuccess ) { for( i = 0; i < nCount; i++ ) { if( x[i] == HUGE_VAL || y[i] == HUGE_VAL ) pabSuccess[i] = FALSE; else pabSuccess[i] = TRUE; } } return TRUE; }
/** Transform an array of points * * @param nCount Number of points * @param x Array of nCount x values. * @param y Array of nCount y values. * @param z Array of nCount z values. * @param pabSuccess Output array of nCount value that will be set to TRUE/FALSE * @return TRUE or FALSE */ int OGRProj4CT::TransformEx( int nCount, double *x, double *y, double *z, int *pabSuccess ) { int err; /* -------------------------------------------------------------------- */ /* Potentially transform to radians. */ /* -------------------------------------------------------------------- */ if( bSourceLatLong ) { if( bSourceWrap ) { for( int i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { if( x[i] < dfSourceWrapLong - 180.0 ) x[i] += 360.0; else if( x[i] > dfSourceWrapLong + 180 ) x[i] -= 360.0; } } } for( int i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL ) { x[i] *= dfSourceToRadians; y[i] *= dfSourceToRadians; } } } /* -------------------------------------------------------------------- */ /* Optimized transform from WebMercator to WGS84 */ /* -------------------------------------------------------------------- */ bool bTransformDone = false; if( bWebMercatorToWGS84 ) { #define REVERSE_SPHERE_RADIUS (1. / 6378137.) double y0 = y[0]; for( int i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL ) { x[i] = x[i] * REVERSE_SPHERE_RADIUS; if( x[i] > M_PI ) { if( x[i] < M_PI+1e-14 ) x[i] = M_PI; else if( bCheckWithInvertProj ) { x[i] = y[i] = HUGE_VAL; y0 = HUGE_VAL; continue; } else { do { x[i] -= 2 * M_PI; } while ( x[i] > M_PI ); } } else if( x[i] < -M_PI ) { if( x[i] > -M_PI-1e-14 ) x[i] = -M_PI; else if( bCheckWithInvertProj ) { x[i] = y[i] = HUGE_VAL; y0 = HUGE_VAL; continue; } else { do { x[i] += 2 * M_PI; } while( x[i] < -M_PI ); } } // Optimization for the case where we are provided a whole line of same northing if( i > 0 && y[i] == y0 ) y[i] = y[0]; else y[i] = M_PI / 2 - 2. * atan(exp(-y[i] * REVERSE_SPHERE_RADIUS)); } } bTransformDone = true; } else if( bIdentityTransform ) { bTransformDone = true; } /* -------------------------------------------------------------------- */ /* Do the transformation (or not...) using PROJ.4. */ /* -------------------------------------------------------------------- */ if( !bTransformDone && pjctx == NULL ) { /* The mutex has already been created */ CPLAssert(hPROJMutex != NULL); CPLAcquireMutex(hPROJMutex, 1000.0); } if( bTransformDone ) err = 0; else if( bCheckWithInvertProj ) { // For some projections, we cannot detect if we are trying to reproject // coordinates outside the validity area of the projection. So let's do // the reverse reprojection and compare with the source coordinates. if (nCount > nMaxCount) { nMaxCount = nCount; padfOriX = (double*) CPLRealloc(padfOriX, sizeof(double)*nCount); padfOriY = (double*) CPLRealloc(padfOriY, sizeof(double)*nCount); padfOriZ = (double*) CPLRealloc(padfOriZ, sizeof(double)*nCount); padfTargetX = (double*) CPLRealloc(padfTargetX, sizeof(double)*nCount); padfTargetY = (double*) CPLRealloc(padfTargetY, sizeof(double)*nCount); padfTargetZ = (double*) CPLRealloc(padfTargetZ, sizeof(double)*nCount); } memcpy(padfOriX, x, sizeof(double)*nCount); memcpy(padfOriY, y, sizeof(double)*nCount); if (z) { memcpy(padfOriZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); if (err == 0) { memcpy(padfTargetX, x, sizeof(double)*nCount); memcpy(padfTargetY, y, sizeof(double)*nCount); if (z) { memcpy(padfTargetZ, z, sizeof(double)*nCount); } err = pfn_pj_transform( psPJTarget, psPJSource , nCount, 1, padfTargetX, padfTargetY, (z) ? padfTargetZ : NULL); if (err == 0) { for( int i = 0; i < nCount; i++ ) { if ( x[i] != HUGE_VAL && y[i] != HUGE_VAL && (fabs(padfTargetX[i] - padfOriX[i]) > dfThreshold || fabs(padfTargetY[i] - padfOriY[i]) > dfThreshold) ) { x[i] = HUGE_VAL; y[i] = HUGE_VAL; } } } } } else { err = pfn_pj_transform( psPJSource, psPJTarget, nCount, 1, x, y, z ); } /* -------------------------------------------------------------------- */ /* Try to report an error through CPL. Get proj.4 error string */ /* if possible. Try to avoid reporting thousands of error */ /* ... suppress further error reporting on this OGRProj4CT if we */ /* have already reported 20 errors. */ /* -------------------------------------------------------------------- */ if( err != 0 ) { if( pabSuccess ) memset( pabSuccess, 0, sizeof(int) * nCount ); if( ++nErrorCount < 20 ) { if (pjctx != NULL) /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ CPLAcquireMutex(hPROJMutex, 1000.0); const char *pszError = NULL; if( pfn_pj_strerrno != NULL ) pszError = pfn_pj_strerrno( err ); if( pszError == NULL ) CPLError( CE_Failure, CPLE_AppDefined, "Reprojection failed, err = %d", err ); else CPLError( CE_Failure, CPLE_AppDefined, "%s", pszError ); if (pjctx != NULL) /* pfn_pj_strerrno not yet thread-safe in PROJ 4.8.0 */ CPLReleaseMutex(hPROJMutex); } else if( nErrorCount == 20 ) { CPLError( CE_Failure, CPLE_AppDefined, "Reprojection failed, err = %d, further errors will be suppressed on the transform object.", err ); } if (pjctx == NULL) CPLReleaseMutex(hPROJMutex); return FALSE; } if( !bTransformDone && pjctx == NULL ) CPLReleaseMutex(hPROJMutex); /* -------------------------------------------------------------------- */ /* Potentially transform back to degrees. */ /* -------------------------------------------------------------------- */ if( bTargetLatLong ) { for( int i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { x[i] *= dfTargetFromRadians; y[i] *= dfTargetFromRadians; } } if( bTargetWrap ) { for( int i = 0; i < nCount; i++ ) { if( x[i] != HUGE_VAL && y[i] != HUGE_VAL ) { if( x[i] < dfTargetWrapLong - 180.0 ) x[i] += 360.0; else if( x[i] > dfTargetWrapLong + 180 ) x[i] -= 360.0; } } } } /* -------------------------------------------------------------------- */ /* Establish error information if pabSuccess provided. */ /* -------------------------------------------------------------------- */ if( pabSuccess ) { for( int i = 0; i < nCount; i++ ) { if( x[i] == HUGE_VAL || y[i] == HUGE_VAL ) pabSuccess[i] = FALSE; else pabSuccess[i] = TRUE; } } return TRUE; }