예제 #1
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;        
}   
예제 #2
0
bool
OSGTileFactory::createValidGeoImage(ImageLayer* layer,
                                    const TileKey& key,
                                    GeoImage& out_image,
                                    TileKey&  out_actualTileKey,
                                    ProgressCallback* progress)
{
    //TODO:  Redo this to just grab images from the parent TerrainTiles
    //Try to create the image with the given key
    out_actualTileKey = key;

    while (out_actualTileKey.valid())
    {
        if ( layer->isKeyValid(out_actualTileKey) )
        {
            out_image = layer->createImage( out_actualTileKey, progress );
            if ( out_image.valid() )
            {
                return true;
            }
        }
        out_actualTileKey = out_actualTileKey.createParentKey();
    }
    return false;
}
예제 #3
0
    // override
    // Creates an image.
    osg::Image* createImage(const TileKey&    key,
                            ProgressCallback* progress )
    {
        if ( !_imageLayer.valid() || !_featureSource.valid() )
            return 0L;

        // fetch the image for this key:
        GeoImage image = _imageLayer->createImage(key, progress);
        if ( !image.valid() )
            return 0L;

        // fetch a set of features for this key. The features are in their
        // own SRS, so we need to transform:
        const SpatialReference* featureSRS = _featureSource->getFeatureProfile()->getSRS();
        GeoExtent extentInFeatureSRS = key.getExtent().transform( featureSRS );

        // assemble a spatial query. It helps if your features have a spatial index.
        Query query;
        query.bounds() = extentInFeatureSRS.bounds();
        //query.expression() = ... // SQL expression compatible with data source
        osg::ref_ptr<FeatureCursor> cursor = _featureSource->createFeatureCursor(query);

        // create a new image to return.
        osg::Image* output = new osg::Image();
        //output->allocateImage(128, 128, 1, GL_RGB, GL_UNSIGNED_BYTE);

        // do your magic here.

        return output;
    }
예제 #4
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;
}
예제 #5
0
 bool handleTile(const TileKey& key)
 {
     bool ok = false;
     GeoImage image = _source->createImage(key);
     if ( image.valid() )
         ok = _dest->storeImage(key, image.getImage(), 0L);
     return ok;
 }
예제 #6
0
    osg::Image*
    createImage( const TileKey& key, ProgressCallback* progress )
    {
        if ( !_image.valid() || key.getLevelOfDetail() > getMaxDataLevel() )
            return NULL;

        GeoImage cropped = _image.crop( key.getExtent(), true, getPixelsPerTile(), getPixelsPerTile() );
        return cropped.valid() ? cropped.takeImage() : 0L;
    }
예제 #7
0
    void execute()
    {
        GeoImage geoImage;
        bool isFallbackData = false;

        // fetch the image from the layer, falling back on parent keys utils we are 
        // able to find one that works.
        TileKey imageKey( _key );
        while( !geoImage.valid() && imageKey.valid() && _layer->isKeyValid(imageKey) )
        {
            geoImage = _layer->createImage( imageKey, 0L ); // TODO: include a progress callback?
            if ( !geoImage.valid() )
            {
                imageKey = imageKey.createParentKey();
                isFallbackData = true;
            }
        }

        GeoLocator* locator = 0L;

        if ( !geoImage.valid() )
        {
            // no image found, so make an empty one (one pixel alpha).
            geoImage = GeoImage( ImageUtils::createEmptyImage(), _key.getExtent() );
            locator = GeoLocator::createForKey( _key, *_mapInfo );
            isFallbackData = true;
        }
        else
        {
            locator = GeoLocator::createForExtent(geoImage.getExtent(), *_mapInfo);                                                                                       
        }
        // add the color layer to the repo.
        _repo->add( CustomColorLayer(
            _layer,
            geoImage.getImage(),
            locator,
            _key.getLevelOfDetail(),
            _key,
            isFallbackData ) );
    }
