mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
2000 lines
70 KiB
C++
2000 lines
70 KiB
C++
/******************************************************************************
|
|
* $Id: gdalgrid.cpp 28459 2015-02-12 13:48:21Z rouault $
|
|
*
|
|
* Project: GDAL Gridding API.
|
|
* Purpose: Implementation of GDAL scattered data gridder.
|
|
* Author: Andrey Kiselev, dron@ak4719.spb.edu
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2007, Andrey Kiselev <dron@ak4719.spb.edu>
|
|
* Copyright (c) 2009-2013, Even Rouault <even dot rouault at mines-paris dot org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included
|
|
* in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
****************************************************************************/
|
|
|
|
#include "cpl_vsi.h"
|
|
#include "cpl_string.h"
|
|
#include "gdalgrid.h"
|
|
#include <float.h>
|
|
#include <limits.h>
|
|
#include "cpl_multiproc.h"
|
|
#include "gdalgrid_priv.h"
|
|
|
|
CPL_CVSID("$Id: gdalgrid.cpp 28459 2015-02-12 13:48:21Z rouault $");
|
|
|
|
#define TO_RADIANS (3.14159265358979323846 / 180.0)
|
|
|
|
#ifndef DBL_MAX
|
|
# ifdef __DBL_MAX__
|
|
# define DBL_MAX __DBL_MAX__
|
|
# else
|
|
# define DBL_MAX 1.7976931348623157E+308
|
|
# endif /* __DBL_MAX__ */
|
|
#endif /* DBL_MAX */
|
|
|
|
/************************************************************************/
|
|
/* GDALGridGetPointBounds() */
|
|
/************************************************************************/
|
|
|
|
static void GDALGridGetPointBounds(const void* hFeature, CPLRectObj* pBounds)
|
|
{
|
|
GDALGridPoint* psPoint = (GDALGridPoint*) hFeature;
|
|
GDALGridXYArrays* psXYArrays = psPoint->psXYArrays;
|
|
int i = psPoint->i;
|
|
double dfX = psXYArrays->padfX[i];
|
|
double dfY = psXYArrays->padfY[i];
|
|
pBounds->minx = dfX;
|
|
pBounds->miny = dfY;
|
|
pBounds->maxx = dfX;
|
|
pBounds->maxy = dfY;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALGridInverseDistanceToAPower() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Inverse distance to a power.
|
|
*
|
|
* The Inverse Distance to a Power gridding method is a weighted average
|
|
* interpolator. You should supply the input arrays with the scattered data
|
|
* values including coordinates of every data point and output grid geometry.
|
|
* The function will compute interpolated value for the given position in
|
|
* output grid.
|
|
*
|
|
* For every grid node the resulting value \f$Z\f$ will be calculated using
|
|
* formula:
|
|
*
|
|
* \f[
|
|
* Z=\frac{\sum_{i=1}^n{\frac{Z_i}{r_i^p}}}{\sum_{i=1}^n{\frac{1}{r_i^p}}}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z_i\f$ is a known value at point \f$i\f$,
|
|
* <li> \f$r_i\f$ is an Euclidean distance from the grid node
|
|
* to point \f$i\f$,
|
|
* <li> \f$p\f$ is a weighting power,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* In this method the weighting factor \f$w\f$ is
|
|
*
|
|
* \f[
|
|
* w=\frac{1}{r^p}
|
|
* \f]
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridInverseDistanceToAPowerOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
* @param hExtraParamsIn extra parameters.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridInverseDistanceToAPower( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint,
|
|
double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle = TO_RADIANS
|
|
* ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
const double dfPowerDiv2 =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower / 2;
|
|
const double dfSmoothing =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing;
|
|
const GUInt32 nMaxPoints =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->nMaxPoints;
|
|
double dfNominator = 0.0, dfDenominator = 0.0;
|
|
GUInt32 i, n = 0;
|
|
|
|
for ( i = 0; i < nPoints; i++ )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
const double dfR2 =
|
|
dfRX * dfRX + dfRY * dfRY + dfSmoothing * dfSmoothing;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
// If the test point is close to the grid node, use the point
|
|
// value directly as a node value to avoid singularity.
|
|
if ( dfR2 < 0.0000000000001 )
|
|
{
|
|
(*pdfValue) = padfZ[i];
|
|
return CE_None;
|
|
}
|
|
else
|
|
{
|
|
const double dfW = pow( dfR2, dfPowerDiv2 );
|
|
double dfInvW = 1.0 / dfW;
|
|
dfNominator += dfInvW * padfZ[i];
|
|
dfDenominator += dfInvW;
|
|
n++;
|
|
if ( nMaxPoints > 0 && n > nMaxPoints )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( n < ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->nMinPoints
|
|
|| dfDenominator == 0.0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfNominator / dfDenominator;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridInverseDistanceToAPowerNoSearch() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Inverse distance to a power for whole data set.
|
|
*
|
|
* This is somewhat optimized version of the Inverse Distance to a Power
|
|
* method. It is used when the search ellips is not set. The algorithm and
|
|
* parameters are the same as in GDALGridInverseDistanceToAPower(), but this
|
|
* implementation works faster, because of no search.
|
|
*
|
|
* @see GDALGridInverseDistanceToAPower()
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridInverseDistanceToAPowerNoSearch( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint,
|
|
double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
const double dfPowerDiv2 =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower / 2;
|
|
const double dfSmoothing =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing;
|
|
const double dfSmoothing2 = dfSmoothing * dfSmoothing;
|
|
double dfNominator = 0.0, dfDenominator = 0.0;
|
|
GUInt32 i = 0;
|
|
int bPower2 = (dfPowerDiv2 == 1.0);
|
|
|
|
if( bPower2 )
|
|
{
|
|
if( dfSmoothing2 > 0 )
|
|
{
|
|
for ( i = 0; i < nPoints; i++ )
|
|
{
|
|
const double dfRX = padfX[i] - dfXPoint;
|
|
const double dfRY = padfY[i] - dfYPoint;
|
|
const double dfR2 =
|
|
dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
|
|
|
|
double dfInvR2 = 1.0 / dfR2;
|
|
dfNominator += dfInvR2 * padfZ[i];
|
|
dfDenominator += dfInvR2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < nPoints; i++ )
|
|
{
|
|
const double dfRX = padfX[i] - dfXPoint;
|
|
const double dfRY = padfY[i] - dfYPoint;
|
|
const double dfR2 =
|
|
dfRX * dfRX + dfRY * dfRY;
|
|
|
|
// If the test point is close to the grid node, use the point
|
|
// value directly as a node value to avoid singularity.
|
|
if ( dfR2 < 0.0000000000001 )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
double dfInvR2 = 1.0 / dfR2;
|
|
dfNominator += dfInvR2 * padfZ[i];
|
|
dfDenominator += dfInvR2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < nPoints; i++ )
|
|
{
|
|
const double dfRX = padfX[i] - dfXPoint;
|
|
const double dfRY = padfY[i] - dfYPoint;
|
|
const double dfR2 =
|
|
dfRX * dfRX + dfRY * dfRY + dfSmoothing2;
|
|
|
|
// If the test point is close to the grid node, use the point
|
|
// value directly as a node value to avoid singularity.
|
|
if ( dfR2 < 0.0000000000001 )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
const double dfW = pow( dfR2, dfPowerDiv2 );
|
|
double dfInvW = 1.0 / dfW;
|
|
dfNominator += dfInvW * padfZ[i];
|
|
dfDenominator += dfInvW;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( i != nPoints )
|
|
{
|
|
(*pdfValue) = padfZ[i];
|
|
}
|
|
else
|
|
if ( dfDenominator == 0.0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridInverseDistanceToAPowerOptions*)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfNominator / dfDenominator;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridMovingAverage() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Moving average.
|
|
*
|
|
* The Moving Average is a simple data averaging algorithm. It uses a moving
|
|
* window of elliptic form to search values and averages all data points
|
|
* within the window. Search ellipse can be rotated by specified angle, the
|
|
* center of ellipse located at the grid node. Also the minimum number of data
|
|
* points to average can be set, if there are not enough points in window, the
|
|
* grid node considered empty and will be filled with specified NODATA value.
|
|
*
|
|
* Mathematically it can be expressed with the formula:
|
|
*
|
|
* \f[
|
|
* Z=\frac{\sum_{i=1}^n{Z_i}}{n}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$Z_i\f$ is a known value at point \f$i\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridMovingAverageOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridMovingAverage( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 = ((GDALGridMovingAverageOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 = ((GDALGridMovingAverageOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridMovingAverageOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfAccumulator = 0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
dfAccumulator += padfZ[i];
|
|
n++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridMovingAverageOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridMovingAverageOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfAccumulator / n;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridNearestNeighbor() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Nearest neighbor.
|
|
*
|
|
* The Nearest Neighbor method doesn't perform any interpolation or smoothing,
|
|
* it just takes the value of nearest point found in grid node search ellipse
|
|
* and returns it as a result. If there are no points found, the specified
|
|
* NODATA value will be returned.
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridNearestNeighborOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridNearestNeighbor( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
void* hExtraParamsIn)
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridNearestNeighborOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
GDALGridExtraParameters* psExtraParams = (GDALGridExtraParameters*) hExtraParamsIn;
|
|
CPLQuadTree* hQuadTree = psExtraParams->hQuadTree;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridNearestNeighborOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
// If the nearest point will not be found, its value remains as NODATA.
|
|
double dfNearestValue =
|
|
((GDALGridNearestNeighborOptions *)poOptions)->dfNoDataValue;
|
|
// Nearest distance will be initialized with the distance to the first
|
|
// point in array.
|
|
double dfNearestR = DBL_MAX;
|
|
GUInt32 i = 0;
|
|
|
|
double dfSearchRadius = psExtraParams->dfInitialSearchRadius;
|
|
if( hQuadTree != NULL && dfRadius1 == dfRadius2 && dfSearchRadius > 0 )
|
|
{
|
|
CPLRectObj sAoi;
|
|
if( dfRadius1 > 0 )
|
|
dfSearchRadius = ((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1;
|
|
while(dfSearchRadius > 0)
|
|
{
|
|
sAoi.minx = dfXPoint - dfSearchRadius;
|
|
sAoi.miny = dfYPoint - dfSearchRadius;
|
|
sAoi.maxx = dfXPoint + dfSearchRadius;
|
|
sAoi.maxy = dfYPoint + dfSearchRadius;
|
|
int nFeatureCount = 0;
|
|
GDALGridPoint** papsPoints = (GDALGridPoint**)
|
|
CPLQuadTreeSearch(hQuadTree, &sAoi, &nFeatureCount);
|
|
if( nFeatureCount != 0 )
|
|
{
|
|
if( dfRadius1 > 0 )
|
|
dfNearestR = dfRadius1;
|
|
for(int k=0; k<nFeatureCount; k++)
|
|
{
|
|
int i = papsPoints[k]->i;
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
const double dfR2 = dfRX * dfRX + dfRY * dfRY;
|
|
if( dfR2 <= dfNearestR )
|
|
{
|
|
dfNearestR = dfR2;
|
|
dfNearestValue = padfZ[i];
|
|
}
|
|
}
|
|
|
|
CPLFree(papsPoints);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
CPLFree(papsPoints);
|
|
if( dfRadius1 > 0 )
|
|
break;
|
|
dfSearchRadius *= 2;
|
|
//CPLDebug("GDAL_GRID", "Increasing search radius to %.16g", dfSearchRadius);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
const double dfR2 = dfRX * dfRX + dfRY * dfRY;
|
|
if ( dfR2 <= dfNearestR )
|
|
{
|
|
dfNearestR = dfR2;
|
|
dfNearestValue = padfZ[i];
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
(*pdfValue) = dfNearestValue;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricMinimum() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Minimum data value (data metric).
|
|
*
|
|
* Minimum value found in grid node search ellipse. If there are no points
|
|
* found, the specified NODATA value will be returned.
|
|
*
|
|
* \f[
|
|
* Z=\min{(Z_1,Z_2,\ldots,Z_n)}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$Z_i\f$ is a known value at point \f$i\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricMinimum( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfMinimumValue=0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
if ( n )
|
|
{
|
|
if ( dfMinimumValue > padfZ[i] )
|
|
dfMinimumValue = padfZ[i];
|
|
}
|
|
else
|
|
dfMinimumValue = padfZ[i];
|
|
n++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfMinimumValue;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricMaximum() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Maximum data value (data metric).
|
|
*
|
|
* Maximum value found in grid node search ellipse. If there are no points
|
|
* found, the specified NODATA value will be returned.
|
|
*
|
|
* \f[
|
|
* Z=\max{(Z_1,Z_2,\ldots,Z_n)}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$Z_i\f$ is a known value at point \f$i\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricMaximum( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfMaximumValue=0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
if ( n )
|
|
{
|
|
if ( dfMaximumValue < padfZ[i] )
|
|
dfMaximumValue = padfZ[i];
|
|
}
|
|
else
|
|
dfMaximumValue = padfZ[i];
|
|
n++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfMaximumValue;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricRange() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Data range (data metric).
|
|
*
|
|
* A difference between the minimum and maximum values found in grid node
|
|
* search ellipse. If there are no points found, the specified NODATA
|
|
* value will be returned.
|
|
*
|
|
* \f[
|
|
* Z=\max{(Z_1,Z_2,\ldots,Z_n)}-\min{(Z_1,Z_2,\ldots,Z_n)}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$Z_i\f$ is a known value at point \f$i\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricRange( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfMaximumValue=0.0, dfMinimumValue=0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
if ( n )
|
|
{
|
|
if ( dfMinimumValue > padfZ[i] )
|
|
dfMinimumValue = padfZ[i];
|
|
if ( dfMaximumValue < padfZ[i] )
|
|
dfMaximumValue = padfZ[i];
|
|
}
|
|
else
|
|
dfMinimumValue = dfMaximumValue = padfZ[i];
|
|
n++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfMaximumValue - dfMinimumValue;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricCount() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Number of data points (data metric).
|
|
*
|
|
* A number of data points found in grid node search ellipse.
|
|
*
|
|
* \f[
|
|
* Z=n
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricCount( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
CPL_UNUSED const double *padfZ,
|
|
double dfXPoint, double dfYPoint, double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
n++;
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = (double)n;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricAverageDistance() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Average distance (data metric).
|
|
*
|
|
* An average distance between the grid node (center of the search ellipse)
|
|
* and all of the data points found in grid node search ellipse. If there are
|
|
* no points found, the specified NODATA value will be returned.
|
|
*
|
|
* \f[
|
|
* Z=\frac{\sum_{i = 1}^n r_i}{n}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$r_i\f$ is an Euclidean distance from the grid node
|
|
* to point \f$i\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricAverageDistance( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
CPL_UNUSED const double *padfZ,
|
|
double dfXPoint, double dfYPoint,
|
|
double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfAccumulator = 0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
while ( i < nPoints )
|
|
{
|
|
double dfRX = padfX[i] - dfXPoint;
|
|
double dfRY = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX * dfCoeff1 + dfRY * dfCoeff2;
|
|
double dfRYRotated = dfRY * dfCoeff1 - dfRX * dfCoeff2;
|
|
|
|
dfRX = dfRXRotated;
|
|
dfRY = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX * dfRX + dfRadius1 * dfRY * dfRY <= dfR12 )
|
|
{
|
|
dfAccumulator += sqrt( dfRX * dfRX + dfRY * dfRY );
|
|
n++;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfAccumulator / n;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridDataMetricAverageDistance() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Average distance between points (data metric).
|
|
*
|
|
* An average distance between the data points found in grid node search
|
|
* ellipse. The distance between each pair of points within ellipse is
|
|
* calculated and average of all distances is set as a grid node value. If
|
|
* there are no points found, the specified NODATA value will be returned.
|
|
|
|
*
|
|
* \f[
|
|
* Z=\frac{\sum_{i = 1}^{n-1}\sum_{j=i+1}^{n} r_{ij}}{\left(n-1\right)\,n-\frac{n+{\left(n-1\right)}^{2}-1}{2}}
|
|
* \f]
|
|
*
|
|
* where
|
|
* <ul>
|
|
* <li> \f$Z\f$ is a resulting value at the grid node,
|
|
* <li> \f$r_{ij}\f$ is an Euclidean distance between points
|
|
* \f$i\f$ and \f$j\f$,
|
|
* <li> \f$n\f$ is a total number of points in search ellipse.
|
|
* </ul>
|
|
*
|
|
* @param poOptions Algorithm parameters. This should point to
|
|
* GDALGridDataMetricsOptions object.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXPoint X coordinate of the point to compute.
|
|
* @param dfYPoint Y coordinate of the point to compute.
|
|
* @param pdfValue Pointer to variable where the computed grid node value
|
|
* will be returned.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridDataMetricAverageDistancePts( const void *poOptions, GUInt32 nPoints,
|
|
const double *padfX, const double *padfY,
|
|
CPL_UNUSED const double *padfZ,
|
|
double dfXPoint, double dfYPoint,
|
|
double *pdfValue,
|
|
CPL_UNUSED void* hExtraParamsIn )
|
|
{
|
|
// TODO: For optimization purposes pre-computed parameters should be moved
|
|
// out of this routine to the calling function.
|
|
|
|
// Pre-compute search ellipse parameters
|
|
double dfRadius1 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius1;
|
|
double dfRadius2 =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfRadius2;
|
|
double dfR12;
|
|
|
|
dfRadius1 *= dfRadius1;
|
|
dfRadius2 *= dfRadius2;
|
|
dfR12 = dfRadius1 * dfRadius2;
|
|
|
|
// Compute coefficients for coordinate system rotation.
|
|
double dfCoeff1 = 0.0, dfCoeff2 = 0.0;
|
|
const double dfAngle =
|
|
TO_RADIANS * ((GDALGridDataMetricsOptions *)poOptions)->dfAngle;
|
|
const bool bRotated = ( dfAngle == 0.0 ) ? false : true;
|
|
if ( bRotated )
|
|
{
|
|
dfCoeff1 = cos(dfAngle);
|
|
dfCoeff2 = sin(dfAngle);
|
|
}
|
|
|
|
double dfAccumulator = 0.0;
|
|
GUInt32 i = 0, n = 0;
|
|
|
|
// Search for the first point within the search ellipse
|
|
while ( i < nPoints - 1 )
|
|
{
|
|
double dfRX1 = padfX[i] - dfXPoint;
|
|
double dfRY1 = padfY[i] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX1 * dfCoeff1 + dfRY1 * dfCoeff2;
|
|
double dfRYRotated = dfRY1 * dfCoeff1 - dfRX1 * dfCoeff2;
|
|
|
|
dfRX1 = dfRXRotated;
|
|
dfRY1 = dfRYRotated;
|
|
}
|
|
|
|
// Is this point located inside the search ellipse?
|
|
if ( dfRadius2 * dfRX1 * dfRX1 + dfRadius1 * dfRY1 * dfRY1 <= dfR12 )
|
|
{
|
|
GUInt32 j;
|
|
|
|
// Search all the remaining points within the ellipse and compute
|
|
// distances between them and the first point
|
|
for ( j = i + 1; j < nPoints; j++ )
|
|
{
|
|
double dfRX2 = padfX[j] - dfXPoint;
|
|
double dfRY2 = padfY[j] - dfYPoint;
|
|
|
|
if ( bRotated )
|
|
{
|
|
double dfRXRotated = dfRX2 * dfCoeff1 + dfRY2 * dfCoeff2;
|
|
double dfRYRotated = dfRY2 * dfCoeff1 - dfRX2 * dfCoeff2;
|
|
|
|
dfRX2 = dfRXRotated;
|
|
dfRY2 = dfRYRotated;
|
|
}
|
|
|
|
if ( dfRadius2 * dfRX2 * dfRX2 + dfRadius1 * dfRY2 * dfRY2 <= dfR12 )
|
|
{
|
|
const double dfRX = padfX[j] - padfX[i];
|
|
const double dfRY = padfY[j] - padfY[i];
|
|
|
|
dfAccumulator += sqrt( dfRX * dfRX + dfRY * dfRY );
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
if ( n < ((GDALGridDataMetricsOptions *)poOptions)->nMinPoints
|
|
|| n == 0 )
|
|
{
|
|
(*pdfValue) =
|
|
((GDALGridDataMetricsOptions *)poOptions)->dfNoDataValue;
|
|
}
|
|
else
|
|
(*pdfValue) = dfAccumulator / n;
|
|
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridJob */
|
|
/************************************************************************/
|
|
|
|
typedef struct _GDALGridJob GDALGridJob;
|
|
|
|
struct _GDALGridJob
|
|
{
|
|
GUInt32 nYStart;
|
|
|
|
GByte *pabyData;
|
|
GUInt32 nYStep;
|
|
GUInt32 nXSize;
|
|
GUInt32 nYSize;
|
|
double dfXMin;
|
|
double dfYMin;
|
|
double dfDeltaX;
|
|
double dfDeltaY;
|
|
GUInt32 nPoints;
|
|
const double *padfX;
|
|
const double *padfY;
|
|
const double *padfZ;
|
|
const void *poOptions;
|
|
GDALGridFunction pfnGDALGridMethod;
|
|
GDALGridExtraParameters* psExtraParameters;
|
|
int (*pfnProgress)(GDALGridJob* psJob);
|
|
GDALDataType eType;
|
|
|
|
CPLJoinableThread *hThread;
|
|
volatile int *pnCounter;
|
|
volatile int *pbStop;
|
|
CPLCond *hCond;
|
|
CPLMutex *hCondMutex;
|
|
|
|
GDALProgressFunc pfnRealProgress;
|
|
void *pRealProgressArg;
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALGridProgressMultiThread() */
|
|
/************************************************************************/
|
|
|
|
/* Return TRUE if the computation must be interrupted */
|
|
static int GDALGridProgressMultiThread(GDALGridJob* psJob)
|
|
{
|
|
CPLAcquireMutex(psJob->hCondMutex, 1.0);
|
|
(*(psJob->pnCounter)) ++;
|
|
CPLCondSignal(psJob->hCond);
|
|
int bStop = *(psJob->pbStop);
|
|
CPLReleaseMutex(psJob->hCondMutex);
|
|
|
|
return bStop;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridProgressMonoThread() */
|
|
/************************************************************************/
|
|
|
|
/* Return TRUE if the computation must be interrupted */
|
|
static int GDALGridProgressMonoThread(GDALGridJob* psJob)
|
|
{
|
|
int nCounter = ++(*(psJob->pnCounter));
|
|
if( !psJob->pfnRealProgress( (nCounter / (double) psJob->nYSize),
|
|
"", psJob->pRealProgressArg ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
*(psJob->pbStop) = TRUE;
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridJobProcess() */
|
|
/************************************************************************/
|
|
|
|
static void GDALGridJobProcess(void* user_data)
|
|
{
|
|
GDALGridJob* psJob = (GDALGridJob*)user_data;
|
|
GUInt32 nXPoint, nYPoint;
|
|
|
|
const GUInt32 nYStart = psJob->nYStart;
|
|
const GUInt32 nYStep = psJob->nYStep;
|
|
GByte *pabyData = psJob->pabyData;
|
|
|
|
const GUInt32 nXSize = psJob->nXSize;
|
|
const GUInt32 nYSize = psJob->nYSize;
|
|
const double dfXMin = psJob->dfXMin;
|
|
const double dfYMin = psJob->dfYMin;
|
|
const double dfDeltaX = psJob->dfDeltaX;
|
|
const double dfDeltaY = psJob->dfDeltaY;
|
|
GUInt32 nPoints = psJob->nPoints;
|
|
const double* padfX = psJob->padfX;
|
|
const double* padfY = psJob->padfY;
|
|
const double* padfZ = psJob->padfZ;
|
|
const void *poOptions = psJob->poOptions;
|
|
GDALGridFunction pfnGDALGridMethod = psJob->pfnGDALGridMethod;
|
|
GDALGridExtraParameters *psExtraParameters = psJob->psExtraParameters;
|
|
GDALDataType eType = psJob->eType;
|
|
int (*pfnProgress)(GDALGridJob* psJob) = psJob->pfnProgress;
|
|
|
|
int nDataTypeSize = GDALGetDataTypeSize(eType) / 8;
|
|
int nLineSpace = nXSize * nDataTypeSize;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Allocate a buffer of scanline size, fill it with gridded values */
|
|
/* and use GDALCopyWords() to copy values into output data array with */
|
|
/* appropriate data type conversion. */
|
|
/* -------------------------------------------------------------------- */
|
|
double *padfValues = (double *)VSIMalloc2( sizeof(double), nXSize );
|
|
if( padfValues == NULL )
|
|
{
|
|
*(psJob->pbStop) = TRUE;
|
|
pfnProgress(psJob); /* to notify the main thread */
|
|
return;
|
|
}
|
|
|
|
for ( nYPoint = nYStart; nYPoint < nYSize; nYPoint += nYStep )
|
|
{
|
|
const double dfYPoint = dfYMin + ( nYPoint + 0.5 ) * dfDeltaY;
|
|
|
|
for ( nXPoint = 0; nXPoint < nXSize; nXPoint++ )
|
|
{
|
|
const double dfXPoint = dfXMin + ( nXPoint + 0.5 ) * dfDeltaX;
|
|
|
|
if ( (*pfnGDALGridMethod)( poOptions, nPoints, padfX, padfY, padfZ,
|
|
dfXPoint, dfYPoint,
|
|
padfValues + nXPoint, psExtraParameters ) != CE_None )
|
|
{
|
|
CPLError( CE_Failure, CPLE_AppDefined,
|
|
"Gridding failed at X position %lu, Y position %lu",
|
|
(long unsigned int)nXPoint,
|
|
(long unsigned int)nYPoint );
|
|
*(psJob->pbStop) = TRUE;
|
|
pfnProgress(psJob); /* to notify the main thread */
|
|
break;
|
|
}
|
|
}
|
|
|
|
GDALCopyWords( padfValues, GDT_Float64, sizeof(double),
|
|
pabyData + nYPoint * nLineSpace, eType, nDataTypeSize,
|
|
nXSize );
|
|
|
|
if( *(psJob->pbStop) || pfnProgress(psJob) )
|
|
break;
|
|
}
|
|
|
|
CPLFree(padfValues);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALGridCreate() */
|
|
/************************************************************************/
|
|
|
|
/**
|
|
* Create regular grid from the scattered data.
|
|
*
|
|
* This function takes the arrays of X and Y coordinates and corresponding Z
|
|
* values as input and computes regular grid (or call it a raster) from these
|
|
* scattered data. You should supply geometry and extent of the output grid
|
|
* and allocate array sufficient to hold such a grid.
|
|
*
|
|
* Starting with GDAL 1.10, it is possible to set the GDAL_NUM_THREADS
|
|
* configuration option to parallelize the processing. The value to set is
|
|
* the number of worker threads, or ALL_CPUS to use all the cores/CPUs of the
|
|
* computer (default value).
|
|
*
|
|
* Starting with GDAL 1.10, on Intel/AMD i386/x86_64 architectures, some
|
|
* gridding methods will be optimized with SSE instructions (provided GDAL
|
|
* has been compiled with such support, and it is availabable at runtime).
|
|
* Currently, only 'invdist' algorithm with default parameters has an optimized
|
|
* implementation.
|
|
* This can provide substantial speed-up, but sometimes at the expense of
|
|
* reduced floating point precision. This can be disabled by setting the
|
|
* GDAL_USE_SSE configuration option to NO.
|
|
* Starting with GDAL 1.11, a further optimized version can use the AVX
|
|
* instruction set. This can be disabled by setting the GDAL_USE_AVX
|
|
* configuration option to NO.
|
|
*
|
|
*
|
|
* @param eAlgorithm Gridding method.
|
|
* @param poOptions Options to control choosen gridding method.
|
|
* @param nPoints Number of elements in input arrays.
|
|
* @param padfX Input array of X coordinates.
|
|
* @param padfY Input array of Y coordinates.
|
|
* @param padfZ Input array of Z values.
|
|
* @param dfXMin Lowest X border of output grid.
|
|
* @param dfXMax Highest X border of output grid.
|
|
* @param dfYMin Lowest Y border of output grid.
|
|
* @param dfYMax Highest Y border of output grid.
|
|
* @param nXSize Number of columns in output grid.
|
|
* @param nYSize Number of rows in output grid.
|
|
* @param eType Data type of output array.
|
|
* @param pData Pointer to array where the computed grid will be stored.
|
|
* @param pfnProgress a GDALProgressFunc() compatible callback function for
|
|
* reporting progress or NULL.
|
|
* @param pProgressArg argument to be passed to pfnProgress. May be NULL.
|
|
*
|
|
* @return CE_None on success or CE_Failure if something goes wrong.
|
|
*/
|
|
|
|
CPLErr
|
|
GDALGridCreate( GDALGridAlgorithm eAlgorithm, const void *poOptions,
|
|
GUInt32 nPoints,
|
|
const double *padfX, const double *padfY, const double *padfZ,
|
|
double dfXMin, double dfXMax, double dfYMin, double dfYMax,
|
|
GUInt32 nXSize, GUInt32 nYSize, GDALDataType eType, void *pData,
|
|
GDALProgressFunc pfnProgress, void *pProgressArg )
|
|
{
|
|
CPLAssert( poOptions );
|
|
CPLAssert( padfX );
|
|
CPLAssert( padfY );
|
|
CPLAssert( padfZ );
|
|
CPLAssert( pData );
|
|
|
|
if ( pfnProgress == NULL )
|
|
pfnProgress = GDALDummyProgress;
|
|
|
|
if ( nXSize == 0 || nYSize == 0 )
|
|
{
|
|
CPLError( CE_Failure, CPLE_IllegalArg,
|
|
"Output raster dimesions should have non-zero size." );
|
|
return CE_Failure;
|
|
}
|
|
|
|
GDALGridFunction pfnGDALGridMethod;
|
|
int bCreateQuadTree = FALSE;
|
|
|
|
/* Potentially unaligned pointers */
|
|
void* pabyX = NULL;
|
|
void* pabyY = NULL;
|
|
void* pabyZ = NULL;
|
|
|
|
/* Starting address aligned on 16-byte boundary */
|
|
float* pafXAligned = NULL;
|
|
float* pafYAligned = NULL;
|
|
float* pafZAligned = NULL;
|
|
|
|
switch ( eAlgorithm )
|
|
{
|
|
case GGA_InverseDistanceToAPower:
|
|
if ( ((GDALGridInverseDistanceToAPowerOptions *)poOptions)->
|
|
dfRadius1 == 0.0 &&
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->
|
|
dfRadius2 == 0.0 )
|
|
{
|
|
const double dfPower =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfPower;
|
|
const double dfSmoothing =
|
|
((GDALGridInverseDistanceToAPowerOptions *)poOptions)->dfSmoothing;
|
|
|
|
pfnGDALGridMethod = GDALGridInverseDistanceToAPowerNoSearch;
|
|
if( dfPower == 2.0 && dfSmoothing == 0.0 )
|
|
{
|
|
#ifdef HAVE_AVX_AT_COMPILE_TIME
|
|
|
|
#define ALIGN32(x) (((char*)(x)) + ((32 - (((size_t)(x)) % 32)) % 32))
|
|
|
|
if( CSLTestBoolean(CPLGetConfigOption("GDAL_USE_AVX", "YES")) &&
|
|
CPLHaveRuntimeAVX() )
|
|
{
|
|
pabyX = (float*)VSIMalloc(sizeof(float) * nPoints + 31);
|
|
pabyY = (float*)VSIMalloc(sizeof(float) * nPoints + 31);
|
|
pabyZ = (float*)VSIMalloc(sizeof(float) * nPoints + 31);
|
|
if( pabyX != NULL && pabyY != NULL && pabyZ != NULL)
|
|
{
|
|
CPLDebug("GDAL_GRID", "Using AVX optimized version");
|
|
pafXAligned = (float*) ALIGN32(pabyX);
|
|
pafYAligned = (float*) ALIGN32(pabyY);
|
|
pafZAligned = (float*) ALIGN32(pabyZ);
|
|
pfnGDALGridMethod = GDALGridInverseDistanceToAPower2NoSmoothingNoSearchAVX;
|
|
GUInt32 i;
|
|
for(i=0;i<nPoints;i++)
|
|
{
|
|
pafXAligned[i] = (float) padfX[i];
|
|
pafYAligned[i] = (float) padfY[i];
|
|
pafZAligned[i] = (float) padfZ[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VSIFree(pabyX);
|
|
VSIFree(pabyY);
|
|
VSIFree(pabyZ);
|
|
pabyX = pabyY = pabyZ = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAVE_SSE_AT_COMPILE_TIME
|
|
|
|
#define ALIGN16(x) (((char*)(x)) + ((16 - (((size_t)(x)) % 16)) % 16))
|
|
|
|
if( pafXAligned == NULL &&
|
|
CSLTestBoolean(CPLGetConfigOption("GDAL_USE_SSE", "YES")) &&
|
|
CPLHaveRuntimeSSE() )
|
|
{
|
|
pabyX = (float*)VSIMalloc(sizeof(float) * nPoints + 15);
|
|
pabyY = (float*)VSIMalloc(sizeof(float) * nPoints + 15);
|
|
pabyZ = (float*)VSIMalloc(sizeof(float) * nPoints + 15);
|
|
if( pabyX != NULL && pabyY != NULL && pabyZ != NULL)
|
|
{
|
|
CPLDebug("GDAL_GRID", "Using SSE optimized version");
|
|
pafXAligned = (float*) ALIGN16(pabyX);
|
|
pafYAligned = (float*) ALIGN16(pabyY);
|
|
pafZAligned = (float*) ALIGN16(pabyZ);
|
|
pfnGDALGridMethod = GDALGridInverseDistanceToAPower2NoSmoothingNoSearchSSE;
|
|
GUInt32 i;
|
|
for(i=0;i<nPoints;i++)
|
|
{
|
|
pafXAligned[i] = (float) padfX[i];
|
|
pafYAligned[i] = (float) padfY[i];
|
|
pafZAligned[i] = (float) padfZ[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VSIFree(pabyX);
|
|
VSIFree(pabyY);
|
|
VSIFree(pabyZ);
|
|
pabyX = pabyY = pabyZ = NULL;
|
|
}
|
|
}
|
|
#endif // HAVE_SSE_AT_COMPILE_TIME
|
|
}
|
|
}
|
|
else
|
|
pfnGDALGridMethod = GDALGridInverseDistanceToAPower;
|
|
break;
|
|
|
|
case GGA_MovingAverage:
|
|
pfnGDALGridMethod = GDALGridMovingAverage;
|
|
break;
|
|
|
|
case GGA_NearestNeighbor:
|
|
pfnGDALGridMethod = GDALGridNearestNeighbor;
|
|
bCreateQuadTree = (nPoints > 100 &&
|
|
(((GDALGridNearestNeighborOptions *)poOptions)->dfRadius1 ==
|
|
((GDALGridNearestNeighborOptions *)poOptions)->dfRadius2));
|
|
break;
|
|
|
|
case GGA_MetricMinimum:
|
|
pfnGDALGridMethod = GDALGridDataMetricMinimum;
|
|
break;
|
|
|
|
case GGA_MetricMaximum:
|
|
pfnGDALGridMethod = GDALGridDataMetricMaximum;
|
|
break;
|
|
|
|
case GGA_MetricRange:
|
|
pfnGDALGridMethod = GDALGridDataMetricRange;
|
|
break;
|
|
|
|
case GGA_MetricCount:
|
|
pfnGDALGridMethod = GDALGridDataMetricCount;
|
|
break;
|
|
|
|
case GGA_MetricAverageDistance:
|
|
pfnGDALGridMethod = GDALGridDataMetricAverageDistance;
|
|
break;
|
|
|
|
case GGA_MetricAverageDistancePts:
|
|
pfnGDALGridMethod = GDALGridDataMetricAverageDistancePts;
|
|
break;
|
|
|
|
default:
|
|
CPLError( CE_Failure, CPLE_IllegalArg,
|
|
"GDAL does not support gridding method %d", eAlgorithm );
|
|
return CE_Failure;
|
|
}
|
|
|
|
const double dfDeltaX = ( dfXMax - dfXMin ) / nXSize;
|
|
const double dfDeltaY = ( dfYMax - dfYMin ) / nYSize;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Create quadtree if requested and possible. */
|
|
/* -------------------------------------------------------------------- */
|
|
CPLQuadTree* hQuadTree = NULL;
|
|
double dfInitialSearchRadius = 0;
|
|
GDALGridPoint* pasGridPoints = NULL;
|
|
GDALGridXYArrays sXYArrays;
|
|
sXYArrays.padfX = padfX;
|
|
sXYArrays.padfY = padfY;
|
|
|
|
if( bCreateQuadTree )
|
|
{
|
|
pasGridPoints = (GDALGridPoint*) VSIMalloc2(nPoints, sizeof(GDALGridPoint));
|
|
if( pasGridPoints != NULL )
|
|
{
|
|
CPLRectObj sRect;
|
|
GUInt32 i;
|
|
|
|
/* Determine point extents */
|
|
sRect.minx = padfX[0];
|
|
sRect.miny = padfY[0];
|
|
sRect.maxx = padfX[0];
|
|
sRect.maxy = padfY[0];
|
|
for(i = 1; i < nPoints; i++)
|
|
{
|
|
if( padfX[i] < sRect.minx ) sRect.minx = padfX[i];
|
|
if( padfY[i] < sRect.miny ) sRect.miny = padfY[i];
|
|
if( padfX[i] > sRect.maxx ) sRect.maxx = padfX[i];
|
|
if( padfY[i] > sRect.maxy ) sRect.maxy = padfY[i];
|
|
}
|
|
|
|
/* Initial value for search radius is the typical dimension of a */
|
|
/* "pixel" of the point array (assuming rather uniform distribution) */
|
|
dfInitialSearchRadius = sqrt((sRect.maxx - sRect.minx) *
|
|
(sRect.maxy - sRect.miny) / nPoints);
|
|
|
|
hQuadTree = CPLQuadTreeCreate(&sRect, GDALGridGetPointBounds );
|
|
|
|
for(i = 0; i < nPoints; i++)
|
|
{
|
|
pasGridPoints[i].psXYArrays = &sXYArrays;
|
|
pasGridPoints[i].i = i;
|
|
CPLQuadTreeInsert(hQuadTree, pasGridPoints + i);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
GDALGridExtraParameters sExtraParameters;
|
|
|
|
sExtraParameters.hQuadTree = hQuadTree;
|
|
sExtraParameters.dfInitialSearchRadius = dfInitialSearchRadius;
|
|
sExtraParameters.pafX = pafXAligned;
|
|
sExtraParameters.pafY = pafYAligned;
|
|
sExtraParameters.pafZ = pafZAligned;
|
|
|
|
const char* pszThreads = CPLGetConfigOption("GDAL_NUM_THREADS", "ALL_CPUS");
|
|
int nThreads;
|
|
if (EQUAL(pszThreads, "ALL_CPUS"))
|
|
nThreads = CPLGetNumCPUs();
|
|
else
|
|
nThreads = atoi(pszThreads);
|
|
if (nThreads > 128)
|
|
nThreads = 128;
|
|
if (nThreads >= (int)nYSize / 2)
|
|
nThreads = (int)nYSize / 2;
|
|
|
|
volatile int nCounter = 0;
|
|
volatile int bStop = FALSE;
|
|
|
|
GDALGridJob sJob;
|
|
sJob.nYStart = 0;
|
|
sJob.pabyData = (GByte*) pData;
|
|
sJob.nYStep = 1;
|
|
sJob.nXSize = nXSize;
|
|
sJob.nYSize = nYSize;
|
|
sJob.dfXMin = dfXMin;
|
|
sJob.dfYMin = dfYMin;
|
|
sJob.dfDeltaX = dfDeltaX;
|
|
sJob.dfDeltaY = dfDeltaY;
|
|
sJob.nPoints = nPoints;
|
|
sJob.padfX = padfX;
|
|
sJob.padfY = padfY;
|
|
sJob.padfZ = padfZ;
|
|
sJob.poOptions = poOptions;
|
|
sJob.pfnGDALGridMethod = pfnGDALGridMethod;
|
|
sJob.psExtraParameters = &sExtraParameters;
|
|
sJob.pfnProgress = NULL;
|
|
sJob.eType = eType;
|
|
sJob.pfnRealProgress = pfnProgress;
|
|
sJob.pRealProgressArg = pProgressArg;
|
|
sJob.pnCounter = &nCounter;
|
|
sJob.pbStop = &bStop;
|
|
sJob.hCond = NULL;
|
|
sJob.hCondMutex = NULL;
|
|
sJob.hThread = NULL;
|
|
|
|
if( nThreads > 1 )
|
|
{
|
|
sJob.hCond = CPLCreateCond();
|
|
if( sJob.hCond == NULL )
|
|
{
|
|
CPLError(CE_Warning, CPLE_AppDefined,
|
|
"Cannot create condition. Reverting to monothread processing");
|
|
nThreads = 1;
|
|
}
|
|
}
|
|
|
|
if( nThreads <= 1 )
|
|
{
|
|
sJob.pfnProgress = GDALGridProgressMonoThread;
|
|
|
|
GDALGridJobProcess(&sJob);
|
|
}
|
|
else
|
|
{
|
|
GDALGridJob* pasJobs = (GDALGridJob*) CPLMalloc(sizeof(GDALGridJob) * nThreads);
|
|
int i;
|
|
|
|
CPLDebug("GDAL_GRID", "Using %d threads", nThreads);
|
|
|
|
sJob.nYStep = nThreads;
|
|
sJob.hCondMutex = CPLCreateMutex(); /* and take implicitely the mutex */
|
|
sJob.pfnProgress = GDALGridProgressMultiThread;
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Start threads. */
|
|
/* -------------------------------------------------------------------- */
|
|
for(i = 0; i < nThreads && !bStop; i++)
|
|
{
|
|
memcpy(&pasJobs[i], &sJob, sizeof(GDALGridJob));
|
|
pasJobs[i].nYStart = i;
|
|
pasJobs[i].hThread = CPLCreateJoinableThread( GDALGridJobProcess,
|
|
(void*) &pasJobs[i] );
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Report progress. */
|
|
/* -------------------------------------------------------------------- */
|
|
while(nCounter < (int)nYSize && !bStop)
|
|
{
|
|
CPLCondWait(sJob.hCond, sJob.hCondMutex);
|
|
|
|
int nLocalCounter = nCounter;
|
|
CPLReleaseMutex(sJob.hCondMutex);
|
|
|
|
if( !pfnProgress( nLocalCounter / (double) nYSize, "", pProgressArg ) )
|
|
{
|
|
CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" );
|
|
bStop = TRUE;
|
|
}
|
|
|
|
CPLAcquireMutex(sJob.hCondMutex, 1.0);
|
|
}
|
|
|
|
/* Release mutex before joining threads, otherwise they will dead-lock */
|
|
/* forever in GDALGridProgressMultiThread() */
|
|
CPLReleaseMutex(sJob.hCondMutex);
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Wait for all threads to complete and finish. */
|
|
/* -------------------------------------------------------------------- */
|
|
for(i=0;i<nThreads;i++)
|
|
{
|
|
if( pasJobs[i].hThread )
|
|
CPLJoinThread(pasJobs[i].hThread);
|
|
}
|
|
|
|
CPLFree(pasJobs);
|
|
CPLDestroyCond(sJob.hCond);
|
|
CPLDestroyMutex(sJob.hCondMutex);
|
|
}
|
|
|
|
CPLFree( pasGridPoints );
|
|
if( hQuadTree != NULL )
|
|
CPLQuadTreeDestroy( hQuadTree );
|
|
|
|
CPLFree(pabyX);
|
|
CPLFree(pabyY);
|
|
CPLFree(pabyZ);
|
|
|
|
return bStop ? CE_Failure : CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ParseAlgorithmAndOptions() */
|
|
/* */
|
|
/* Translates mnemonic gridding algorithm names into */
|
|
/* GDALGridAlgorithm code, parse control parameters and assign */
|
|
/* defaults. */
|
|
/************************************************************************/
|
|
|
|
CPLErr ParseAlgorithmAndOptions( const char *pszAlgorithm,
|
|
GDALGridAlgorithm *peAlgorithm,
|
|
void **ppOptions )
|
|
{
|
|
CPLAssert( pszAlgorithm );
|
|
CPLAssert( peAlgorithm );
|
|
CPLAssert( ppOptions );
|
|
|
|
*ppOptions = NULL;
|
|
|
|
char **papszParms = CSLTokenizeString2( pszAlgorithm, ":", FALSE );
|
|
|
|
if ( CSLCount(papszParms) < 1 )
|
|
return CE_Failure;
|
|
|
|
if ( EQUAL(papszParms[0], szAlgNameInvDist) )
|
|
*peAlgorithm = GGA_InverseDistanceToAPower;
|
|
else if ( EQUAL(papszParms[0], szAlgNameAverage) )
|
|
*peAlgorithm = GGA_MovingAverage;
|
|
else if ( EQUAL(papszParms[0], szAlgNameNearest) )
|
|
*peAlgorithm = GGA_NearestNeighbor;
|
|
else if ( EQUAL(papszParms[0], szAlgNameMinimum) )
|
|
*peAlgorithm = GGA_MetricMinimum;
|
|
else if ( EQUAL(papszParms[0], szAlgNameMaximum) )
|
|
*peAlgorithm = GGA_MetricMaximum;
|
|
else if ( EQUAL(papszParms[0], szAlgNameRange) )
|
|
*peAlgorithm = GGA_MetricRange;
|
|
else if ( EQUAL(papszParms[0], szAlgNameCount) )
|
|
*peAlgorithm = GGA_MetricCount;
|
|
else if ( EQUAL(papszParms[0], szAlgNameAverageDistance) )
|
|
*peAlgorithm = GGA_MetricAverageDistance;
|
|
else if ( EQUAL(papszParms[0], szAlgNameAverageDistancePts) )
|
|
*peAlgorithm = GGA_MetricAverageDistancePts;
|
|
else
|
|
{
|
|
fprintf( stderr, "Unsupported gridding method \"%s\".\n",
|
|
papszParms[0] );
|
|
CSLDestroy( papszParms );
|
|
return CE_Failure;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Parse algorithm parameters and assign defaults. */
|
|
/* -------------------------------------------------------------------- */
|
|
const char *pszValue;
|
|
|
|
switch ( *peAlgorithm )
|
|
{
|
|
case GGA_InverseDistanceToAPower:
|
|
default:
|
|
*ppOptions =
|
|
CPLMalloc( sizeof(GDALGridInverseDistanceToAPowerOptions) );
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "power" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfPower = (pszValue) ? CPLAtofM(pszValue) : 2.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "smoothing" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfSmoothing = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius1" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius2" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "angle" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "max_points" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
nMaxPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0);
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "min_points" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
nMinPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0);
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "nodata" );
|
|
((GDALGridInverseDistanceToAPowerOptions *)*ppOptions)->
|
|
dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
break;
|
|
|
|
case GGA_MovingAverage:
|
|
*ppOptions =
|
|
CPLMalloc( sizeof(GDALGridMovingAverageOptions) );
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius1" );
|
|
((GDALGridMovingAverageOptions *)*ppOptions)->
|
|
dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius2" );
|
|
((GDALGridMovingAverageOptions *)*ppOptions)->
|
|
dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "angle" );
|
|
((GDALGridMovingAverageOptions *)*ppOptions)->
|
|
dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "min_points" );
|
|
((GDALGridMovingAverageOptions *)*ppOptions)->
|
|
nMinPoints = (GUInt32) ((pszValue) ? CPLAtofM(pszValue) : 0);
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "nodata" );
|
|
((GDALGridMovingAverageOptions *)*ppOptions)->
|
|
dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
break;
|
|
|
|
case GGA_NearestNeighbor:
|
|
*ppOptions =
|
|
CPLMalloc( sizeof(GDALGridNearestNeighborOptions) );
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius1" );
|
|
((GDALGridNearestNeighborOptions *)*ppOptions)->
|
|
dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius2" );
|
|
((GDALGridNearestNeighborOptions *)*ppOptions)->
|
|
dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "angle" );
|
|
((GDALGridNearestNeighborOptions *)*ppOptions)->
|
|
dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "nodata" );
|
|
((GDALGridNearestNeighborOptions *)*ppOptions)->
|
|
dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
break;
|
|
|
|
case GGA_MetricMinimum:
|
|
case GGA_MetricMaximum:
|
|
case GGA_MetricRange:
|
|
case GGA_MetricCount:
|
|
case GGA_MetricAverageDistance:
|
|
case GGA_MetricAverageDistancePts:
|
|
*ppOptions =
|
|
CPLMalloc( sizeof(GDALGridDataMetricsOptions) );
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius1" );
|
|
((GDALGridDataMetricsOptions *)*ppOptions)->
|
|
dfRadius1 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "radius2" );
|
|
((GDALGridDataMetricsOptions *)*ppOptions)->
|
|
dfRadius2 = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "angle" );
|
|
((GDALGridDataMetricsOptions *)*ppOptions)->
|
|
dfAngle = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "min_points" );
|
|
((GDALGridDataMetricsOptions *)*ppOptions)->
|
|
nMinPoints = (pszValue) ? atol(pszValue) : 0;
|
|
|
|
pszValue = CSLFetchNameValue( papszParms, "nodata" );
|
|
((GDALGridDataMetricsOptions *)*ppOptions)->
|
|
dfNoDataValue = (pszValue) ? CPLAtofM(pszValue) : 0.0;
|
|
break;
|
|
|
|
}
|
|
|
|
CSLDestroy( papszParms );
|
|
return CE_None;
|
|
}
|