コード例 #1
0
    /**
    * Gets or creates the heightfields that would be effected by the given deformation
    */
    void getOrCreateHeightFieldsForDeformation(const Deformation& deformation, unsigned int level, HeightFieldMap& results)
    {
        OpenThreads::ScopedLock< OpenThreads::Mutex > lk(_mutex);

        // Get the extent of the deformation feature.
        GeoExtent extent(deformation._feature->getSRS(), deformation._feature->getGeometry()->getBounds());

        TileKey ll = getProfile()->createTileKey(extent.xMin(), extent.yMin(), level);
        TileKey ur = getProfile()->createTileKey(extent.xMax(), extent.yMax(), level);        

        for (unsigned int c = ll.getTileX(); c <= ur.getTileX(); c++)
        {
            for (unsigned int r = ur.getTileY(); r <= ll.getTileY(); r++)
            {
                TileKey key(level, c, r, getProfile());

                osg::ref_ptr< osg::HeightField > hf;

                HeightFieldMap::iterator itr = _heightfields.find( key );
                if (itr == _heightfields.end())
                {
                    //Allocate a new heightfield
                    hf = new osg::HeightField;
                    hf->allocate(getPixelsPerTile(), getPixelsPerTile());
                    for (unsigned int i = 0; i < hf->getHeightList().size(); ++i) hf->getHeightList()[i] = NO_DATA_VALUE;
                    _heightfields[ key ] = hf.get();
                    results[ key ] = hf.get();
                }
                else
                {                
                    results[ key ] = itr->second.get();
                }
            }
        }
    }
コード例 #2
0
unsigned int
CacheEstimator::getNumTiles() const
{
    unsigned int total = 0;

    for (unsigned int level = _minLevel; level <= _maxLevel; level++)
    {
        if (_extents.empty())
        {
            unsigned int wide, high;
            _profile->getNumTiles( level, wide, high );
            total += (wide * high);
        }
        else
        {
            for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); ++itr)
            {
                const GeoExtent& extent = *itr;

                TileKey ll = _profile->createTileKey(extent.xMin(), extent.yMin(), level);
                TileKey ur = _profile->createTileKey(extent.xMax(), extent.yMax(), level);

                if (!ll.valid() || !ur.valid()) continue;
                
                int tilesWide = ur.getTileX() - ll.getTileX() + 1;
                int tilesHigh = ll.getTileY() - ur.getTileY() + 1;
                int tilesAtLevel = tilesWide * tilesHigh;                
                total += tilesAtLevel;
            }
        }
    }
    return total;
}
コード例 #3
0
ファイル: MPGeometry.cpp プロジェクト: DavidLeehome/osgearth
MPGeometry::MPGeometry(const TileKey& key, const MapFrame& frame, int imageUnit) : 
osg::Geometry    ( ),
_frame           ( frame ),
_imageUnit       ( imageUnit )
{
    _supportsGLSL = Registry::capabilities().supportsGLSL();

    unsigned tw, th;
    key.getProfile()->getNumTiles(key.getLOD(), tw, th);
    _tileKeyValue.set( key.getTileX(), th-key.getTileY()-1.0f, key.getLOD(), -1.0f );

    _imageUnitParent = _imageUnit + 1; // temp

    // establish uniform name IDs.
    _tileKeyUniformNameID      = osg::Uniform::getNameID( "oe_tile_key" );
    _birthTimeUniformNameID    = osg::Uniform::getNameID( "oe_tile_birthtime" );
    _uidUniformNameID          = osg::Uniform::getNameID( "oe_layer_uid" );
    _orderUniformNameID        = osg::Uniform::getNameID( "oe_layer_order" );
    _opacityUniformNameID      = osg::Uniform::getNameID( "oe_layer_opacity" );
    _texMatParentUniformNameID = osg::Uniform::getNameID( "oe_layer_parent_matrix" );

    // we will set these later (in TileModelCompiler)
    this->setUseVertexBufferObjects(false);
    this->setUseDisplayList(false);
}
コード例 #4
0
    FeatureCursor* createFeatureCursor( const Symbology::Query& query )
    {
        TileKey key = *query.tileKey();

        int z = key.getLevelOfDetail();
        int tileX = key.getTileX();
        int tileY = key.getTileY();

        unsigned int numRows, numCols;
        key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows);
        tileY  = numRows - tileY - 1;

        //Get the image
        sqlite3_stmt* select = NULL;
        std::string queryStr = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?";
        int rc = sqlite3_prepare_v2( _database, queryStr.c_str(), -1, &select, 0L );
        if ( rc != SQLITE_OK )
        {
            OE_WARN << LC << "Failed to prepare SQL: " << queryStr << "; " << sqlite3_errmsg(_database) << std::endl;
            return NULL;
        }

        bool valid = true;        

        sqlite3_bind_int( select, 1, z );
        sqlite3_bind_int( select, 2, tileX );
        sqlite3_bind_int( select, 3, tileY );

        rc = sqlite3_step( select );

        FeatureList features;

        if ( rc == SQLITE_ROW)
        {                     
            // the pointer returned from _blob gets freed internally by sqlite, supposedly
            const char* data = (const char*)sqlite3_column_blob( select, 0 );
            int dataLen = sqlite3_column_bytes( select, 0 );
            std::string dataBuffer( data, dataLen );
            std::stringstream in(dataBuffer);
            MVT::read(in, key, features);
        }
        else
        {
            OE_DEBUG << LC << "SQL QUERY failed for " << queryStr << ": " << std::endl;
            valid = false;
        }

        sqlite3_finalize( select );

        // apply filters before returning.
        applyFilters( features );

        if (!features.empty())
        {
            //OE_NOTICE << "Returning " << features.size() << " features" << std::endl;
            return new FeatureListCursor(features);
        }

        return 0;
    }
