Example #1
0
bool
CacheSeed::cacheTile(const MapFrame& mapf, const TileKey& key ) const
{
    bool gotData = false;

    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ )
    {
        ImageLayer* layer = i->get();
        if ( layer->isKeyValid( key ) )
        {
            GeoImage image = layer->createImage( key );
            if ( image.valid() )
                gotData = true;
        }
    }

    if ( mapf.elevationLayers().size() > 0 )
    {
        osg::ref_ptr<osg::HeightField> hf;
        mapf.getHeightField( key, false, hf );
        if ( hf.valid() )
            gotData = true;
    }

    return gotData;
}
Example #2
0
bool CacheTileHandler::handleTile(const TileKey& key, const TileVisitor& tv)
{        
    ImageLayer* imageLayer = dynamic_cast< ImageLayer* >( _layer.get() );
    ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer* >( _layer.get() );    

    // Just call createImage or createHeightField on the layer and the it will be cached!
    if (imageLayer)
    {                
        GeoImage image = imageLayer->createImage( key );
        if (image.valid())
        {                
            return true;
        }            
    }
    else if (elevationLayer )
    {
        GeoHeightField hf = elevationLayer->createHeightField(key, 0L);
        if (hf.valid())
        {                
            return true;
        }            
    }

    // If we didn't produce a result but the key isn't within range then we should continue to 
    // traverse the children b/c a min level was set.
    if (!_layer->isKeyInLegalRange(key))
    {
        return true;
    }

    return false;        
}   
Example #3
0
bool CacheTileHandler::handleTile( const TileKey& key )
{        
    ImageLayer* imageLayer = dynamic_cast< ImageLayer* >( _layer.get() );
    ElevationLayer* elevationLayer = dynamic_cast< ElevationLayer* >( _layer.get() );    

    // Just call createImage or createHeightField on the layer and the it will be cached!
    if (imageLayer)
    {                
        GeoImage image = imageLayer->createImage( key );
        if (image.valid())
        {                
            return true;
        }            
    }
    else if (elevationLayer )
    {
        GeoHeightField hf = elevationLayer->createHeightField( key );
        if (hf.valid())
        {                
            return true;
        }            
    }
    return false;        
}   
osg::Image*
CompositeTileSource::createImage(const TileKey&    key,
                                 ProgressCallback* progress )
{    
    ImageMixVector images;
    images.reserve(_imageLayers.size());

    // Try to get an image from each of the layers for the given key.
    for (ImageLayerVector::const_iterator itr = _imageLayers.begin(); itr != _imageLayers.end(); ++itr)
    {
        ImageLayer* layer = itr->get();
        ImageInfo imageInfo;
        imageInfo.dataInExtents = layer->getTileSource()->hasDataInExtent( key.getExtent() );
        imageInfo.opacity = layer->getOpacity();

        if (imageInfo.dataInExtents)
        {
            GeoImage image = layer->createImage(key, progress);
            if (image.valid())
            {
                imageInfo.image = image.getImage();
            }

            // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
            if (progress && (progress->isCanceled() || progress->needsRetry()))
            {
                OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                return 0L;
            }
        }

        images.push_back(imageInfo);
    }

    // Determine the output texture size to use based on the image that were creatd.
    unsigned numValidImages = 0;
    osg::Vec2s textureSize;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid())
        {
            if (numValidImages == 0)
            {
                textureSize.set( info.image->s(), info.image->t());
            }
            numValidImages++;        
        }
    } 

    // Create fallback images if we have some valid data but not for all the layers
    if (numValidImages > 0 && numValidImages < images.size())
    {
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            ImageLayer* layer = _imageLayers[i].get();
            if (!info.image.valid() && info.dataInExtents)
            {                      
                TileKey parentKey = key.createParentKey();

                GeoImage image;
                while (!image.valid() && parentKey.valid())
                {
                    image = layer->createImage(parentKey, progress);
                    if (image.valid())
                    {
                        break;
                    }

                    // If the progress got cancelled or it needs a retry then return NULL to prevent this tile from being built and cached with incomplete or partial data.
                    if (progress && (progress->isCanceled() || progress->needsRetry()))
                    {
                        OE_DEBUG << LC << " createImage was cancelled or needs retry for " << key.str() << std::endl;
                        return 0L;
                    }

                    parentKey = parentKey.createParentKey();
                }

                if (image.valid())
                {                                        
                    // TODO:  Bilinear options?
                    bool bilinear = layer->isCoverage() ? false : true;
                    GeoImage cropped = image.crop( key.getExtent(), true, textureSize.x(), textureSize.y(), bilinear);
                    info.image = cropped.getImage();
                }                    
            }
        }
    }

    // Now finally create the output image.
    //Recompute the number of valid images
    numValidImages = 0;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        ImageInfo& info = images[i];
        if (info.image.valid()) numValidImages++;        
    }    

    if ( progress && progress->isCanceled() )
    {
        return 0L;
    }
    else if ( numValidImages == 0 )
    {
        return 0L;
    }
    else if ( numValidImages == 1 )
    {
        //We only have one valid image, so just return it and don't bother with compositing
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& info = images[i];
            if (info.image.valid())
                return info.image.release();
        }
        return 0L;
    }
    else
    {
        osg::Image* result = 0;
        for (unsigned int i = 0; i < images.size(); i++)
        {
            ImageInfo& imageInfo = images[i];
            if (!result)
            {
                if (imageInfo.image.valid())
                {
                    result = new osg::Image( *imageInfo.image.get());
                }
            }
            else
            {
                if (imageInfo.image.valid())
                {
                    ImageUtils::mix( result, imageInfo.image.get(), imageInfo.opacity );
                }
            }            
        }        
        return result;
    }



}
void
TerrainTileModelFactory::addColorLayers(TerrainTileModel* model,
                                        const Map* map,
                                        const TerrainEngineRequirements* reqs,
                                        const TileKey&    key,
                                        const CreateTileModelFilter& filter,
                                        ProgressCallback* progress)
{
    OE_START_TIMER(fetch_image_layers);

    int order = 0;

    LayerVector layers;
    map->getLayers(layers);

    for (LayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i)
    {
        Layer* layer = i->get();

        if (layer->getRenderType() != layer->RENDERTYPE_TERRAIN_SURFACE)
            continue;

        if (!layer->getEnabled())
            continue;

        if (!filter.accept(layer))
            continue;

        ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(layer);
        if (imageLayer)
        {
            osg::Texture* tex = 0L;
            osg::Matrixf textureMatrix;

            if (imageLayer->isKeyInLegalRange(key) && imageLayer->mayHaveDataInExtent(key.getExtent()))
            {
                if (imageLayer->createTextureSupported())
                {
                    tex = imageLayer->createTexture( key, progress, textureMatrix );
                }

                else
                {
                    GeoImage geoImage = imageLayer->createImage( key, progress );
           
                    if ( geoImage.valid() )
                    {
                        if ( imageLayer->isCoverage() )
                            tex = createCoverageTexture(geoImage.getImage(), imageLayer);
                        else
                            tex = createImageTexture(geoImage.getImage(), imageLayer);
                    }
                }
            }
        
            // if this is the first LOD, and the engine requires that the first LOD
            // be populated, make an empty texture if we didn't get one.
            if (tex == 0L &&
                _options.firstLOD() == key.getLOD() &&
                reqs && reqs->fullDataAtFirstLodRequired())
            {
                tex = _emptyTexture.get();
            }
         
            if (tex)
            {
                tex->setName(model->getKey().str());

                TerrainTileImageLayerModel* layerModel = new TerrainTileImageLayerModel();

                layerModel->setImageLayer(imageLayer);

                layerModel->setTexture(tex);
                layerModel->setMatrix(new osg::RefMatrixf(textureMatrix));

                model->colorLayers().push_back(layerModel);

                if (imageLayer->isShared())
                {
                    model->sharedLayers().push_back(layerModel);
                }

                if (imageLayer->isDynamic())
                {
                    model->setRequiresUpdateTraverse(true);
                }
            }
        }

        else // non-image kind of TILE layer:
        {
            TerrainTileColorLayerModel* colorModel = new TerrainTileColorLayerModel();
            colorModel->setLayer(layer);
            model->colorLayers().push_back(colorModel);
        }
    }

    if (progress)
        progress->stats()["fetch_imagery_time"] += OE_STOP_TIMER(fetch_image_layers);
}
Example #6
0
osg::Node*
OSGTileFactory::createPopulatedTile(const MapFrame&  mapf, 
                                    Terrain*         terrain, 
                                    const TileKey&   key, 
                                    bool             wrapInPagedLOD, 
                                    bool             fallback, 
                                    bool&            validData )
{
    const MapInfo& mapInfo = mapf.getMapInfo();
    bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS();

    typedef std::vector<GeoImageData> GeoImageDataVector;
    GeoImageDataVector image_tiles;

    // Collect the image layers
    bool empty_map = mapf.imageLayers().size() == 0 && mapf.elevationLayers().size() == 0;

    // Create the images for the tile
    for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i )
    {
        ImageLayer* layer = i->get();
        GeoImageData imageData;

        // Only try to create images if the key is valid
        if ( layer->isKeyValid( key ) )
        {
            imageData._image = layer->createImage( key );
            imageData._layerUID = layer->getUID();
            imageData._imageTileKey = key;
        }

        // always push images, even it they are empty, so that the image_tiles vector is one-to-one
        // with the imageLayers() vector.
        image_tiles.push_back( imageData );
    }

    bool hasElevation = false;

    //Create the heightfield for the tile
    osg::ref_ptr<osg::HeightField> hf;
    if ( mapf.elevationLayers().size() > 0 )
    {
        mapf.getHeightField( key, false, hf, 0L, _terrainOptions.elevationInterpolation().value());     
    }

    //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one.  Otherwise you can run into the situation
    //where you could have an inset heightfield on one hemisphere and the whole other hemisphere won't show up.
    if ( mapInfo.isGeocentric() && key.getLevelOfDetail() <= 1 && !hf.valid())
    {
        hf = createEmptyHeightField( key );
    }
    hasElevation = hf.valid();

    //Determine if we've created any images
    unsigned int numValidImages = 0;
    for (unsigned int i = 0; i < image_tiles.size(); ++i)
    {
        if (image_tiles[i]._image.valid()) numValidImages++;
    }


    //If we couldn't create any imagery or heightfields, bail out
    if (!hf.valid() && (numValidImages == 0) && !empty_map)
    {
        OE_DEBUG << LC << "Could not create any imagery or heightfields for " << key.str() <<".  Not building tile" << std::endl;
        validData = false;

        //If we're not asked to fallback on previous LOD's and we have no data, return NULL
        if (!fallback)
        {
            return NULL;
        }
    }
    else
    {
        validData = true;
    }

    //Try to interpolate any missing image layers from parent tiles
    for (unsigned int i = 0; i < mapf.imageLayers().size(); i++ )
    {
        if (!image_tiles[i]._image.valid())
        {
            if (mapf.getImageLayerAt(i)->isKeyValid(key))
            {
                //If the key was valid and we have no image, then something possibly went wrong with the image creation such as a server being busy.
                createValidGeoImage(mapf.getImageLayerAt(i), key, image_tiles[i]._image, image_tiles[i]._imageTileKey);
            }

            //If we still couldn't create an image, either something is really wrong or the key wasn't valid, so just create a transparent placeholder image
            if (!image_tiles[i]._image.valid())
            {
                //If the image is not valid, create an empty texture as a placeholder
                image_tiles[i]._image = GeoImage(ImageUtils::createEmptyImage(), key.getExtent());
                image_tiles[i]._imageTileKey = key;
            }
        }
    }

    //Fill in missing heightfield information from parent tiles
    if (!hf.valid())
    {
        //We have no heightfield sources, 
        if ( mapf.elevationLayers().size() == 0 )
        {
            hf = createEmptyHeightField( key );
        }
        else
        {
            //Try to get a heightfield again, but this time fallback on parent tiles
            if ( mapf.getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value() ) )
            {
                hasElevation = true;
            }
            else
            {
                //We couldn't get any heightfield, so just create an empty one.
                hf = createEmptyHeightField( key );
            }
        }
    }


    // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees
    if ( isPlateCarre )
    {
        HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() );
    }

    osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo );
    osgTerrain::HeightFieldLayer* hf_layer = new osgTerrain::HeightFieldLayer();
    hf_layer->setLocator( locator.get() );
    hf_layer->setHeightField( hf.get() );

    bool isStreaming = 
        _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL ||
        _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE;

    Tile* tile = terrain->createTile( key, locator.get() );
    tile->setTerrainTechnique( terrain->cloneTechnique() );
    tile->setVerticalScale( _terrainOptions.verticalScale().value() );
    //tile->setLocator( locator.get() );
    tile->setElevationLayer( hf_layer );
    //tile->setRequiresNormals( true );
    tile->setDataVariance(osg::Object::DYNAMIC);

