目标:(七十二)中的问题147
高程文件的划分规则和rex瓦片是不一样的,高程文件的0级就是文件本身,不管文件实际范围多大,然后采用二分法逐级增加。rex瓦片的0级对应整个地球表面。
两个范围一般不是一一对应的,秘密尽在
1、首先根据rex瓦片的范围从高程文件中找出相交的高程文件瓦片(该瓦片不同于rex瓦片,是和高程文件对应的瓦片,瓦片尺寸和rex瓦片最为接近)
2、从相交的高程文件瓦片构建出一个中间高程场,以上两步在这里完成
osgEarth/ElevationLayer.cpp
void
ElevationLayer::assembleHeightField(const TileKey& key,
osg::ref_ptr<osg::HeightField>& out_hf,
osg::ref_ptr<NormalMap>& out_normalMap,
ProgressCallback* progress)
{
//osg::HeightField* result = 0L;
// Collect the heightfields for each of the intersecting tiles.
GeoHeightFieldVector heightFields;
//Determine the intersecting keys
std::vector< TileKey > intersectingTiles;
getProfile()->getIntersectingTiles( key, intersectingTiles );//获取相交的高程文件瓦片
// collect heightfield for each intersecting key. Note, we're hitting the
// underlying tile source here, so there's no vetical datum shifts happening yet.
// we will do that later.
if ( intersectingTiles.size() > 0 )
{
for (unsigned int i = 0; i < intersectingTiles.size(); ++i)
{
const TileKey& layerKey = intersectingTiles[i];
if ( isKeyInLegalRange(layerKey) )
{
osg::ref_ptr<osg::HeightField> hf;
osg::ref_ptr<NormalMap> normalMap;
createImplementation(layerKey, hf, normalMap, progress);
//osg::HeightField* hf = createHeightFieldImplementation( layerKey, progress );
if (hf.valid())
{
heightFields.push_back( GeoHeightField(hf.get(), normalMap.get(), layerKey.getExtent()) );
}
}
}
}
// If we actually got a HeightField, resample/reproject it to match the incoming TileKey's extents.
if (heightFields.size() > 0)
{
unsigned int width = 0;
unsigned int height = 0;
for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
{
if (itr->getHeightField()->getNumColumns() > width)
width = itr->getHeightField()->getNumColumns();
if (itr->getHeightField()->getNumRows() > height)
height = itr->getHeightField()->getNumRows();
}
//Now sort the heightfields by resolution to make sure we're sampling the highest resolution one first.
std::sort( heightFields.begin(), heightFields.end(), GeoHeightField::SortByResolutionFunctor());
out_hf = new osg::HeightField();
out_hf->allocate(width, height);
out_normalMap = new NormalMap(width, height);
//Go ahead and set up the heightfield so we don't have to worry about it later
double minx, miny, maxx, maxy;
key.getExtent().getBounds(minx, miny, maxx, maxy);
double dx = (maxx - minx)/(double)(width-1);
double dy = (maxy - miny)/(double)(height-1);
//Create the new heightfield by sampling all of them.
for (unsigned int c = 0; c < width; ++c)
{
double x = minx + (dx * (double)c);
for (unsigned r = 0; r < height; ++r)
{
double y = miny + (dy * (double)r);
//For each sample point, try each heightfield. The first one with a valid elevation wins.
float elevation = NO_DATA_VALUE;
osg::Vec3 normal(0,0,1);
for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr)
{
// get the elevation value, at the same time transforming it vertically into the
// requesting key's vertical datum.
float e = 0.0;
osg::Vec3 n;
if (itr->getElevationAndNormal(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e, n))
{
elevation = e;
normal = n;
break;
}
}
out_hf->setHeight( c, r, elevation ); //设置中间高程场,最终的rex瓦片高程来自于此
out_normalMap->set( c, r, normal );
}
}
}
}
3、将中间高程场提取出rex瓦片的高程,该步在下面完成
bool
ElevationLayerVector::populateHeightFieldAndNormalMap(osg::HeightField* hf,
NormalMap* normalMap,
const TileKey& key,
const Profile* haeProfile,
ElevationInterpolation interpolation,
ProgressCallback* progress ) const
{
// heightfield must already exist.
if ( !hf )
return false;
METRIC_SCOPED("ElevationLayer.populateHeightField");
// if the caller provided an "HAE map profile", he wants an HAE elevation grid even if
// the map profile has a vertical datum. This is the usual case when building the 3D
// terrain, for example. Construct a temporary key that doesn't have the vertical
// datum info and use that to query the elevation data.
TileKey keyToUse = key;
if ( haeProfile )
{
keyToUse = TileKey(key.getLOD(), key.getTileX(), key.getTileY(), haeProfile );
}
// Collect the valid layers for this tile.
LayerDataVector contenders;
LayerDataVector offsets;
// Track the number of layers that would return fallback data.
unsigned numFallbackLayers = 0;
// Check them in reverse order since the highest priority is last.
for (int i = size()-1; i>=0; --i)
//for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
{
ElevationLayer* layer = (*this)[i].get(); //i->get();
if ( layer->getEnabled() && layer->getVisible() )
{
// calculate the resolution-mapped key (adjusted for tile resolution differential).
TileKey mappedKey = keyToUse.mapResolution(
hf->getNumColumns(),
layer->getTileSize() );
bool useLayer = true;
TileKey bestKey( mappedKey );
// Check whether the non-mapped key is valid according to the user's min/max level settings:
if ( !layer->isKeyInLegalRange(key) )
{
useLayer = false;
}
// Find the "best available" mapped key from the tile source:
else
{
bestKey = layer->getBestAvailableTileKey(mappedKey);
if (bestKey.valid())
{
// If the bestKey is not the mappedKey, this layer is providing
// fallback data (data at a lower resolution than requested)
if ( mappedKey != bestKey )
{
numFallbackLayers++;
}
}
else
{
useLayer = false;
}
}
if ( useLayer )
{
if ( layer->isOffset() )
{
offsets.push_back(LayerData());
LayerData& ld = offsets.back();
ld.layer = layer;
ld.key = bestKey;
ld.index = i;
}
else
{
contenders.push_back(LayerData());
LayerData& ld = contenders.back();
ld.layer = layer;
ld.key = bestKey;
ld.index = i;
}
}
}
}
// nothing? bail out.
if ( contenders.empty() && offsets.empty() )
{
return false;
}
// if everything is fallback data, bail out.
if ( contenders.size() + offsets.size() == numFallbackLayers )
{
return false;
}
// Sample the layers into our target.
unsigned numColumns = hf->getNumColumns();
unsigned numRows = hf->getNumRows();
double xmin = key.getExtent().xMin();
double ymin = key.getExtent().yMin();
double dx = key.getExtent().width() / (double)(numColumns-1);
double dy = key.getExtent().height() / (double)(numRows-1);
#if 0
// If the incoming heightfield requests a positive border width,
// we need to adjust the extents so that we request data outside the
// extent of the tile key:
unsigned border = hf->getBorderWidth();
if (border > 0u)
{
dx = key.getExtent().width() / (double)(numColumns - (border*2+1));
dy = key.getExtent().height() / (double)(numRows - (border*2+1));
xmin -= dx * (double)border;
ymin -= dy * (double)border;
}
#endif
// We will load the actual heightfields on demand. We might not need them all.
#if 0
GeoHeightFieldVector heightFields(contenders.size());
GeoHeightFieldVector offsetFields(offsets.size());
std::vector<bool> heightFallback(contenders.size(), false);
std::vector<bool> heightFailed(contenders.size(), false);
std::vector<bool> offsetFailed(offsets.size(), false);
#else
GeoHeightFieldVector heightFields[9];
GeoHeightFieldVector offsetFields[9]; //(offsets.size());
std::vector<bool> heightFallback[9]; //(contenders.size(), false);
std::vector<bool> heightFailed[9]; //(contenders.size(), false);
std::vector<bool> offsetFailed[9]; //(offsets.size(), false);
for (int n = 0; n < 9; ++n)
{
heightFields[n].resize(contenders.size());
offsetFields[n].resize(offsets.size());
heightFallback[n].assign(9, false);
heightFailed[n].assign(9, false);
offsetFailed[n].assign(9, false);
}
#endif
// The maximum number of heightfields to keep in this local cache
unsigned int maxHeightFields = 50;
unsigned numHeightFieldsInCache = 0;
const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();
bool realData = false;
unsigned int total = numColumns * numRows;
// query resolution interval (x, y) of each sample.
osg::ref_ptr<osg::ShortArray> deltaLOD = new osg::ShortArray(total);
int nodataCount = 0;
TileKey scratchKey; // Storage if a new key needs to be constructed
for (unsigned c = 0; c < numColumns; ++c)
{
double x = xmin + (dx * (double)c);
for (unsigned r = 0; r < numRows; ++r)
{
double y = ymin + (dy * (double)r);
// Collect elevations from each layer as necessary.
int resolvedIndex = -1;
osg::Vec3 normal_sum(0,0,0);
for(int i=0; i<contenders.size() && resolvedIndex<0; ++i)
{
ElevationLayer* layer = contenders[i].layer.get();
TileKey& contenderKey = contenders[i].key;
int index = contenders[i].index;
// If there is a border, the edge points may not fall within the key extents
// and we may need to fetch a neighboring key.
int n = 4; // index 4 is the center/default tile
#if 0
if (border > 0u && !contenderKey.getExtent().contains(x, y))
{
int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
int dTy = y < contenderKey.getExtent().yMin() ? +1 : y > contenderKey.getExtent().yMax() ? -1 : 0;
contenderKey = contenderKey.createNeighborKey(dTx, dTy);
n = (dTy+1)*3 + (dTx+1);
}
#endif
if ( heightFailed[n][i] )
continue;
TileKey* actualKey = &contenderKey;
GeoHeightField& layerHF = heightFields[n][i];
if (!layerHF.valid())
{
// We couldn't get the heightfield from the cache, so try to create it.
// We also fallback on parent layers to make sure that we have data at the location even if it's fallback.
while (!layerHF.valid() && actualKey->valid() && layer->isKeyInLegalRange(*actualKey))
{
layerHF = layer->createHeightField(*actualKey, progress);
if (!layerHF.valid())
{
if (actualKey != &scratchKey)
{
scratchKey = *actualKey;
actualKey = &scratchKey;
}
*actualKey = actualKey->createParentKey();
}
}
// Mark this layer as fallback if necessary.
if (layerHF.valid())
{
heightFallback[n][i] = (*actualKey != contenderKey); // actualKey != contenders[i].second;
numHeightFieldsInCache++;
}
else
{
heightFailed[n][i] = true;
continue;
}
}
if (layerHF.valid())
{
bool isFallback = heightFallback[n][i];
// We only have real data if this is not a fallback heightfield.
if (!isFallback)
{
realData = true;
}
float elevation;
if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation))
{
if ( elevation != NO_DATA_VALUE )
{
// remember the index so we can only apply offset layers that
// sit on TOP of this layer.
resolvedIndex = index;
hf->setHeight(c, r, elevation);//从中间高程场构建最终的rex高程瓦片
if (deltaLOD)
{
(*deltaLOD)[r*numColumns + c] = key.getLOD() - actualKey->getLOD();
}
}
else
{
++nodataCount;
}
}
}
// Clear the heightfield cache if we have too many heightfields in the cache.
if (numHeightFieldsInCache >= maxHeightFields)
{
//OE_NOTICE << "Clearing cache" << std::endl;
for (unsigned int j = 0; j < 9; ++j)
{
for (unsigned int k = 0; k < heightFields[j].size(); k++)
{
heightFields[j][k] = GeoHeightField::INVALID;
heightFallback[j][k] = false;
}
}
numHeightFieldsInCache = 0;
}
}
for(int i=offsets.size()-1; i>=0; --i)
{
// Only apply an offset layer if it sits on top of the resolved layer
// (or if there was no resolved layer).
if (resolvedIndex >= 0 && offsets[i].index < resolvedIndex)
continue;
TileKey &contenderKey = offsets[i].key;
// If there is a border, the edge points may not fall within the key extents
// and we may need to fetch a neighboring key.
int n = 4; // index 4 is the center/default tile
#if 0
if (border > 0u && !contenderKey.getExtent().contains(x, y))
{
int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0;
int dTy = y < contenderKey.getExtent().yMin() ? +1 : x > contenderKey.getExtent().yMax() ? -1 : 0;
contenderKey = contenderKey.createNeighborKey(dTx, dTy);
n = (dTy+1)*3 + (dTx+1);
}
#endif
if ( offsetFailed[n][i] == true )
continue;
GeoHeightField& layerHF = offsetFields[n][i];
if ( !layerHF.valid() )
{
ElevationLayer* offset = offsets[i].layer.get();
layerHF = offset->createHeightField(contenderKey, progress);
if ( !layerHF.valid() )
{
offsetFailed[n][i] = true;
continue;
}
}
// If we actually got a layer then we have real data
realData = true;
float elevation = 0.0f;
if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) &&
elevation != NO_DATA_VALUE)
{
hf->getHeight(c, r) += elevation;
// Update the resolution tracker to account for the offset. Sadly this
// will wipe out the resolution of the actual data, and might result in
// normal faceting. See the comments on "createNormalMap" for more info
if (deltaLOD)
{
(*deltaLOD)[r*numColumns + c] = key.getLOD() - contenderKey.getLOD();
}
}
}
}
}
if (normalMap)
{
createNormalMap(key.getExtent(), hf, deltaLOD.get(), normalMap);
}
// Return whether or not we actually read any real data
return realData;
}
gdal判断外部瓦片和文件瓦片是否相交要用到下面的变换函数,如果不能正确变换会判断不相交。
gdal中OCTTransform( xform_handle, count, x, y, 0L )函数的含义是什么
gdal-2.2.2\ogr\ogrct.cpp
/************************************************************************/
/* OCTTransform() */
/************************************************************************/
/** Transform an array of points
*
* @param hTransform Transformation object
* @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.
* @return TRUE or FALSE
*/
int CPL_STDCALL OCTTransform( OGRCoordinateTransformationH hTransform,
int nCount, double *x, double *y, double *z )
{
VALIDATE_POINTER1( hTransform, "OCTTransform", FALSE );
return ((OGRCoordinateTransformation*) hTransform)->
Transform( nCount, x, y, z );
}
/************************************************************************/
/* OGRProj4CT */
/************************************************************************/
class OGRProj4CT : public OGRCoordinateTransformation
/************************************************************************/
/* Transform() */
/* */
/* This is a small wrapper for the extended transform version. */
/************************************************************************/
int OGRProj4CT::Transform( int nCount, double *x, double *y, double *z )
{
int *pabSuccess = static_cast<int *>(CPLMalloc(sizeof(int) * nCount));
bool bOverallSuccess =
CPL_TO_BOOL(TransformEx( nCount, x, y, z, pabSuccess ));
for( int i = 0; i < nCount; i++ )
{
if( !pabSuccess[i] )
{
bOverallSuccess = false;
break;
}
}
CPLFree( pabSuccess );
return bOverallSuccess;
}
/************************************************************************/
/* TransformEx() */
/************************************************************************/
/** 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 )
{
// Prevent any coordinate modification when possible
if ( bNoTransform )
{
if( pabSuccess )
{
for( int i = 0; i < nCount; i++ )
{
pabSuccess[i] = TRUE;
}
}
return TRUE;
}
// Workaround potential bugs in proj.4 such as
// the one of https://github.com/OSGeo/proj.4/commit/
// bc7453d1a75aab05bdff2c51ed78c908e3efa3cd
for( int i = 0; i < nCount; i++ )
{
if( CPLIsNan(x[i]) || CPLIsNan(y[i]) )
{
x[i] = HUGE_VAL;
y[i] = HUGE_VAL;
}
}
/* -------------------------------------------------------------------- */
/* 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 )
{
static const double REVERSE_SPHERE_RADIUS = 1.0 / 6378137.0;
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] = HUGE_VAL;
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] = HUGE_VAL;
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.0 -
2.0 * 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);
}
int err = 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 = static_cast<double*>(
CPLRealloc(padfOriX, sizeof(double) * nCount));
padfOriY = static_cast<double*>(
CPLRealloc(padfOriY, sizeof(double)*nCount));
padfOriZ = static_cast<double*>(
CPLRealloc(padfOriZ, sizeof(double)*nCount));
padfTargetX = static_cast<double*>(
CPLRealloc(padfTargetX, sizeof(double)*nCount));
padfTargetY = static_cast<double*>(
CPLRealloc(padfTargetY, sizeof(double)*nCount));
padfTargetZ = static_cast<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 errors. */
/* 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( m_bEmitErrors && ++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;
}
gdal-2.2.2\ogr\ogr_spatialref.h
/************************************************************************/
/* OGRCoordinateTransformation */
/* */
/* This is really just used as a base class for a private */
/* implementation. */
/************************************************************************/
/**
* Interface for transforming between coordinate systems.
*
* Currently, the only implementation within OGR is OGRProj4CT, which
* requires the PROJ.4 library to be available at run-time.
*
* Also, see OGRCreateCoordinateTransformation() for creating transformations.
*/
class CPL_DLL OGRCoordinateTransformation
{}
待继续分析列表:
9、earth文件中都有哪些options((九)中问题)
10、如何根据earth文件options创建不同的地理信息引擎节点((九)中问题)
11、rex地理信息引擎的四梁八柱((九)中问题)
12、osgEarth::TerrainEngineNode中setMap方法作用((十二)中问题)
13、RexTerrainEngineNode中_mapFrame的作用((十二)中问题)
14、地形变形(Terrain morphing)((十二)中问题)
15、地球瓦片过期门限的含义((十二)中问题)
16、高分辨率优先的含义((十二)中问题)
17、OSGEARTH_DEBUG_NORMALS环境变量的作用((十二)中问题)
18、活跃瓦片寄存器的作用((十二)中问题)
19、资源释放器子节点的作用((十二)中问题)
20、共享几何图形池子节点的作用((十二)中问题)
21、分页瓦片加载器子节点的作用((十二)中问题)
22、分页瓦片卸载器子节点的作用((十二)中问题)
23、栅格化器子节点的作用((十二)中问题)
24、地形子节点的作用((十二)中问题)
25、绑定渲染器的作用((十二)中问题)
26、地图回调函数的作用((十二)中问题)
27、如何将地图图层添加到rex引擎中((十二)中问题)
28、选择信息的作用((十二)中问题)
29、瓦片包围盒修改回调函数的作用((十二)中问题)
30、刷新rex引擎((十二)中问题)
31、刷新边界作用((十二)中问题)
32、osgEarth::Metrics类的意义((十四)中问题)
33、请求合并队列_mergeQueue((十四)中问题)
34、分页瓦片加载器在更新遍历时对请求处理过程((十四)中问题)
35、分页瓦片加载器在更新遍历时对已处理请求裁剪过程((十四)中问题)
36、已处理的请求队列_requests((十四)中问题)
37、DatabasePager中的_fileRequestQueue和_httpRequestQueue((十六)中问题)
38、瓦片请求的生成到处理过程详解((十六)中问题)
39、瓦片节点TileNode的创建过程((十七)中问题)
40、request请求加载瓦片优先级的含义((十七)中问题)
41、request的_internalHandle的作用((十七)中问题)
42、DatabaseRequest中_objectCache含义((十七)中问题)
42、osgEarth的多线程分析((十七)中问题)
43、osgEarth的缓存及其结构((十七)中问题)
44、DatabaseThread从缓存加载数据过程((十七)中问题)
45、DatabaseThread从文件加载数据过程((十七)中问题)
46、决定创建TileNode的时机条件((十七)中问题)
47、TerrainEngineNode的createTileModel过程详解((十七)中问题)
48、DatabaseThread中CompileSet的含义((十七)中问题)
48、PagerLoader的traverse过程详解((十七)中问题)
49、DatabaseThread的run过程详解((十七)中问题)
50、LoadTileData的invoke过程详解((十七)中问题)
51、TileNode的cull过程详解((十七)中问题)
52、遮罩生成器osgEarth::Drivers::RexTerrainEngine::MaskGenerator((十八)中问题)
53、RexTerrainEngineNode::traverse过程详解((十八)中问题)
54、TileNode节点下的场景树分析((十八)中问题)
55、地形瓦片大小尺寸和LOD的关系((十八)中问题)
56、TileNode的_tileKeyValue作用((十八)中问题)
57、TileNode的_morphConstants作用((十八)中问题)
58、TileNode的_stitchNormalMap作用((十八)中问题)
59、TileNode的_renderModel作用((十八)中问题)
60、初始化高程栅格过程详解((十八)中问题)
61、LoadTileData中的CreateTileModelFilter作用((十八)中问题)
62、TileNode节点何时会从场景树中移除((十八)中问题)
63、osgEarth::Map的Profile创建过程((二十)中问题)
64、osgEarth::TerrainTileModelFactory添加颜色层和影像层的区别((二十一)中问题)
65、osgEarth::PatchLayer修补层的作用((二十一)中问题)
66、osgEarth::TerrainLayer中的_memCache(osgEarth::MemCache)详解((二十一)中问题)
67、osgEarth::Layer::RenderType图层渲染类型的作用((二十一)中问题)
68、osgEarth::TerrainLayer中TileSource的作用((二十一)中问题)
69、earth文件没有设置高程图层会不会有默认高程层(高程均为0)((二十一)中问题)
70、TerrainTileModelFactory::addColorLayers过程详解((二十一)中问题)
71、TerrainTileModelFactory::addElevation过程详解((二十一)中问题)
72、osgearth中可能用到的几个全局实例对象(osgDB::Registry osgEarth::Registry osg::Timer osg::DisplaySetting)((二十三)中问题)
73、osgEarth::Map::addLayer过程详解((二十三)中问题)
74、TileNode::setDirty过程详解((二十三)中问题)
75、请求四个状态的含义(IDLE RUNNING MERGING FINISHED)((二十三)中问题)
76、什么时候删除TileNode节点,不会一直增加吧((二十三)中问题)
77、寄存器中请求状态活动记录的含义Registry::instance()->endActivity( req->getName() )((二十三)中问题)
78、瓦片TileNode的生命周期流程详解((二十三)中问题)
79、rex引擎如何将瓦片构造成地球形状((二十五)中问题)
80、高程、影像文件格式详解((二十五)中问题)
81、TileNode的merge过程详解((二十六)中问题)
82、osgEarth支持的空间参考坐标系详解(osgEarth::SpatialReference、osgEarth::CubeSpatialReference、osgEarth::TangentPlaneSpatialReference)((二十九)中问题)
83、osgEarth地球椭球体ellipsoid 大地基准面datum 地图投影Projection详解((二十九)中问题)
84、空间参考坐标系和坐标系统类型的关系(geocentric projected)((二十九)中问题)
85、proj4是什么((二十九)中问题)
86、为什么要删除设置过的垂直水准面((二十九)中问题)
87、osgEarth如何对投影坐标系和大地坐标系进行显示处理的((二十九)中问题)
88、TileNode的节点构成,一个surface、tilenode((三十)中问题)
89、MapFram和MapInfo的关系((三十)中问题)
90、ModifyBoundingBoxCallback的使用时机和场合((三十)中问题)
91、MapFrame为什么要单独存放高程层_elevationLayers,而不是放在图层_layers中((三十)中问题)
92、MapFrame和Map中高程池的作用osg::ref_ptr<ElevationPool> _elevationPool((三十)中问题)
93、osgEarth::Drivers::RexTerrainEngine::TileDrawable分析((三十)中问题)
94、请求读取地理信息失败会如何处理((三十二)中问题)
95、RexTerrainEngineNode的遍历过程详解((三十三)中问题)
96、osgEarth::Drivers::RexTerrainEngine::TerrainCuller的apply过程详解((三十三)中问题)
97、RexTerrainEngineNode的updateState过程详解 设置了很多着色器变量((三十三)中问题)
98、什么时候分配opengl资源((三十三)中问题)
99、TileNode释放opengl资源过程releaseGLObjects详解((三十三)中问题)
100、最近一次遍历的帧号和时间是怎么设置呢(在渲染遍历里),怎么就不会再渲染遍历该瓦片节点了((三十三)中问题)
101、osg::State和osg::StateSet的关系((三十四)中问题)
102、osgEarth::SpatialReference和osgEarth::Profile的关系((三十六)中问题)
103、osgEarth的Geographic、Geodetic、Geocentric和Project的关系((三十六)中问题)
104、TileNode绘制过程详解((三十七)中问题)
105、如何控制父子TileNode节点的显隐((三十七)中问题)
106、GeometryPool的createGeometry过程详解((三十七)中问题)
107、TileNode如何从地图中提取与其分辨率相适应的图像数据((三十七)中问题)
108、如何定制椭球体并进行椭球体间坐标转换((四十五)中问题)
109、Horizon Cull是什么意思((四十五)中问题)
110、osgEarth::Drivers::RexTerrainEngine::DrawState的作用((四十五)中问题)
111、osgEarth的线程分析((四十五)中问题)
112、从osgEarth到osg到Opengl((四十五)中问题)
113、osg::Program与osgEarth::VirtualProgram的关系((四十五)中问题)
114、rex引擎shader文件中的#pragma vp_entryPoint vp_location等含义((四十五)中问题)
115、rex引擎的着色器如何区分顶点和片段((四十五)中问题)
116、osg::Program是如何对着色器及其变量进行管理的((四十五)中问题)
117、osg的窗口是如何与opengl集成的((四十五)中问题)
118、osg是如何实现opengl的初始化的((四十五)中问题)
119、CGCS2000余WGS84坐标系的比较((四十六)中问题)
120、着色器代码文件到着色器程序的过程((五十一)中问题)
121、osgEarth::VirtualProgram默认出现在哪些位置((五十一)中问题)
122、rex引擎默认的几个着色器功能分析((五十一)中问题)
123、osgEarth::TileRasterizer功能详解((五十二)中问题)
124、osgEarth::ImageLayer如何使用VirtualProgram((五十二)中问题)
125、osgEarth::ShaderFactory osgEarth::ShaderLoader关系((五十四)中问题)
126、osgEarth::URI和osgEarth::URIContext的作用((五十四)中问题)
127、RexTerrainEngineNode中_renderBindings的作用((五十四)中问题)
128、Rex引擎如何给shader文件中的uniform变量赋值((五十四)中问题)
129、osgEarth中多个着色器的源代码的编译链接过程((五十四)中问题)
130、osgEarth::ShaderFactory osgEarth::ShaderLoader关系((五十四)中问题)
131、TileNode与DrawTileCommand的关系((五十五)中问题)
132、如何提取出指定范围的高程网格((五十五)中问题)
133、从earth文件加载高层图层的过程((五十五)中问题)
134、TerrainTileModel与TileRenderModel的关系((五十五)中问题)
135、EngineContext的作用((五十五)中问题)
136、几个uniformmap的关系((五十五)中问题)
137、DrawTileCommand中的采样器((五十五)中问题)
138、TileNode中的_surface(SurfaceNode)作用是什么((五十五)中问题)
139、stateset中的adduniform、setTextureAttribute等最后是如何反应到opengl上的((五十五)中问题)
140、状态树和渲染树的关系((五十五)中问题)
141、TileRenderModel中的RenderingPass和RenderBindings((五十五)中问题)
142、高程瓦片的绘制过程((五十五)中问题)
143、如何从高程影像变成高程网格((七十一)中问题)
144、osg::StateSet中的_binMode作用((七十二)中问题)
145、rex的瓦片高程影像和高程文件中的影像尺寸如何对应((七十二)中问题)
146、osgEarth::TerrainLayerOptions高程层选项中参数的含义((七十二)中问题)
147、从高程文件读取的高程信息如何填充rex的高程瓦片((七十二)中问题)
148、地图下载器实现原理((七十二)中问题)