예제 #8
0
CustomColorLayerRef*
OSGTileFactory::createImageLayer(const MapInfo&    mapInfo,
                                 ImageLayer*       layer,
                                 const TileKey&    key,
                                 ProgressCallback* progress)
{
    if ( !layer )
        return 0L;

    GeoImage geoImage;

    //If the key is valid, try to get the image from the MapLayer
    bool keyValid = layer->isKeyValid( key );
    if ( keyValid )
    {
        geoImage = layer->createImage(key, progress);
    }
    else
    {
        //If the key is not valid, simply make a transparent tile
        geoImage = GeoImage(ImageUtils::createEmptyImage(), key.getExtent());
    }

    if (geoImage.valid())
    {
        osg::ref_ptr<GeoLocator> imgLocator = GeoLocator::createForKey( key, mapInfo );

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

        return new CustomColorLayerRef( CustomColorLayer(
            layer,
            geoImage.getImage(),
            imgLocator.get(),
            key.getLevelOfDetail(),
            key) );
    }

    return NULL;
}
예제 #9
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;        
}   
예제 #10
0
GeoImage
ImageLayer::createImageInKeyProfile( const TileKey& key, ProgressCallback* progress, bool forceFallback, bool& out_isFallback )
{
    GeoImage result;

    out_isFallback = false;

    // If the layer is disabled, bail out.
    if ( !getEnabled() )
    {
        return GeoImage::INVALID;
    }

    // Check the max data level, which limits the LOD of available data.
    if ( _runtimeOptions.maxDataLevel().isSet() && key.getLOD() > _runtimeOptions.maxDataLevel().value() )
    {
        return GeoImage::INVALID;
    }

    // Check for a "Minumum level" setting on this layer. If we are before the
    // min level, just return the empty image. Do not cache empties
    if ( _runtimeOptions.minLevel().isSet() && key.getLOD() < _runtimeOptions.minLevel().value() )
    {
        return GeoImage( _emptyImage.get(), key.getExtent() );
    }

    // Check for a "Minimum resolution" setting on the layer. If we are before the
    // min resolution, return the empty image. Do not cache empties.
    if ( _runtimeOptions.minResolution().isSet() )
    {
        double keyres = key.getExtent().width() / getTileSize();
        double keyresInLayerProfile = key.getProfile()->getSRS()->transformUnits(keyres, getProfile()->getSRS());

        if ( keyresInLayerProfile > _runtimeOptions.minResolution().value() )
        {
            return GeoImage( _emptyImage.get(), key.getExtent() );
        }
    }

    OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= "
        << key.getExtent().toString() << std::endl;


    // locate the cache bin for the target profile for this layer:
    CacheBin* cacheBin = getCacheBin( key.getProfile() );

    // validate that we have either a valid tile source, or we're cache-only.
    if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) )
    {
        OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create image " << std::endl;
        _runtimeOptions.enabled() = false;
        return GeoImage::INVALID;
    }

    // validate the existance of a valid layer profile (unless we're in cache-only mode, in which
    // case there is no layer profile)
    if ( !isCacheOnly() && !getProfile() )
    {
        OE_WARN << LC << "Could not establish a valid profile" << std::endl;
        _runtimeOptions.enabled() = false;
        return GeoImage::INVALID;
    }

    // First, attempt to read from the cache. Since the cached data is stored in the
    // map profile, we can try this first.
    if ( cacheBin && getCachePolicy().isCacheReadable() )
    {
        ReadResult r = cacheBin->readImage( key.str(), getCachePolicy().getMinAcceptTime() );
        if ( r.succeeded() )
        {
            ImageUtils::normalizeImage( r.getImage() );
            return GeoImage( r.releaseImage(), key.getExtent() );
        }
        //else if ( r.code() == ReadResult::RESULT_EXPIRED )
        //{
        //    OE_INFO << LC << getName() << " : " << key.str() << " record expired!" << std::endl;
        //}
    }
    
    // The data was not in the cache. If we are cache-only, fail sliently
    if ( isCacheOnly() )
    {
        return GeoImage::INVALID;
    }

    // Get an image from the underlying TileSource.
    result = createImageFromTileSource( key, progress, forceFallback, out_isFallback );

    // Normalize the image if necessary
    if ( result.valid() )
    {
        ImageUtils::normalizeImage( result.getImage() );
    }

    // If we got a result, the cache is valid and we are caching in the map profile, write to the map cache.
    if (result.valid()  &&
        //JB:  Removed the check to not write out fallback data.  If you have a low resolution base dataset (max lod 3) and a high resolution insert (max lod 22)
        //     then the low res data needs to "fallback" from LOD 4 - 22 so you can display the high res inset.  If you don't cache these intermediate tiles then
        //     performance can suffer generating all those fallback tiles, especially if you have to do reprojection or mosaicing.
        //!out_isFallback &&
        cacheBin        && 
        getCachePolicy().isCacheWriteable() )
    {
        if ( key.getExtent() != result.getExtent() )
        {
            OE_INFO << LC << "WARNING! mismatched extents." << std::endl;
        }

        cacheBin->write( key.str(), result.getImage() );
        //OE_INFO << LC << "WRITING " << key.str() << " to the cache." << std::endl;
    }

    if ( result.valid() )
    {
        OE_DEBUG << LC << key.str() << " result OK" << std::endl;
    }
    else
    {
        OE_DEBUG << LC << key.str() << "result INVALID" << std::endl;
    }

    return result;
}
예제 #11
0
void
OSGTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
{
    if ( !layerAdded )
        return;

    if (!_isStreaming)
    {
        refresh();
    }
    else
    {
        // visit all existing terrain tiles and inform each one of the new image layer:
        TileVector tiles;
        _terrain->getTiles( tiles );

        for( TileVector::iterator itr = tiles.begin(); itr != tiles.end(); ++itr )
        {
            Tile* tile = itr->get();

            StreamingTile* streamingTile = 0L;

            GeoImage geoImage;
            bool needToUpdateImagery = false;
            int imageLOD = -1;

            if ( !_isStreaming || tile->getKey().getLevelOfDetail() == 1 )
            {
                // in standard mode, or at the first LOD in seq/pre mode, fetch the image immediately.
                TileKey geoImageKey = tile->getKey();
                _tileFactory->createValidGeoImage( layerAdded, tile->getKey(), geoImage, geoImageKey );
                imageLOD = tile->getKey().getLevelOfDetail();
            }
            else
            {
                // in seq/pre mode, set up a placeholder and mark the tile as dirty.
                geoImage = GeoImage(ImageUtils::createEmptyImage(), tile->getKey().getExtent() );
                needToUpdateImagery = true;
                streamingTile = static_cast<StreamingTile*>(tile);
            }

            if (geoImage.valid())
            {
                const MapInfo& mapInfo = _update_mapf->getMapInfo();

                double img_min_lon, img_min_lat, img_max_lon, img_max_lat;
                geoImage.getExtent().getBounds(img_min_lon, img_min_lat, img_max_lon, img_max_lat);

                //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 = tile->getKey().getProfile()->getSRS()->createLocator( 
                    img_min_lon, img_min_lat, img_max_lon, img_max_lat, 
                    !mapInfo.isGeocentric() );

                //Set the CS to geocentric if we are dealing with a geocentric map
                if ( mapInfo.isGeocentric() )
                {
                    img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC );
                }

                tile->setCustomColorLayer( CustomColorLayer(
                    layerAdded,
                    geoImage.getImage(),
                    img_locator.get(), imageLOD,  tile->getKey() ) );

                // if necessary, tell the tile to queue up a new imagery request (since we
                // just installed a placeholder)
                if ( needToUpdateImagery )
                {
                    streamingTile->updateImagery( layerAdded, *_update_mapf, _tileFactory.get() );
                }
            }
            else
            {
                // this can happen if there's no data in the new layer for the given tile.
                // we will rely on the driver to dump out a warning if this is an error.
            }

            tile->applyImmediateTileUpdate( TileUpdate::ADD_IMAGE_LAYER, layerAdded->getUID() );
        }

        updateTextureCombining();
    }
}
예제 #12
0
    FeatureCursor* createFeatureCursor( const Symbology::Query& query )
    {
        TileKey key = *query.tileKey();

#if 0
        // Debug
        Polygon* poly = new Polygon();
        poly->push_back(key.getExtent().xMin(), key.getExtent().yMin());
        poly->push_back(key.getExtent().xMax(), key.getExtent().yMin());
        poly->push_back(key.getExtent().xMax(), key.getExtent().yMax());
        poly->push_back(key.getExtent().xMin(), key.getExtent().yMax());
        FeatureList features;
        Feature* feature = new Feature(poly, SpatialReference::create("wgs84"));
        features.push_back( feature );
        return new FeatureListCursor( features );
#else

        osg::ref_ptr< osgEarth::ImageLayer > layer = query.getMap()->getImageLayerByName(*_options.layer());
        if (layer.valid())
        {
            GeoImage image = layer->createImage( key );
         
            FeatureList features;

            if (image.valid())
            {
                double pixWidth  = key.getExtent().width() / (double)image.getImage()->s();
                double pixHeight = key.getExtent().height() / (double)image.getImage()->t();
                ImageUtils::PixelReader reader(image.getImage());

                for (unsigned int r = 0; r < image.getImage()->t(); r++)
                {
                    double y = key.getExtent().yMin() + (double)r * pixHeight;

                    double minX = 0;
                    double maxX = 0;
                    float value = 0.0;

                    for (unsigned int c = 0; c < image.getImage()->s(); c++)
                    {
                        double x = key.getExtent().xMin() + (double)c * pixWidth;

                        osg::Vec4f color = reader(c, r);

                        // Starting a new row.  Initialize the values.
                        if (c == 0)
                        {
                            minX = x;
                            maxX = x + pixWidth;
                            value = color.r(); 
                        }
                        // Ending a row, finish the polygon.
                        else if (c == image.getImage()->s() -1)
                        {
                            // Increment the maxX to finish the row.
                            maxX = x + pixWidth;
                            Polygon* poly = new Polygon();
                            poly->push_back(minX, y);
                            poly->push_back(maxX, y);
                            poly->push_back(maxX, y+pixHeight);
                            poly->push_back(minX, y+pixHeight);
                            Feature* feature = new Feature(poly, SpatialReference::create("wgs84"));
                            feature->set(*_options.attribute(), value);
                            features.push_back( feature );
                            minX = x;
                            maxX = x + pixWidth;
                            value = color.r();
                        }
                        // The value is different, so complete the polygon and start a new one.
                        else if (color.r() != value)
                        {
                            Polygon* poly = new Polygon();
                            poly->push_back(minX, y);
                            poly->push_back(maxX, y);
                            poly->push_back(maxX, y+pixHeight);
                            poly->push_back(minX, y+pixHeight);
                            Feature* feature = new Feature(poly, SpatialReference::create("wgs84"));
                            feature->set(*_options.attribute(), value);
                            features.push_back( feature );
                            minX = x;
                            maxX = x + pixWidth;
                            value = color.r();
                        }
                        // The value is the same as the previous value, continue the polygon by increasing the maxX.
                        else if (color.r() == value)
                        {
                            maxX = x + pixWidth;
                        }
                    }

                    
                }
               
                if (!features.empty())
                {
                    //OE_NOTICE << LC << "Returning " << features.size() << " features" << std::endl;
                    return new FeatureListCursor( features );
                }
            }
        }
        else
        {
            OE_NOTICE << LC << "Couldn't get layer " << *_options.layer() << std::endl;
        }
        return 0;
#endif
    }