#if 0
    //Attach an updatecallback to normalize the edges of TerrainTiles.
    if (hasElevation && _terrainOptions.normalizeEdges().get() )
    {
        tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback());
        tile->setDataVariance(osg::Object::DYNAMIC);
    }
#endif

    //Assign the terrain system to the TerrainTile.
    //It is very important the terrain system is set while the MapConfig's sourceMutex is locked.
    //This registers the terrain tile so that adding/removing layers are always in sync.  If you don't do this
    //you can end up with a situation where the database pager is waiting to merge a tile, then a layer is added, then
    //the tile is finally merged and is out of sync.

    double min_units_per_pixel = DBL_MAX;

#if 0
    // create contour layer:
    if (map->getContourTransferFunction() != NULL)
    {
        osgTerrain::ContourLayer* contourLayer(new osgTerrain::ContourLayer(map->getContourTransferFunction()));

        contourLayer->setMagFilter(_terrainOptions.getContourMagFilter().value());
        contourLayer->setMinFilter(_terrainOptions.getContourMinFilter().value());
        tile->setCustomColorLayer(layer,contourLayer); //TODO: need layerUID, not layer index here -GW
        ++layer;
    }
#endif

    for (unsigned int i = 0; i < image_tiles.size(); ++i)
    {
        if (image_tiles[i]._image.valid())
        {
            const GeoImage& geo_image = image_tiles[i]._image;

            double img_xmin, img_ymin, img_xmax, img_ymax;
            geo_image.getExtent().getBounds( img_xmin, img_ymin, img_xmax, img_ymax );

            //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image
            osg::ref_ptr<GeoLocator> img_locator = key.getProfile()->getSRS()->createLocator( 
                img_xmin, img_ymin, img_xmax, img_ymax,
                isPlateCarre );

            if ( mapInfo.isGeocentric() )
                img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );

            tile->setCustomColorLayer( CustomColorLayer(
                mapf.getImageLayerAt(i),
                geo_image.getImage(),
                img_locator.get(),
                key.getLevelOfDetail(),
                key) );

            double upp = geo_image.getUnitsPerPixel();

            // Scale the units per pixel to degrees if the image is mercator (and the key is geo)
            if ( geo_image.getSRS()->isMercator() && key.getExtent().getSRS()->isGeographic() )
                upp *= 1.0f/111319.0f;

            min_units_per_pixel = osg::minimum(upp, min_units_per_pixel);
        }
    }

    osg::BoundingSphere bs = tile->getBound();
    double max_range = 1e10;
    double radius = bs.radius();

