osgEarth的Rex引擎原理分析(七十三)从高程文件读取的高程信息如何填充rex的高程瓦片

目标:(七十二)中的问题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、地图下载器实现原理((七十二)中问题)

发布了388 篇原创文章 · 获赞 36 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/hankern/article/details/104258951