コード例 #5
0
ファイル: TMSBackFiller.cpp プロジェクト: JD31/osgearth
void TMSBackFiller::process( const std::string& tms, osgDB::Options* options )
{               
    std::string fullPath = getFullPath( "", tms );        
    _options = options;

    //Read the tilemap
    _tileMap = TileMapReaderWriter::read( fullPath, 0 );
    if (_tileMap)
    {                        
        //The max level is where we are going to read data from, so we need to start one level up.
        osg::ref_ptr< const Profile> profile = _tileMap->createProfile();           

        //If the bounds aren't valid just use the full extent of the profile.
        if (!_bounds.valid())
        {                
            _bounds = profile->getExtent().bounds();
        }


        int firstLevel = _maxLevel-1;            

        GeoExtent extent( profile->getSRS(), _bounds );           

        //Process each level in it's entirety
        for (int level = firstLevel; level >= static_cast<int>(_minLevel); level--)
        {
            if (_verbose) OE_NOTICE << "Processing level " << level << std::endl;                

            TileKey ll = profile->createTileKey(extent.xMin(), extent.yMin(), level);
            TileKey ur = profile->createTileKey(extent.xMax(), extent.yMax(), level);

            for (unsigned int x = ll.getTileX(); x <= ur.getTileX(); x++)
            {
                for (unsigned int y = ur.getTileY(); y <= ll.getTileY(); y++)
                {
                    TileKey key = TileKey(level, x, y, profile.get());
                    processKey( key );
                }
            }                

        }            
    }
    else
    {
        OE_NOTICE << "Failed to load TileMap from " << _tmsPath << std::endl;
    }
}
コード例 #6
0
ファイル: MPGeometry.cpp プロジェクト: rhabacker/osgearth
MPGeometry::MPGeometry(const TileKey& key, const MapFrame& frame, int imageUnit) : 
osg::Geometry    ( ),
_frame           ( frame ),
_imageUnit       ( imageUnit ),
_uidUniformNameID(0),
_birthTimeUniformNameID(0u),
_orderUniformNameID(0u),
_opacityUniformNameID(0u),
_texMatParentUniformNameID(0u),
_tileKeyUniformNameID(0u),
_minRangeUniformNameID(0u),
_maxRangeUniformNameID(0u),
_imageUnitParent(0),
_elevUnit(0),
_supportsGLSL(false)
{
    _supportsGLSL = Registry::capabilities().supportsGLSL();
    
    // Encode the tile key in a uniform. Note! The X and Y components are presented
    // modulo 2^16 form so they don't overrun single-precision space.
    unsigned tw, th;
    key.getProfile()->getNumTiles(key.getLOD(), tw, th);

    const double m = pow(2.0, 16.0);

    double x = (double)key.getTileX();
    double y = (double)(th - key.getTileY()-1);

    _tileKeyValue.set(
        (float)fmod(x, m),
        (float)fmod(y, m),
        (float)key.getLOD(),
        -1.0f);

    _imageUnitParent = _imageUnit + 1; // temp

    _elevUnit = _imageUnit + 2; // temp

    // establish uniform name IDs.
    _tileKeyUniformNameID      = osg::Uniform::getNameID( "oe_tile_key" );
    _birthTimeUniformNameID    = osg::Uniform::getNameID( "oe_tile_birthtime" );
    _uidUniformNameID          = osg::Uniform::getNameID( "oe_layer_uid" );
    _orderUniformNameID        = osg::Uniform::getNameID( "oe_layer_order" );
    _opacityUniformNameID      = osg::Uniform::getNameID( "oe_layer_opacity" );
    _texMatParentUniformNameID = osg::Uniform::getNameID( "oe_layer_parent_texmat" );
    _minRangeUniformNameID     = osg::Uniform::getNameID( "oe_layer_minRange" );
    _maxRangeUniformNameID     = osg::Uniform::getNameID( "oe_layer_maxRange" );

    // we will set these later (in TileModelCompiler)
    this->setUseDisplayList(false);
    this->setUseVertexBufferObjects(true);
}
コード例 #7
0
MPGeometry::MPGeometry(const TileKey& key, const MapFrame& frame, int imageUnit) : 
osg::Geometry    ( ),
_frame           ( frame ),
_imageUnit       ( imageUnit )
{
    unsigned tw, th;
    key.getProfile()->getNumTiles(key.getLOD(), tw, th);
    _tileKeyValue.set( key.getTileX(), th-key.getTileY()-1.0f, key.getLOD(), -1.0f );

    _imageUnitParent = _imageUnit + 1; // temp

    // establish uniform name IDs.
    _tileKeyUniformNameID      = osg::Uniform::getNameID( "oe_tile_key" );
    _uidUniformNameID          = osg::Uniform::getNameID( "oe_layer_uid" );
    _orderUniformNameID        = osg::Uniform::getNameID( "oe_layer_order" );
    _opacityUniformNameID      = osg::Uniform::getNameID( "oe_layer_opacity" );
    _texMatParentUniformNameID = osg::Uniform::getNameID( "oe_layer_parent_matrix" );

    // Temporary solution to the OverlayDecorator techniques' inappropriate setting of
    // uniform values during the CULL traversal, which causes corruption of the RTT 
    // camera matricies when DRAW overlaps the next frame's CULL. Please see my comments
    // in DrapingTechnique.cpp for more information.
    this->setDataVariance( osg::Object::DYNAMIC );
}
コード例 #8
0
ファイル: Cube.cpp プロジェクト: emminizer/osgearth
int
UnifiedCubeProfile::getFace( const TileKey& key )
{
    return key.getTileX() >> key.getLevelOfDetail();
}
コード例 #9
0
bool 
MBTilesTileSource::storeImage(const TileKey&    key,
                              osg::Image*       image,
                              ProgressCallback* progress)
{
    if ( (getMode() & MODE_WRITE) == 0 )
        return false;

    Threading::ScopedMutexLock exclusiveLock(_mutex);

    // encode the data stream:
    std::stringstream buf;
    osgDB::ReaderWriter::WriteResult wr;
    if ( _forceRGB && ImageUtils::hasAlphaChannel(image) )
    {
        osg::ref_ptr<osg::Image> rgb = ImageUtils::convertToRGB8(image);
        wr = _rw->writeImage(*(rgb.get()), buf, _dbOptions.get());
    }
    else
    {
        wr = _rw->writeImage(*image, buf, _dbOptions.get());
    }

    if ( wr.error() )
    {
        OE_WARN << LC << "Image encoding failed: " << wr.message() << std::endl;
        return false;
    }

    std::string value = buf.str();
    
    // compress if necessary:
    if ( _compressor.valid() )
    {
        std::ostringstream output;
        if ( !_compressor->compress(output, value) )
        {
            OE_WARN << LC << "Compressor failed" << std::endl;
            return false;
        }
        value = output.str();
    }

    int z = key.getLOD();
    int x = key.getTileX();
    int y = key.getTileY();

    // flip Y axis
    unsigned int numRows, numCols;
    key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows);
    y  = numRows - y - 1;

    // Prep the insert statement:
    sqlite3_stmt* insert = NULL;
    std::string query = "INSERT OR REPLACE INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (?, ?, ?, ?)";
    int rc = sqlite3_prepare_v2( _database, query.c_str(), -1, &insert, 0L );
    if ( rc != SQLITE_OK )
    {
        OE_WARN << LC << "Failed to prepare SQL: " << query << "; " << sqlite3_errmsg(_database) << std::endl;
        return false;
    }

    // bind parameters:
    sqlite3_bind_int( insert, 1, z );
    sqlite3_bind_int( insert, 2, x );
    sqlite3_bind_int( insert, 3, y );

    // bind the data blob:
    sqlite3_bind_blob( insert, 4, value.c_str(), value.length(), SQLITE_STATIC );

    // run the sql.
    bool ok = true;
    int tries = 0;
    do {
        rc = sqlite3_step(insert);
    }
    while (++tries < 100 && (rc == SQLITE_BUSY || rc == SQLITE_LOCKED));

    if (SQLITE_OK != rc && SQLITE_DONE != rc)
    {
#if SQLITE_VERSION_NUMBER >= 3007015
        OE_WARN << LC << "Failed query: " << query << "(" << rc << ")" << sqlite3_errstr(rc) << "; " << sqlite3_errmsg(_database) << std::endl;
#else
        OE_WARN << LC << "Failed query: " << query << "(" << rc << ")" << rc << "; " << sqlite3_errmsg(_database) << std::endl;
#endif        
        ok = false;
    }

    sqlite3_finalize( insert );

    return ok;
}
コード例 #10
0
    FeatureCursor* createFeatureCursor( const Symbology::Query& query )
    {
        TileKey key = *query.tileKey();

        int z = key.getLevelOfDetail();
        int tileX = key.getTileX();
        int tileY = key.getTileY();

        unsigned int numRows, numCols;
        key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows);
        tileY  = numRows - tileY - 1;

        //Get the image
        sqlite3_stmt* select = NULL;
        std::string queryStr = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?";
        int rc = sqlite3_prepare_v2( _database, queryStr.c_str(), -1, &select, 0L );
        if ( rc != SQLITE_OK )
        {
            OE_WARN << LC << "Failed to prepare SQL: " << queryStr << "; " << sqlite3_errmsg(_database) << std::endl;
            return NULL;
        }

        bool valid = true;        

        sqlite3_bind_int( select, 1, z );
        sqlite3_bind_int( select, 2, tileX );
        sqlite3_bind_int( select, 3, tileY );

        rc = sqlite3_step( select );

        FeatureList features;

        if ( rc == SQLITE_ROW)
        {                     
            // the pointer returned from _blob gets freed internally by sqlite, supposedly
            const char* data = (const char*)sqlite3_column_blob( select, 0 );
            int dataLen = sqlite3_column_bytes( select, 0 );

            std::string dataBuffer( data, dataLen );

            // decompress if necessary:
            if ( _compressor.valid() )
            {
                std::istringstream inputStream(dataBuffer);
                std::string value;
                if ( !_compressor->decompress(inputStream, value) )
                {
                    OE_WARN << LC << "Decompression failed" << std::endl;
                    valid = false;
                }
                else
                {
                    dataBuffer = value;
                }
            }

            
            
            mapnik::vector::tile tile;

            if (tile.ParseFromString(dataBuffer))
            {
                // Get the layer in question
                for (unsigned int i = 0; i < tile.layers().size(); i++)
                {
                    const mapnik::vector::tile_layer &layer = tile.layers().Get(i);

                    //OE_NOTICE << layer.name() << std::endl;
                    //if (layer.name() != "road") continue;
                    //if (layer.name() != "building") continue;

                    for (unsigned int j = 0; j < layer.features().size(); j++)
                    {
                        const mapnik::vector::tile_feature &feature = layer.features().Get(j);

                        osg::ref_ptr< osgEarth::Symbology::Geometry > geometry; 

                        eGeomType geomType = static_cast<eGeomType>(feature.type());
                        if (geomType == ::Polygon)
                        {
                            //OE_NOTICE << "Polygon " << std::endl;
                            geometry = new osgEarth::Symbology::Polygon();
                        }
                        else if (geomType == ::LineString)
                        {
                            //OE_NOTICE << "LineString" << std::endl;
                            geometry = new osgEarth::Symbology::LineString();
                        }
                        else if (geomType == ::Point)
                        {
                            //OE_NOTICE << "Point" << std::endl;
                            geometry = new osgEarth::Symbology::PointSet();
                        }
                        else
                        {
                            //OE_NOTICE << "uknown" << std::endl;
                            geometry = new osgEarth::Symbology::LineString();
                        }

                        osg::ref_ptr< Feature > oeFeature = new Feature(geometry, key.getProfile()->getSRS());
                        features.push_back(oeFeature.get());                    

                        // Read attributes
                        for (unsigned int k = 0; k < feature.tags().size(); k+=2)
                        {
                            std::string key = layer.keys().Get(feature.tags().Get(k));
                            mapnik::vector::tile_value value = layer.values().Get(feature.tags().Get(k+1));

                            if (value.has_bool_value())
                            {
                                oeFeature->set(key, value.bool_value());
                            }
                            else if (value.has_double_value())
                            {
                                oeFeature->set(key, value.double_value());
                            }
                            else if (value.has_float_value())
                            {
                                oeFeature->set(key, value.float_value());
                            }
                            else if (value.has_int_value())
                            {
                                oeFeature->set(key, (int)value.int_value());
                            }
                            else if (value.has_sint_value())
                            {
                                oeFeature->set(key, (int)value.sint_value());
                            }
                            else if (value.has_string_value())
                            {
                                oeFeature->set(key, value.string_value());
                            }
                            else if (value.has_uint_value())
                            {
                                oeFeature->set(key, (int)value.uint_value());
                            }

                            // Special path for getting heights from our test dataset.
                            if (key == "other_tags")
                            {
                                std::string other_tags = value.string_value();
                                
                                StringTokenizer tok("=>");
                                StringVector tized;
                                tok.tokenize(other_tags, tized);            
                                if (tized.size() == 3)
                                {
                                    if (tized[0] == "height")
                                    {
                                        std::string value = tized[2];
                                        // Remove quotes from the height
                                        float height = as<float>(value, FLT_MAX);
                                        if (height != FLT_MAX)
                                        {
                                            oeFeature->set("height", height);                                            
                                        }
                                    }
                                }
                            }
                        }
                        
                        
                        unsigned int length = 0;
                        int cmd = -1;
                        const int cmd_bits = 3;

                        unsigned int tileres = layer.extent();

                        int x = 0;
                        int y = 0;

                        for (int k = 0; k < feature.geometry_size();)
                        {
                            if (!length)
                            {
                                unsigned int cmd_length = feature.geometry(k++);
                                cmd = cmd_length & ((1 << cmd_bits) - 1);
                                length = cmd_length >> cmd_bits;
                            } 
                            if (length > 0)
                            {
                                length--;
                                if (cmd == SEG_MOVETO || cmd == SEG_LINETO)
                                {
                                    int px = feature.geometry(k++);
                                    int py = feature.geometry(k++);
                                    px = zig_zag_decode(px);
                                    py = zig_zag_decode(py);

                                    x += px;
                                    y += py;

                                    double width = key.getExtent().width();
                                    double height = key.getExtent().height();

                                    double geoX = key.getExtent().xMin() + (width/(double)tileres) * (double)x;
                                    double geoY = key.getExtent().yMax() - (height/(double)tileres) * (double)y;
                                    geometry->push_back(geoX, geoY, 0);
                                }
                                else if (cmd == (SEG_CLOSE & ((1 << cmd_bits) - 1)))
                                {
                                    geometry->push_back(geometry->front());
                                }
                            }
                        }

                        if (geometry->getType() == Geometry::TYPE_POLYGON)
                        {
                            geometry->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CCW);                                               
                        }
                    }
                }
            }
            else
            {
コード例 #11
0
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;

#ifdef ANALYZE
    struct LayerAnalysis {
        LayerAnalysis() : samples(0), used(false), failed(false), fallback(false), actualKeyValid(true) { }
        int samples; bool used; bool failed; bool fallback; bool actualKeyValid; std::string message;
    };
    std::map<ElevationLayer*, LayerAnalysis> layerAnalysis;
#endif

    // 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;
                }