예제 #13
0
GeoImage
ImageLayer::createImageInKeyProfile(const TileKey&    key, 
                                    ProgressCallback* progress)
{
    GeoImage result;

    // If the layer is disabled, bail out.
    if ( !getEnabled() )
    {
        return GeoImage::INVALID;
    }

    // Make sure the request is in range.
    if ( !isKeyInRange(key) )
    {
        return GeoImage::INVALID;
    }

    OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= "
        << key.getExtent().toString() << std::endl;
    
    // Check the layer L2 cache first
    if ( _memCache.valid() )
    {
        CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() );        
        ReadResult result = bin->readObject(key.str(), 0);
        if ( result.succeeded() )
            return GeoImage(static_cast<osg::Image*>(result.releaseObject()), key.getExtent());
        //_memCache->dumpStats(key.getProfile()->getFullSignature());
    }

    // locate the cache bin for the target profile for this layer:
    CacheBin* cacheBin = getCacheBin( key.getProfile() );

    // validate that we have either a valid tile source, or we're cache-only.
    if ( ! (getTileSource() || (isCacheOnly() && cacheBin) ) )
    {
        OE_WARN << LC << "Error: layer does not have a valid TileSource, cannot create image " << std::endl;
        _runtimeOptions.enabled() = false;
        return GeoImage::INVALID;
    }

    // validate the existance of a valid layer profile (unless we're in cache-only mode, in which
    // case there is no layer profile)
    if ( !isCacheOnly() && !getProfile() )
    {
        OE_WARN << LC << "Could not establish a valid profile" << std::endl;
        _runtimeOptions.enabled() = false;
        return GeoImage::INVALID;
    }

    // First, attempt to read from the cache. Since the cached data is stored in the
    // map profile, we can try this first.
    if ( cacheBin && getCachePolicy().isCacheReadable() )
    {
        ReadResult r = cacheBin->readImage( key.str(), getCachePolicy().getMinAcceptTime() );
        if ( r.succeeded() )
        {
            ImageUtils::normalizeImage( r.getImage() );
            return GeoImage( r.releaseImage(), key.getExtent() );
        }
    }
    
    // The data was not in the cache. If we are cache-only, fail sliently
    if ( isCacheOnly() )
    {
        return GeoImage::INVALID;
    }

    // Get an image from the underlying TileSource.
    result = createImageFromTileSource( key, progress );

    // Normalize the image if necessary
    if ( result.valid() )
    {
        ImageUtils::normalizeImage( result.getImage() );
    }

    // memory cache first:
    if ( result.valid() && _memCache.valid() )
    {
        CacheBin* bin = _memCache->getOrCreateBin( key.getProfile()->getFullSignature() ); 
        bin->write(key.str(), result.getImage());
    }

    // If we got a result, the cache is valid and we are caching in the map profile,
    // write to the map cache.
    if (result.valid()  &&
        cacheBin        && 
        getCachePolicy().isCacheWriteable() )
    {
        if ( key.getExtent() != result.getExtent() )
        {
            OE_INFO << LC << "WARNING! mismatched extents." << std::endl;
        }

        cacheBin->write( key.str(), result.getImage() );
    }

    if ( result.valid() )
    {
        OE_DEBUG << LC << key.str() << " result OK" << std::endl;
    }
    else
    {
        OE_DEBUG << LC << key.str() << "result INVALID" << std::endl;
    }

    return result;
}
예제 #14
0
    void execute()
    {
        GeoImage geoImage;
        bool isFallbackData = false;

        bool useMercatorFastPath =
            _opt->enableMercatorFastPath() != false &&
            _mapInfo->isGeocentric()                &&
            _layer->getProfile()                    &&
            _layer->getProfile()->getSRS()->isSphericalMercator();

        // fetch the image from the layer, falling back on parent keys utils we are 
        // able to find one that works.

        bool autoFallback = _key.getLevelOfDetail() <= 1;

        TileKey imageKey( _key );
        TileSource* tileSource = _layer->getTileSource();
        const Profile* layerProfile = _layer->getProfile();

        //Only try to get data from the source if it actually intersects the key extent
        bool hasDataInExtent = true;
        if (tileSource && layerProfile)
        {
            GeoExtent ext = _key.getExtent();
            if (!layerProfile->getSRS()->isEquivalentTo( ext.getSRS()))
            {
                ext = layerProfile->clampAndTransformExtent( ext );
            }
            hasDataInExtent = ext.isValid() && tileSource->hasDataInExtent( ext );
        }        
        
        if (hasDataInExtent)
        {
            while( !geoImage.valid() && imageKey.valid() && _layer->isKeyValid(imageKey) )
            {
                if ( useMercatorFastPath )
                {
                    bool mercFallbackData = false;
                    geoImage = _layer->createImageInNativeProfile( imageKey, 0L, autoFallback, mercFallbackData );
                    if ( geoImage.valid() && mercFallbackData )
                    {
                        isFallbackData = true;
                    }
                }
                else
                {
                    geoImage = _layer->createImage( imageKey, 0L, autoFallback );
                }

                if ( !geoImage.valid() )
                {
                    imageKey = imageKey.createParentKey();
                    isFallbackData = true;
                }
            }
        }

        GeoLocator* locator = 0L;

        if ( !geoImage.valid() )
        {
            // no image found, so make an empty one (one pixel alpha).
            geoImage = GeoImage( ImageUtils::createEmptyImage(), _key.getExtent() );
            locator = GeoLocator::createForKey( _key, *_mapInfo );
            isFallbackData = true;
        }
        else
        {
            if ( useMercatorFastPath )
                locator = new MercatorLocator(geoImage.getExtent());
            else
                locator = GeoLocator::createForExtent(geoImage.getExtent(), *_mapInfo);
        }

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

        if (geoImage.getImage() && isStreaming)
        {
            // protected against multi threaded access. This is a requirement in sequential/preemptive mode, 
            // for example. This used to be in TextureCompositorTexArray::prepareImage.
            // TODO: review whether this affects performance.    
            geoImage.getImage()->setDataVariance( osg::Object::DYNAMIC );
        }

        // add the color layer to the repo.
        _repo->add( CustomColorLayer(
            _layer,
            geoImage.getImage(),
            locator,
            _key.getLevelOfDetail(),
            _key,
            isFallbackData ) );
    }
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);
}
예제 #16
0
GeoImage
ImageLayer::assembleImageFromTileSource(const TileKey&    key,
                                        ProgressCallback* progress)
{
    GeoImage mosaicedImage, result;

    // Scale the extent if necessary to apply an "edge buffer"
    GeoExtent ext = key.getExtent();
    if ( _runtimeOptions.edgeBufferRatio().isSet() )
    {
        double ratio = _runtimeOptions.edgeBufferRatio().get();
        ext.scale(ratio, ratio);
    }

    // Get a set of layer tiles that intersect the requested extent.
    std::vector<TileKey> intersectingKeys;
    getProfile()->getIntersectingTiles( key, intersectingKeys );

    if ( intersectingKeys.size() > 0 )
    {
        double dst_minx, dst_miny, dst_maxx, dst_maxy;
        key.getExtent().getBounds(dst_minx, dst_miny, dst_maxx, dst_maxy);

        // if we find at least one "real" tile in the mosaic, then the whole result tile is
        // "real" (i.e. not a fallback tile)
        bool retry = false;
        ImageMosaic mosaic;

        // keep track of failed tiles.
        std::vector<TileKey> failedKeys;

        for( std::vector<TileKey>::iterator k = intersectingKeys.begin(); k != intersectingKeys.end(); ++k )
        {
            GeoImage image = createImageFromTileSource( *k, progress );

            if ( image.valid() )
            {
                if ( !isCoverage() )
                {
                    ImageUtils::fixInternalFormat(image.getImage());

                    // Make sure all images in mosaic are based on "RGBA - unsigned byte" pixels.
                    // This is not the smarter choice (in some case RGB would be sufficient) but
                    // it ensure consistency between all images / layers.
                    //
                    // The main drawback is probably the CPU memory foot-print which would be reduced by allocating RGB instead of RGBA images.
                    // On GPU side, this should not change anything because of data alignements : often RGB and RGBA textures have the same memory footprint
                    //
                    if (   (image.getImage()->getDataType() != GL_UNSIGNED_BYTE)
                        || (image.getImage()->getPixelFormat() != GL_RGBA) )
                    {
                        osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(image.getImage());
                        if (convertedImg.valid())
                        {
                            image = GeoImage(convertedImg, image.getExtent());
                        }
                    }
                }

                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
            }
            else
            {
                // the tile source did not return a tile, so make a note of it.
                failedKeys.push_back( *k );

                if (progress && (progress->isCanceled() || progress->needsRetry()))
                {
                    retry = true;
                    break;
                }
            }
        }

        if ( mosaic.getImages().empty() || retry )
        {
            // if we didn't get any data, fail.
            OE_DEBUG << LC << "Couldn't create image for ImageMosaic " << std::endl;
            return GeoImage::INVALID;
        }

        // We got at least one good tile, so go through the bad ones and try to fall back on
        // lower resolution data to fill in the gaps. The entire mosaic must be populated or
        // this qualifies as a bad tile.
        for(std::vector<TileKey>::iterator k = failedKeys.begin(); k != failedKeys.end(); ++k)
        {
            GeoImage image;

            for(TileKey parentKey = k->createParentKey();
                parentKey.valid() && !image.valid();
                parentKey = parentKey.createParentKey())
            {
                image = createImageFromTileSource( parentKey, progress );
                if ( image.valid() )
                {
                    GeoImage cropped;

                    if ( !isCoverage() )
                    {
                        ImageUtils::fixInternalFormat(image.getImage());
                        if (   (image.getImage()->getDataType() != GL_UNSIGNED_BYTE)
                            || (image.getImage()->getPixelFormat() != GL_RGBA) )
                        {
                            osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(image.getImage());
                            if (convertedImg.valid())
                            {
                                image = GeoImage(convertedImg, image.getExtent());
                            }
                        }

                        cropped = image.crop( k->getExtent(), false, image.getImage()->s(), image.getImage()->t() );
                    }

                    else
                    {
                        // TODO: may not work.... test; tilekey extent will <> cropped extent
                        cropped = image.crop( k->getExtent(), true, image.getImage()->s(), image.getImage()->t(), false );
                    }

                    // and queue it.
                    mosaic.getImages().push_back( TileImage(cropped.getImage(), *k) );       

                }
            }

            if ( !image.valid() )
            {
                // a tile completely failed, even with fallback. Eject.
                OE_DEBUG << LC << "Couldn't fallback on tiles for ImageMosaic" << std::endl;
                // let it go. The empty areas will be filled with alpha by ImageMosaic.
            }
        }

        // all set. Mosaic all the images together.
        double rxmin, rymin, rxmax, rymax;
        mosaic.getExtents( rxmin, rymin, rxmax, rymax );

        mosaicedImage = GeoImage(
            mosaic.createImage(),
            GeoExtent( getProfile()->getSRS(), rxmin, rymin, rxmax, rymax ) );
    }
    else
    {
        OE_DEBUG << LC << "assembleImageFromTileSource: no intersections (" << key.str() << ")" << std::endl;
    }

    // Final step: transform the mosaic into the requesting key's extent.
    if ( mosaicedImage.valid() )
    {
        // GeoImage::reproject() will automatically crop the image to the correct extents.
        // so there is no need to crop after reprojection. Also note that if the SRS's are the 
        // same (even though extents are different), then this operation is technically not a
        // reprojection but merely a resampling.

        result = mosaicedImage.reproject( 
            key.getProfile()->getSRS(),
            &key.getExtent(), 
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.driver()->bilinearReprojection() );
    }

    // Process images with full alpha to properly support MP blending.
    if ( result.valid() && *_runtimeOptions.featherPixels() && !isCoverage() )
    {
        ImageUtils::featherAlphaRegions( result.getImage() );
    }

    return result;
}
예제 #17
0
GeoImage
ImageLayer::createImageInKeyProfile(const TileKey&    key, 
                                    ProgressCallback* progress)
{
    if (getStatus().isError())
    {
        return GeoImage::INVALID;
    }

    // If the layer is disabled, bail out.
    if ( !getEnabled() )
    {
        return GeoImage::INVALID;
    }

    // Make sure the request is in range.
    if ( !isKeyInRange(key) )
    {
        return GeoImage::INVALID;
    }


    GeoImage result;

    OE_DEBUG << LC << "create image for \"" << key.str() << "\", ext= "
        << key.getExtent().toString() << std::endl;

    // the cache key combines the Key and the horizontal profile.
    std::string cacheKey = Stringify() << key.str() << "_" << key.getProfile()->getHorizSignature();
    const CachePolicy& policy = getCacheSettings()->cachePolicy().get();
    
    // Check the layer L2 cache first
    if ( _memCache.valid() )
    {
        CacheBin* bin = _memCache->getOrCreateDefaultBin();
        ReadResult result = bin->readObject(cacheKey, 0L);
        if ( result.succeeded() )
            return GeoImage(static_cast<osg::Image*>(result.releaseObject()), key.getExtent());
    }

    // locate the cache bin for the target profile for this layer:
    CacheBin* cacheBin = getCacheBin( key.getProfile() );

    // validate that we have either a valid tile source, or we're cache-only.
    if (getTileSource() || (cacheBin && policy.isCacheOnly()))
    {
        //nop = OK.
    }
    else
    {
        disable("Error: layer does not have a valid TileSource, cannot create image");
        return GeoImage::INVALID;
    }

    // validate the existance of a valid layer profile (unless we're in cache-only mode, in which
    // case there is no layer profile)
    if ( !policy.isCacheOnly() && !getProfile() )
    {
        disable("Could not establish a valid profile");
        return GeoImage::INVALID;
    }

    osg::ref_ptr< osg::Image > cachedImage;

    // First, attempt to read from the cache. Since the cached data is stored in the
    // map profile, we can try this first.
    if ( cacheBin && policy.isCacheReadable() )
    {
        ReadResult r = cacheBin->readImage(cacheKey, 0L);
        if ( r.succeeded() )
        {
            cachedImage = r.releaseImage();
            ImageUtils::fixInternalFormat( cachedImage.get() );            
            bool expired = policy.isExpired(r.lastModifiedTime());
            if (!expired)
            {
                OE_DEBUG << "Got cached image for " << key.str() << std::endl;                
                return GeoImage( cachedImage.get(), key.getExtent() );                        
            }
            else
            {
                OE_DEBUG << "Expired image for " << key.str() << std::endl;                
            }
        }
    }
    
    // The data was not in the cache. If we are cache-only, fail sliently
    if ( policy.isCacheOnly() )
    {
        // If it's cache only and we have an expired but cached image, just return it.
        if (cachedImage.valid())
        {
            return GeoImage( cachedImage.get(), key.getExtent() );            
        }
        else
        {
            return GeoImage::INVALID;
        }
    }

    // Get an image from the underlying TileSource.
    result = createImageFromTileSource( key, progress );

    // Normalize the image if necessary
    if ( result.valid() )
    {
        ImageUtils::fixInternalFormat( result.getImage() );
    }

    // memory cache first:
    if ( result.valid() && _memCache.valid() )
    {
        CacheBin* bin = _memCache->getOrCreateDefaultBin();
        bin->write(cacheKey, result.getImage(), 0L);
    }

    // If we got a result, the cache is valid and we are caching in the map profile,
    // write to the map cache.
    if (result.valid()  &&
        cacheBin        && 
        policy.isCacheWriteable())
    {
        if ( key.getExtent() != result.getExtent() )
        {
            OE_INFO << LC << "WARNING! mismatched extents." << std::endl;
        }

        cacheBin->write(cacheKey, result.getImage(), 0L);
    }

    if ( result.valid() )
    {
        OE_DEBUG << LC << key.str() << " result OK" << std::endl;
    }
    else
    {
        OE_DEBUG << LC << key.str() << "result INVALID" << std::endl;        
        // We couldn't get an image from the source.  So see if we have an expired cached image
        if (cachedImage.valid())
        {
            OE_DEBUG << LC << "Using cached but expired image for " << key.str() << std::endl;
            result = GeoImage( cachedImage.get(), key.getExtent());
        }
    }

    return result;
}
예제 #18
0
GeoImage
ImageLayer::createImageInNativeProfile(const TileKey&    key,
                                       ProgressCallback* progress)
{
    if (getStatus().isError())
    {
        return GeoImage::INVALID;
    }

    const Profile* nativeProfile = getProfile();
    if ( !nativeProfile )
    {
        OE_WARN << LC << "Could not establish the profile" << std::endl;
        return GeoImage::INVALID;
    }
    

    GeoImage result;

    if ( key.getProfile()->isHorizEquivalentTo(nativeProfile) )
    {
        // requested profile matches native profile, move along.
        result = createImageInKeyProfile( key, progress );
    }
    else
    {
        // find the intersection of keys.
        std::vector<TileKey> nativeKeys;
        nativeProfile->getIntersectingTiles(key, nativeKeys);

        // build a mosaic of the images from the native profile keys:
        bool foundAtLeastOneRealTile = false;

        ImageMosaic mosaic;
        for( std::vector<TileKey>::iterator k = nativeKeys.begin(); k != nativeKeys.end(); ++k )
        {
            bool isFallback = false;
            GeoImage image = createImageInKeyProfile( *k, progress );
            if ( image.valid() )
            {
                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
            }
            else
            {
                // if we get EVEN ONE invalid tile, we have to abort because there will be
                // empty spots in the mosaic. (By "invalid" we mean a tile that could not
                // even be resolved through the fallback procedure.)
                return GeoImage::INVALID;
                //TODO: probably need to change this so the mosaic uses alpha.
            }
        }

        // bail out if we got nothing.
        if ( mosaic.getImages().size() > 0 )
        {
            // assemble new GeoImage from the mosaic.
            double rxmin, rymin, rxmax, rymax;
            mosaic.getExtents( rxmin, rymin, rxmax, rymax );

            result = GeoImage(
                mosaic.createImage(), 
                GeoExtent( nativeProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );
        }
    }

    return result;
}
예제 #19
0
void
FractalElevationLayer::createImplementation(const TileKey& key,
                                            osg::ref_ptr<osg::HeightField>& out_hf,
                                            osg::ref_ptr<NormalMap>& out_normalMap,
                                            ProgressCallback* progress)
{
    double min_n = FLT_MAX, max_n = -FLT_MAX;
    double min_h = FLT_MAX, max_h = -FLT_MAX;
    double h_mean = 0.0;

    ImageUtils::PixelReader noise1(_noiseImage1.get());
    noise1.setBilinear(true);

    ImageUtils::PixelReader noise2(_noiseImage2.get());
    noise2.setBilinear(true);

    osg::ref_ptr<osg::HeightField> hf = HeightFieldUtils::createReferenceHeightField(
        key.getExtent(), getTileSize(), getTileSize(), 0u);

    // land cover tile
    GeoImage lcTile;

    osg::ref_ptr<LandCoverLayer> lcLayer;
    _landCover.lock(lcLayer);

    if (lcLayer.valid())
    {
        lcTile = lcLayer->createImage(key, progress);
    }

    for (int s = 0; s < getTileSize(); ++s)
    {
        for (int t = 0; t < getTileSize(); ++t)
        {
            double u = (double)s / (double)(getTileSize() - 1);
            double v = (double)t / (double)(getTileSize() - 1);

            double n = 0.0;
            double uScaled, vScaled;

            double finalScale = 4.0;

            // Step 1
            if (_noiseImage1.valid())
            {
                uScaled = u, vScaled = v;
                scaleCoordsToLOD(uScaled, vScaled, options().baseLOD().get(), key);

                double uMod = fmod(uScaled, 1.0);
                double vMod = fmod(vScaled, 1.0);

                n += noise1(uMod, vMod).r() - 0.5;
                finalScale *= 0.5;
            }

            if (_noiseImage2.valid())
            {
                uScaled = u, vScaled = v;
                scaleCoordsToLOD(uScaled, vScaled, options().baseLOD().get() + 3, key);

                double uMod = fmod(uScaled, 1.0);
                double vMod = fmod(vScaled, 1.0);
                n += noise2(uMod, vMod).r() - 0.5;
                finalScale *= 0.5;
            }

            n *= finalScale;

            // default amplitude:
            float amp = options().amplitude().get();

            // if we have land cover mappings, use them:
            if (lcTile.valid())
            {
                const LandCoverClass* lcClass = lcLayer->getClassByUV(lcTile, u, v);
                if (lcClass)
                {
                    const FractalElevationLayerLandCoverMapping* mapping = getMapping(lcClass);
                    if (mapping)
                    {
                        amp = mapping->amplitude.getOrUse(amp);
                    }
                }
            }

            hf->setHeight(s, t, n * amp);

            if (_debug)
            {
                h_mean += hf->getHeight(s, t);
                min_n = std::min(min_n, n);
                max_n = std::max(max_n, n);
                min_h = std::min(min_h, (double)hf->getHeight(s, t));
                max_h = std::max(max_h, (double)hf->getHeight(s, t));
            }
        }
    }

    if (_debug)
    {
        h_mean /= double(getTileSize()*getTileSize());
        double q_mean = 0.0;

        for (int s = 0; s < getTileSize(); ++s)
        {
            for (int t = 0; t < getTileSize(); ++t)
            {
                double q = hf->getHeight(s, t) - h_mean;
                q_mean += q*q;
            }
        }

        double stdev = sqrt(q_mean / double(getTileSize()*getTileSize()));

        OE_INFO << LC << "Tile " << key.str() << " Hmean=" << h_mean
            << ", stdev=" << stdev << ", n[" << min_n << ", " << max_n << "] "
            << "h[" << min_h << ", " << max_h << "]\n";
    }

    out_hf = hf.release();
    out_normalMap = 0L;
}
예제 #20
0
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;
    }



}
예제 #21
0
GeoImage
ImageLayer::createImageInNativeProfile( const TileKey& key, ProgressCallback* progress, bool forceFallback, bool& out_isFallback)
{
    out_isFallback = false;

    const Profile* nativeProfile = getProfile();
    if ( !nativeProfile )
    {
        OE_WARN << LC << "Could not establish the profile" << std::endl;
        return GeoImage::INVALID;
    }

    if ( key.getProfile()->isEquivalentTo(nativeProfile) )
    {
        // requested profile matches native profile, move along.
        return createImageInKeyProfile( key, progress, forceFallback, out_isFallback );
    }
    else
    {
        // find the intersection of keys.
        std::vector<TileKey> nativeKeys;
        nativeProfile->getIntersectingTiles(key.getExtent(), nativeKeys);


        //OE_INFO << "KEY = " << key.str() << ":" << std::endl;
        //for(int i=0; i<nativeKeys.size(); ++i)
        //    OE_INFO << "    " << nativeKeys[i].str() << std::endl;
        
        // build a mosaic of the images from the native profile keys:
        bool foundAtLeastOneRealTile = false;

        ImageMosaic mosaic;
        for( std::vector<TileKey>::iterator k = nativeKeys.begin(); k != nativeKeys.end(); ++k )
        {
            bool isFallback = false;
            GeoImage image = createImageInKeyProfile( *k, progress, true, isFallback );
            if ( image.valid() )
            {
                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
                if ( !isFallback )
                    foundAtLeastOneRealTile = true;
            }
            else
            {
                // if we get EVEN ONE invalid tile, we have to abort because there will be
                // empty spots in the mosaic. (By "invalid" we mean a tile that could not
                // even be resolved through the fallback procedure.)
                return GeoImage::INVALID;
            }
        }

        // bail out if we got nothing.
        if ( mosaic.getImages().size() == 0 )
            return GeoImage::INVALID;

        // if the mosaic is ALL fallback data, this tile is fallback data.
        if ( foundAtLeastOneRealTile )
        {
            // assemble new GeoImage from the mosaic.
            double rxmin, rymin, rxmax, rymax;
            mosaic.getExtents( rxmin, rymin, rxmax, rymax );

            GeoImage result( 
                mosaic.createImage(), 
                GeoExtent( nativeProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );

#if 1
            return result;

#else // let's try this. why crop? Just leave it. Faster and more compatible with NPOT
      // systems (like iOS)

            // calculate a tigher extent that matches the original input key:
            GeoExtent tightExtent = nativeProfile->clampAndTransformExtent( key.getExtent() );

            // a non-exact crop is critical here to avoid resampling the data
            return result.crop( tightExtent, false, 0, 0, *_runtimeOptions.driver()->bilinearReprojection() );
#endif
        }

        else // all fallback data
        {
            GeoImage result;

            if ( forceFallback && key.getLevelOfDetail() > 0 )
            {
                result = createImageInNativeProfile(
                    key.createParentKey(),
                    progress,
                    forceFallback,
                    out_isFallback );
            }

            out_isFallback = true;
            return result;
        }

        //if ( !foundAtLeastOneRealTile )
        //    out_isFallback = true;

    }
}
예제 #22
0
GeoImage
ImageLayer::assembleImageFromTileSource(const TileKey&    key,
                                        ProgressCallback* progress,
                                        bool&             out_isFallback)
{
    GeoImage mosaicedImage, result;

    out_isFallback = false;

    // Scale the extent if necessary to apply an "edge buffer"
    GeoExtent ext = key.getExtent();
    if ( _runtimeOptions.edgeBufferRatio().isSet() )
    {
        double ratio = _runtimeOptions.edgeBufferRatio().get();
        ext.scale(ratio, ratio);
    }

    // Get a set of layer tiles that intersect the requested extent.
    std::vector<TileKey> intersectingKeys;
    getProfile()->getIntersectingTiles( ext, intersectingKeys );

    if ( intersectingKeys.size() > 0 )
    {
        double dst_minx, dst_miny, dst_maxx, dst_maxy;
        key.getExtent().getBounds(dst_minx, dst_miny, dst_maxx, dst_maxy);

        // if we find at least one "real" tile in the mosaic, then the whole result tile is
        // "real" (i.e. not a fallback tile)
        bool foundAtLeastOneRealTile = false;
        bool retry = false;
        ImageMosaic mosaic;

        for( std::vector<TileKey>::iterator k = intersectingKeys.begin(); k != intersectingKeys.end(); ++k )
        {
            double minX, minY, maxX, maxY;
            k->getExtent().getBounds(minX, minY, maxX, maxY);

            bool isFallback = false;
            GeoImage image = createImageFromTileSource( *k, progress, true, isFallback );
            if ( image.valid() )
            {
                // make sure the image is RGBA.
                // (TODO: investigate whether we still need this -gw 6/25/2012)
                if (image.getImage()->getPixelFormat() != GL_RGBA || image.getImage()->getDataType() != GL_UNSIGNED_BYTE || image.getImage()->getInternalTextureFormat() != GL_RGBA8 )
                {
                    osg::ref_ptr<osg::Image> convertedImg = ImageUtils::convertToRGBA8(image.getImage());
                    if (convertedImg.valid())
                    {
                        image = GeoImage(convertedImg, image.getExtent());
                    }
                }

                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
                if ( !isFallback )
                    foundAtLeastOneRealTile = true;
            }
            else
            {
                // the tile source did not return a tile, so make a note of it.
                if (progress && (progress->isCanceled() || progress->needsRetry()))
                {
                    retry = true;
                    break;
                }
            }
        }

        if ( mosaic.getImages().empty() || retry )
        {
            // if we didn't get any data, fail
            OE_DEBUG << LC << "Couldn't create image for ImageMosaic " << std::endl;
            return GeoImage::INVALID;
        }

        // all set. Mosaic all the images together.
        double rxmin, rymin, rxmax, rymax;
        mosaic.getExtents( rxmin, rymin, rxmax, rymax );

        mosaicedImage = GeoImage(
            mosaic.createImage(),
            GeoExtent( getProfile()->getSRS(), rxmin, rymin, rxmax, rymax ) );

        if ( !foundAtLeastOneRealTile )
            out_isFallback = true;
    }
    else
    {
        OE_DEBUG << LC << "assembleImageFromTileSource: no intersections (" << key.str() << ")" << std::endl;
    }

    // Final step: transform the mosaic into the requesting key's extent.
    if ( mosaicedImage.valid() )
    {
        // GeoImage::reproject() will automatically crop the image to the correct extents.
        // so there is no need to crop after reprojection. Also note that if the SRS's are the 
        // same (even though extents are different), then this operation is technically not a
        // reprojection but merely a resampling.

        result = mosaicedImage.reproject( 
            key.getProfile()->getSRS(),
            &key.getExtent(), 
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.reprojectedTileSize(),
            *_runtimeOptions.driver()->bilinearReprojection());
    }

    // Process images with full alpha to properly support MP blending.
    if ( result.valid() && *_runtimeOptions.featherPixels() )
    {
        ImageUtils::featherAlphaRegions( result.getImage() );
    }

    return result;
}
예제 #23
0
GeoImage
ImageLayer::createImageInNativeProfile(const TileKey&    key,
                                       ProgressCallback* progress)
{
    if (getStatus().isError())
    {
        return GeoImage::INVALID;
    }

    const Profile* nativeProfile = getProfile();
    if ( !nativeProfile )
    {
        OE_WARN << LC << "Could not establish the profile" << std::endl;
        return GeoImage::INVALID;
    }
    

    GeoImage result;

    if ( key.getProfile()->isHorizEquivalentTo(nativeProfile) )
    {
        // requested profile matches native profile, move along.
        result = createImageInKeyProfile( key, progress );
    }
    else
    {
        // find the intersection of keys.
        std::vector<TileKey> nativeKeys;
        nativeProfile->getIntersectingTiles(key, nativeKeys);

        // build a mosaic of the images from the native profile keys:
        bool foundAtLeastOneRealTile = false;

        ImageMosaic mosaic;
        for( std::vector<TileKey>::iterator k = nativeKeys.begin(); k != nativeKeys.end(); ++k )
        {
            GeoImage image = createImageInKeyProfile( *k, progress );
            if ( image.valid() )
            {
                foundAtLeastOneRealTile = true;
                mosaic.getImages().push_back( TileImage(image.getImage(), *k) );
            }
            else
            {
                // We didn't get an image so pad the mosaic with a transparent image.
                mosaic.getImages().push_back( TileImage(ImageUtils::createEmptyImage(getTileSize(), getTileSize()), *k));
            }
        }

        // bail out if we got nothing.
        if ( foundAtLeastOneRealTile )
        {
            // assemble new GeoImage from the mosaic.
            double rxmin, rymin, rxmax, rymax;
            mosaic.getExtents( rxmin, rymin, rxmax, rymax );

            result = GeoImage(
                mosaic.createImage(), 
                GeoExtent( nativeProfile->getSRS(), rxmin, rymin, rxmax, rymax ) );
        }
    }

    return result;
}