#if 1
    double min_range = radius * _terrainOptions.minTileRangeFactor().get();
    //osg::LOD::RangeMode mode = osg::LOD::DISTANCE_FROM_EYE_POINT;
#else
    double width = key.getExtent().width();	
    if (min_units_per_pixel == DBL_MAX) min_units_per_pixel = width/256.0;
    double min_range = (width / min_units_per_pixel) * _terrainOptions.getMinTileRangeFactor(); 
    //osg::LOD::RangeMode mode = osg::LOD::PIXEL_SIZE_ON_SCREEN;
#endif


    // a skirt hides cracks when transitioning between LODs:
    hf->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() );

    // for now, cluster culling does not work for CUBE rendering
    //bool isCube = mapInfo.isCube(); //map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE;
    if ( mapInfo.isGeocentric() && !mapInfo.isCube() )
    {
        //TODO:  Work on cluster culling computation for cube faces
        osg::ClusterCullingCallback* ccc = createClusterCullingCallback(tile, locator->getEllipsoidModel() );
        tile->setCullCallback( ccc );
    }

    // Wait until now, when the tile is fully baked, to assign the terrain to the tile.
    // Placeholder tiles might try to locate this tile as an ancestor, and access its layers
    // and locators...so they must be intact before making this tile available via setTerrain.
    //
    // If there's already a placeholder tile registered, this will be ignored. If there isn't,
    // this will register the new tile.
    tile->attachToTerrain( terrain );
    //tile->setTerrain( terrain );
    //terrain->registerTile( tile );

    if ( isStreaming && key.getLevelOfDetail() > 0 )
    {
        static_cast<StreamingTile*>(tile)->setHasElevationHint( hasElevation );
    }

    osg::Node* result = 0L;

    if (wrapInPagedLOD)
    {
        // create a PLOD so we can keep subdividing:
        osg::PagedLOD* plod = new osg::PagedLOD();
        plod->setCenter( bs.center() );
        plod->addChild( tile, min_range, max_range );

        std::string filename = createURI( _engineId, key ); //map->getId(), key );

        //Only add the next tile if it hasn't been blacklisted
        bool isBlacklisted = osgEarth::Registry::instance()->isBlacklisted( filename );
        if (!isBlacklisted && key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().value() && validData )
        {
            plod->setFileName( 1, filename  );
            plod->setRange( 1, 0.0, min_range );
        }
        else
        {
            plod->setRange( 0, 0, FLT_MAX );
        }

#if USE_FILELOCATIONCALLBACK
        osgDB::Options* options = new osgDB::Options;
        options->setFileLocationCallback( new FileLocationCallback() );
        plod->setDatabaseOptions( options );
#endif
        result = plod;

        if ( isStreaming )
            result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) );
    }
    else
    {
        result = tile;
    }

    return result;
}