#ifdef ANALYZE
                layerAnalysis[layer].used = true;
#endif
            }
        }
    }

    // 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);
   
    // We will load the actual heightfields on demand. We might not need them all.
    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);

    // The maximum number of heightfields to keep in this local cache
    const unsigned 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

    bool requiresResample = true;

    // If we only have a single contender layer, and the tile is the same size as the requested 
    // heightfield then we just use it directly and avoid having to resample it
    if (contenders.size() == 1 && offsets.empty())
    {
        ElevationLayer* layer = contenders[0].layer.get();
        TileKey& contenderKey = contenders[0].key;

        GeoHeightField layerHF = layer->createHeightField(contenderKey, 0);
        if (layerHF.valid())
        {
            if (layerHF.getHeightField()->getNumColumns() == hf->getNumColumns() &&
                layerHF.getHeightField()->getNumRows() == hf->getNumRows())
            {
                requiresResample = false;
                memcpy(hf->getFloatArray()->asVector().data(),
                    layerHF.getHeightField()->getFloatArray()->asVector().data(),
                    sizeof(float) * hf->getFloatArray()->size()
                );
                deltaLOD->resize(hf->getFloatArray()->size(), 0);
                realData = true;
            }
        }
    }

    // If we need to mosaic multiple layers or resample it to a new output tilesize go through a resampling loop.
    if (requiresResample)
    {
        for (unsigned c = 0; c < numColumns; ++c)
        {
            double x = xmin + (dx * (double)c);

            // periodically check for cancelation
            if (progress && progress->isCanceled())
            {
                return false;
            }

            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 (heightFailed[i])
                        continue;

                    TileKey* actualKey = &contenderKey;

                    GeoHeightField& layerHF = heightFields[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[i] = (*actualKey != contenderKey); // actualKey != contenders[i].second;
                            numHeightFieldsInCache++;
                        }
                        else
                        {
                            heightFailed[i] = true;
#ifdef ANALYZE
                            layerAnalysis[layer].failed = true;
                            layerAnalysis[layer].actualKeyValid = actualKey->valid();
                            if (progress) layerAnalysis[layer].message = progress->message();
#endif
                            continue;
                        }
                    }

                    if (layerHF.valid())
                    {
                        bool isFallback = heightFallback[i];
#ifdef ANALYZE
                        layerAnalysis[layer].fallback = isFallback;
#endif

                        // 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);

#ifdef ANALYZE
                                layerAnalysis[layer].samples++;
#endif

                                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 k = 0; k < heightFields.size(); k++)
                        {
                            heightFields[k] = GeoHeightField::INVALID;
                            heightFallback[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 (offsetFailed[i] == true)
                        continue;

                    GeoHeightField& layerHF = offsetFields[i];
                    if (!layerHF.valid())
                    {
                        ElevationLayer* offset = offsets[i].layer.get();

                        layerHF = offset->createHeightField(contenderKey, progress);
                        if (!layerHF.valid())
                        {
                            offsetFailed[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)
    {
        // periodically check for cancelation
        if (progress && progress->isCanceled())
        {
            return false;
        }

        createNormalMap(key.getExtent(), hf, deltaLOD.get(), normalMap);
    }

#ifdef ANALYZE
    {
        static Threading::Mutex m;
        Threading::ScopedMutexLock lock(m);
        std::cout << key.str() << ": ";
        for (std::map<ElevationLayer*, LayerAnalysis>::const_iterator i = layerAnalysis.begin();
            i != layerAnalysis.end(); ++i)
        {
            std::cout << i->first->getName() 
                << " used=" << i->second.used
                << " failed=" << i->second.failed
                << " akv=" << i->second.actualKeyValid
                << " fallback=" << i->second.fallback
                << " samples=" << i->second.samples
                << " msg=" << i->second.message
                << "; ";
        }
        std::cout << std::endl;
    }
#endif

    if (progress && progress->isCanceled())
    {
        return false;
    }

    // Return whether or not we actually read any real data
    return realData;
}
コード例 #12
0
ファイル: CacheSeed.cpp プロジェクト: codingless/osgearth
void CacheSeed::seed( Map* map )
{
    if ( !map->getCache() )
    {
        OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl;
        return;
    }

    std::vector<TileKey> keys;
    map->getProfile()->getRootKeys(keys);

    //Add the map's entire extent if we don't have one specified.
    if (_extents.empty())
    {
        addExtent( map->getProfile()->getExtent() );
    }

    bool hasCaches = false;
    int src_min_level = INT_MAX;
    unsigned int src_max_level = 0;

    MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" );

    //Assumes the the TileSource will perform the caching for us when we call createImage
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        TileSource* src   = layer->getTileSource();

        const ImageLayerOptions& opt = layer->getImageLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if ( !src )
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ )
    {
        ElevationLayer* layer = i->get();
        TileSource*     src   = layer->getTileSource();
        const ElevationLayerOptions& opt = layer->getElevationLayerOptions();

        if ( layer->isCacheOnly() )
        {
            OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl;
        }
        else if (!src)
        {
            OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl;
        }
        //else if ( src->getCachePolicyHint(0L) == CachePolicy::NO_CACHE )
        //{
        //    OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl;
        //}
        else if ( !layer->getCache() )
        {
            OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl;
        }
        else
        {
            hasCaches = true;

            if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level)
                src_min_level = opt.minLevel().get();
            if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level)
                src_max_level = opt.maxLevel().get();
        }
    }

    if ( !hasCaches )
    {
        OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl;
        return;
    }

    if ( src_max_level > 0 && src_max_level < _maxLevel )
    {
        _maxLevel = src_max_level;
    }

    OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl;

    osg::Timer_t startTime = osg::Timer::instance()->tick();
    //Estimate the number of tiles
    _total = 0;    

    for (unsigned int level = _minLevel; level <= _maxLevel; level++)
    {
        double coverageRatio = 0.0;

        if (_extents.empty())
        {
            unsigned int wide, high;
            map->getProfile()->getNumTiles( level, wide, high );
            _total += (wide * high);
        }
        else
        {
            for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); itr++)
            {
                const GeoExtent& extent = *itr;
                double boundsArea = extent.area();

                TileKey ll = map->getProfile()->createTileKey(extent.xMin(), extent.yMin(), level);
                TileKey ur = map->getProfile()->createTileKey(extent.xMax(), extent.yMax(), level);

                if (!ll.valid() || !ur.valid()) continue;
                
                int tilesWide = ur.getTileX() - ll.getTileX() + 1;
                int tilesHigh = ll.getTileY() - ur.getTileY() + 1;
                int tilesAtLevel = tilesWide * tilesHigh;
                //OE_NOTICE << "Tiles at level " << level << "=" << tilesAtLevel << std::endl;

                /*
                bool hasData = false;
                
                for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); itr++)
                {
                    TileSource* src = itr->get()->getTileSource();
                    if (src)
                    {
                        if (src->hasDataAtLOD( level ))
                        {
                            //Compute the percent coverage of this dataset on the current extent
                            if (src->getDataExtents().size() > 0)
                            {
                                double cov = 0.0;
                                for (unsigned int j = 0; j < src->getDataExtents().size(); j++)
                                {
                                    GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS());
                                    GeoExtent intersection = b.intersectionSameSRS( extent );
                                    if (intersection.isValid())
                                    {
                                        double coverage = intersection.area() / boundsArea;
                                        cov += coverage; //Assumes the extents aren't overlapping                            
                                    }
                                }
                                if (coverageRatio < cov) coverageRatio = cov;
                            }
                            else
                            {
                                //We have no way of knowing how much coverage we have
                                coverageRatio = 1.0;
                            }
                            hasData = true;
                            break;
                        }
                    }
                }

                for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); itr++)
                {
                    TileSource* src = itr->get()->getTileSource();
                    if (src)
                    {
                        if (src->hasDataAtLOD( level ))
                        {
                            //Compute the percent coverage of this dataset on the current extent
                            if (src->getDataExtents().size() > 0)
                            {
                                double cov = 0.0;
                                for (unsigned int j = 0; j < src->getDataExtents().size(); j++)
                                {
                                    GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS());
                                    GeoExtent intersection = b.intersectionSameSRS( extent );
                                    if (intersection.isValid())
                                    {
                                        double coverage = intersection.area() / boundsArea;
                                        cov += coverage; //Assumes the extents aren't overlapping                            
                                    }
                                }
                                if (coverageRatio < cov) coverageRatio = cov;
                            }
                            else
                            {
                                //We have no way of knowing how much coverage we have
                                coverageRatio = 1.0;
                            }
                            hasData = true;
                            break;
                        }
                    }
                }

                //Adjust the coverage ratio by a fudge factor to try to keep it from being too small,
                //tiles are either processed or not and the ratio is exact so will cover tiles partially
                //and potentially be too small
                double adjust = 4.0;
                coverageRatio = osg::clampBetween(coverageRatio * adjust, 0.0, 1.0);                

                //OE_NOTICE << level <<  " CoverageRatio = " << coverageRatio << std::endl;

                if (hasData)
                {
                    _total += (int)ceil(coverageRatio * (double)tilesAtLevel );
                }
                */
                _total += tilesAtLevel;
            }
        }
    }

    osg::Timer_t endTime = osg::Timer::instance()->tick();
    //OE_NOTICE << "Counted tiles in " << osg::Timer::instance()->delta_s(startTime, endTime) << " s" << std::endl;

    OE_INFO << "Processing ~" << _total << " tiles" << std::endl;

    for (unsigned int i = 0; i < keys.size(); ++i)
    {
        processKey( mapf, keys[i] );
    }

    _total = _completed;

    if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished");
}
コード例 #13
0
ファイル: ElevationLayer.cpp プロジェクト: JohnDr/osgearth
bool
ElevationLayerVector::createHeightField(const TileKey&                  key,
                                        bool                            fallback,
                                        const Profile*                  haeProfile,
                                        ElevationInterpolation          interpolation,
                                        ElevationSamplePolicy           samplePolicy,
                                        osg::ref_ptr<osg::HeightField>& out_result,
                                        bool*                           out_isFallback,
                                        ProgressCallback*               progress )  const
{        
    unsigned lowestLOD = key.getLevelOfDetail();
    bool hfInitialized = false;

    //Get a HeightField for each of the enabled layers
    GeoHeightFieldVector heightFields;

    //The number of fallback heightfields we have
    int numFallbacks = 0;

    //Default to being fallback data.
    if ( out_isFallback )
    {
        *out_isFallback = true;
    }

    // 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.getLevelOfDetail(), key.getTileX(), key.getTileY(), haeProfile );
    }

    // Generate a heightfield for each elevation layer.

    unsigned defElevSize = 8;

    for( ElevationLayerVector::const_iterator i = this->begin(); i != this->end(); i++ )
    {
        ElevationLayer* layer = i->get();
        if ( layer->getVisible() )
        {
            GeoHeightField geoHF = layer->createHeightField( keyToUse, progress );

            // if "fallback" is set, try to fall back on lower LODs.
            if ( !geoHF.valid() && fallback )
            {
                TileKey hf_key = keyToUse.createParentKey();

                while ( hf_key.valid() && !geoHF.valid() )
                {
                    geoHF = layer->createHeightField( hf_key, progress );
                    if ( !geoHF.valid() )
                        hf_key = hf_key.createParentKey();
                }

                if ( geoHF.valid() )
                {
                    if ( hf_key.getLevelOfDetail() < lowestLOD )
                        lowestLOD = hf_key.getLevelOfDetail();

                    //This HeightField is fallback data, so increment the count.
                    numFallbacks++;
                }
            }

            if ( geoHF.valid() )
            {
                heightFields.push_back( geoHF );
            }
        }
    }

    //If any of the layers produced valid data then it's not considered a fallback
    if ( out_isFallback )
    {
        *out_isFallback = (numFallbacks == heightFields.size());
        //OE_NOTICE << "Num fallbacks=" << numFallbacks << " numHeightFields=" << heightFields.size() << " is fallback " << *out_isFallback << std::endl;
    }   

    if ( heightFields.size() == 0 )
    {            
        //If we got no heightfields but were requested to fallback, create an empty heightfield.
        if ( fallback )
        {
            out_result = HeightFieldUtils::createReferenceHeightField( keyToUse.getExtent(), defElevSize, defElevSize );                
            return true;
        }
        else
        {
            //We weren't requested to fallback so just return.
            return false;
        }
    }

    else if (heightFields.size() == 1)
    {
        if ( lowestLOD == key.getLevelOfDetail() )
        {
            //If we only have on heightfield, just return it.
            out_result = heightFields[0].takeHeightField();
        }
        else
        {
            GeoHeightField geoHF = heightFields[0].createSubSample( key.getExtent(), interpolation);
            out_result = geoHF.takeHeightField();
            hfInitialized = true;
        }
    }

    else
    {
        //If we have multiple heightfields, we need to composite them together.
        unsigned int width = 0;
        unsigned int height = 0;

        for (GeoHeightFieldVector::const_iterator i = heightFields.begin(); i < heightFields.end(); ++i)
        {
            if (i->getHeightField()->getNumColumns() > width) 
                width = i->getHeightField()->getNumColumns();
            if (i->getHeightField()->getNumRows() > height) 
                height = i->getHeightField()->getNumRows();
        }
        out_result = new osg::HeightField();
        out_result->allocate( 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)(out_result->getNumColumns()-1);
        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);

        const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

        //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);

                //Collect elevations from all of the layers. Iterate BACKWARDS because the last layer
                // is the highest priority.
                std::vector<float> elevations;
                for( GeoHeightFieldVector::reverse_iterator itr = heightFields.rbegin(); itr != heightFields.rend(); ++itr )
                {
                    const GeoHeightField& geoHF = *itr;

                    float elevation = 0.0f;
                    if ( geoHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) )
                    {
                        if (elevation != NO_DATA_VALUE)
                        {
                            elevations.push_back(elevation);
                        }
                    }
                }

                float elevation = NO_DATA_VALUE;

                //The list of elevations only contains valid values
                if (elevations.size() > 0)
                {
                    if (samplePolicy == SAMPLE_FIRST_VALID)
                    {
                        elevation = elevations[0];
                    }
                    else if (samplePolicy == SAMPLE_HIGHEST)
                    {
                        elevation = -FLT_MAX;
                        for (unsigned int i = 0; i < elevations.size(); ++i)
                        {
                            if (elevation < elevations[i]) elevation = elevations[i];
                        }
                    }
                    else if (samplePolicy == SAMPLE_LOWEST)
                    {
                        elevation = FLT_MAX;
                        for (unsigned i = 0; i < elevations.size(); ++i)
                        {
                            if (elevation > elevations[i]) elevation = elevations[i];
                        }
                    }
                    else if (samplePolicy == SAMPLE_AVERAGE)
                    {
                        elevation = 0.0;
                        for (unsigned i = 0; i < elevations.size(); ++i)
                        {
                            elevation += elevations[i];
                        }
                        elevation /= (float)elevations.size();
                    }
                }
                out_result->setHeight(c, r, elevation);
            }
        }
    }

    // Replace any NoData areas with the reference value. This is zero for HAE datums,
    // and some geoid height for orthometric datums.
    if (out_result.valid())
    {
        const Geoid*         geoid = 0L;
        const VerticalDatum* vdatum = key.getProfile()->getSRS()->getVerticalDatum();

        if ( haeProfile && vdatum )
        {
            geoid = vdatum->getGeoid();
        }

        HeightFieldUtils::resolveInvalidHeights(
            out_result.get(),
            key.getExtent(),
            NO_DATA_VALUE,
            geoid );

        //ReplaceInvalidDataOperator o;
        //o.setValidDataOperator(new osgTerrain::NoDataValue(NO_DATA_VALUE));
        //o( out_result.get() );
    }

    //Initialize the HF values for osgTerrain
    if (out_result.valid() && !hfInitialized )
    {   
        //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);
        out_result->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
        double dx = (maxx - minx)/(double)(out_result->getNumColumns()-1);
        double dy = (maxy - miny)/(double)(out_result->getNumRows()-1);
        out_result->setXInterval( dx );
        out_result->setYInterval( dy );
        out_result->setBorderWidth( 0 );
    }

    return out_result.valid();
}
コード例 #14
0
ファイル: ElevationLayer.cpp プロジェクト: Geo12/osgearth
bool
ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                                          const TileKey&         key,
                                          const Profile*         haeProfile,
                                          ElevationInterpolation interpolation,
                                          ProgressCallback*      progress ) const
{
    // heightfield must already exist.
    if ( !hf )
        return false;

    // 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.
    ElevationLayerVector contenders;
    ElevationLayerVector offsets;
    for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
    {
        ElevationLayer* layer = 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());

            // Note: isKeyInRange tests the key, but haData tests the mapped key.
            // I think that's right!
            if ((layer->getTileSource() == 0L) || 
                (layer->isKeyInRange(key) && layer->getTileSource()->hasData(mappedKey)))
            {
                if (layer->isOffset())
                    offsets.push_back(layer);
                else
                    contenders.push_back(layer);
            }
        }
    }

    // nothing? bail out.
    if ( contenders.empty() && offsets.empty() )
    {
        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);
    
    // We will load the actual heightfields on demand. We might not need them all.
    GeoHeightFieldVector heightFields(contenders.size());
    GeoHeightFieldVector offsetFields(offsets.size());
    std::vector<bool>    heightFailed (contenders.size(), false);
    std::vector<bool>    offsetFailed(offsets.size(), false);

    const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

    bool realData = false;

    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.
            bool resolved = false;

            for(int i=0; i<contenders.size() && !resolved; ++i)
            {
                if ( heightFailed[i] )
                    continue;

                GeoHeightField& layerHF = heightFields[i];
                if ( !layerHF.valid() )
                {
                    TileKey mappedKey = 
                        keyToUse.mapResolution(hf->getNumColumns(), contenders[i]->getTileSize());

                    layerHF = contenders[i]->createHeightField(mappedKey, progress);
                    if ( !layerHF.valid() )
                    {
                        heightFailed[i] = true;
                        continue;
                    }
                }

                // If we actually got a layer then we have real data
                realData = true;

                float elevation;
                if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) &&
                    elevation != NO_DATA_VALUE)
                {
                    resolved = true;                    
                    hf->setHeight(c, r, elevation);
                }
            }

            for(int i=offsets.size()-1; i>=0; --i)
            {
                if ( offsetFailed[i] )
                    continue;

                GeoHeightField& layerHF = offsetFields[i];
                if ( !layerHF.valid() )
                {
                    TileKey mappedKey = 
                        keyToUse.mapResolution(hf->getNumColumns(), offsets[i]->getTileSize());

                    layerHF = offsets[i]->createHeightField(mappedKey, progress);
                    if ( !layerHF.valid() )
                    {
                        offsetFailed[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;
                }
            }
        }
    }   

    // Return whether or not we actually read any real data
    return realData;
}
コード例 #15
0
ファイル: ElevationLayer.cpp プロジェクト: Displacer/osgearth
bool
ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                                          const TileKey&         key,
                                          const Profile*         haeProfile,
                                          ElevationInterpolation interpolation,
                                          ProgressCallback*      progress ) const
{
    //osg::Timer_t startTime = osg::Timer::instance()->tick();
    // heightfield must already exist.
    if ( !hf )
        return false;

    // 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.
    LayerAndKeyVector contenders;
    LayerAndKeyVector 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(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
    {
        ElevationLayer* layer = 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 );

            // Is there a tilesource? If not we are cache-only and cannot reject the layer.
            if ( layer->getTileSource() )
            {
                // Check whether the non-mapped key is valid according to the user's min/max level settings:
                if ( !layer->isKeyInRange(key) )
                {
                    useLayer = false;
                }
                

                // Find the "best available" mapped key from the tile source:
                else 
                {
                    if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) )
                    {
                        // 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( std::make_pair(layer, bestKey) );
                }
                else
                {
                    contenders.push_back( std::make_pair(layer, bestKey) );
                }
            }
        }
    }

    // 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);
    
    // We will load the actual heightfields on demand. We might not need them all.
    GeoHeightFieldVector heightFields(contenders.size());
    GeoHeightFieldVector offsetFields(offsets.size());
    std::vector<bool>    heightFailed(contenders.size(), false);
    std::vector<bool>    offsetFailed(offsets.size(), false);

    // The maximum number of heightfields to keep in this local cache
    unsigned int maxHeightFields = 50;
    unsigned numHeightFieldsInCache = 0;

    //double fallBackTime = 0;

    const SpatialReference* keySRS = keyToUse.getProfile()->getSRS();

    bool realData = false;

    //unsigned int numFallback = 0;


    unsigned int total = numColumns * numRows;
    unsigned int completed = 0;

    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.
            bool resolved = false;

            for(int i=0; i<contenders.size() && !resolved; ++i)
            {
                if ( heightFailed[i] )
                    continue;

                ElevationLayer* layer = contenders[i].first.get();

                GeoHeightField& layerHF = heightFields[i];
                if ( !layerHF.valid() )
                {
                    layerHF = layer->createHeightField(contenders[i].second, progress);
                    
                    if ( !layerHF.valid() )
                    {
                        // This layer potentially has data or it wouldn't have ended up in the contendors list, so try falling back on the parent
                        TileKey parentKey = contenders[i].second.createParentKey();
                        while (!layerHF.valid() && parentKey.valid())
                        {
                            //numFallback++;
                            //osg::Timer_t fbStartTime = osg::Timer::instance()->tick();
                            GeoHeightField parentHF = layer->createHeightField(parentKey, progress);
                            //osg::Timer_t fbEndTime = osg::Timer::instance()->tick();

                            // Only penalize time wasted actually falling back.
                            //if (!parentHF.valid())
                            // {
                            //    fallBackTime += osg::Timer::instance()->delta_m(fbStartTime, fbEndTime);
                            //}

                            if (parentHF.valid())
                            {
                                layerHF = parentHF;
                                break;
                            }
                            else
                            {
                                parentKey = parentKey.createParentKey();
                            }

                        }

                        if (!layerHF.valid())
                        {
                            heightFailed[i] = true;
                            continue;
                        }
                    }
                    else
                    {
                        numHeightFieldsInCache++;
                    }
                }

                // If we actually got a layer then we have real data
                realData = true;

                float elevation;
                if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) &&
                    elevation != NO_DATA_VALUE)
                {
                    resolved = true;                    
                    hf->setHeight(c, r, elevation);
                }


                // 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 k = 0; k < heightFields.size(); k++)
                    {
                        heightFields[k] = GeoHeightField::INVALID;
                    }
                    numHeightFieldsInCache = 0;
                }
            }

            for(int i=offsets.size()-1; i>=0; --i)
            {
                if ( offsetFailed[i] )
                    continue;

                GeoHeightField& layerHF = offsetFields[i];
                if ( !layerHF.valid() )
                {
                    ElevationLayer* offset = offsets[i].first.get();

                    layerHF = offset->createHeightField(offsets[i].second, progress);
                    if ( !layerHF.valid() )
                    {
                        offsetFailed[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;
                }
            }

            completed++;
            //OE_NOTICE << "Completed " << completed << " of " << total << std::endl;
        }
    }   

    //osg::Timer_t endTime = osg::Timer::instance()->tick();
    //double totalTime = osg::Timer::instance()->delta_m(startTime, endTime);
   // double fallbackPercentage = fallBackTime / totalTime;
    //if (fallBackTime > 0)
    //{
    //    OE_NOTICE << "populateHeightField took " << totalTime << "ms fallbacktime=" << fallBackTime << "ms count=" << numFallback << " percentage=" << fallbackPercentage << std::endl;
    //}
    //else
    //{
    //    OE_NOTICE << "populateHeightField took " << totalTime << "ms" << std::endl;
    //}

    // Return whether or not we actually read any real data
    return realData;
}
コード例 #16
0
ファイル: ElevationLayer.cpp プロジェクト: ldelgass/osgearth
bool
ElevationLayerVector::populateHeightField(osg::HeightField*      hf,
                                          const TileKey&         key,
                                          const Profile*         haeProfile,
                                          ElevationInterpolation interpolation,
                                          ProgressCallback*      progress ) const
{
    // heightfield must already exist.
    if ( !hf )
        return false;

    // 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.
    LayerAndKeyVector contenders;
    LayerAndKeyVector 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(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i)
    {
        ElevationLayer* layer = 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 );

            // Is there a tilesource? If not we are cache-only and cannot reject the layer.
            if ( layer->getTileSource() )
            {
                // Check whether the non-mapped key is valid according to the user's min/max level settings:
                if ( !layer->isKeyInRange(key) )
                {
                    useLayer = false;
                }
                

                // Find the "best available" mapped key from the tile source:
                else 
                {
                    if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) )
                    {
                        // 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( std::make_pair(layer, bestKey) );
                }
                else
                {
                    contenders.push_back( std::make_pair(layer, bestKey) );
                }
            }
        }
    }

    // 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 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;
    }
    
    // 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;
    unsigned int completed = 0;
    int nodataCount = 0;

    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.
            bool resolved = false;

            for(int i=0; i<contenders.size() && !resolved; ++i)
            {
                ElevationLayer* layer = contenders[i].first.get();                
                TileKey contenderKey = contenders[i].second;

                // 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 (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);
                }

                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())
                    {
                        layerHF = layer->createHeightField(actualKey, progress);
                        if (!layerHF.valid())
                        {
                            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 )
                        {
                            resolved = true;                    
                            hf->setHeight(c, r, elevation);
                        }
                        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)
            {
                TileKey contenderKey = offsets[i].second;

                // 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 (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);
                }
                
                if ( offsetFailed[n][i] == true )
                    continue;

                GeoHeightField& layerHF = offsetFields[n][i];
                if ( !layerHF.valid() )
                {
                    ElevationLayer* offset = offsets[i].first.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;
                }
            }
        }
    }

    // Return whether or not we actually read any real data
    return realData;
}