예제 #1
0
void
OSGTerrainEngineNode::addImageLayer( ImageLayer* layerAdded )
{
    if ( !layerAdded || !layerAdded->getTileSource() )
        return;

    // 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();
}
예제 #2
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() )
    {
        ImageUtils::featherAlphaRegions( result.getImage() );
    }

    return result;
}
void
TextureCompositorTexArray::applyLayerUpdate(osg::StateSet*       stateSet,
                                            UID                  layerUID,
                                            const GeoImage&      preparedImage,
                                            const TileKey&       tileKey,
                                            const TextureLayout& layout,
                                            osg::StateSet*       parentStateSet) const
{
    GeoExtent tileExtent(tileKey.getExtent());
    int slot = layout.getSlot( layerUID );
    if ( slot < 0 )
        return; // means the layer no longer exists

    // access the texture array, creating or growing it if necessary:
    osg::Texture2DArray* texture = s_getTexture( stateSet, layout, 0,
                                                 textureSize() );
    ensureSampler( stateSet, 0 );
    // assign the new image at the proper position in the texture array.
    osg::Image* image = preparedImage.getImage();
    assignImage(texture, slot, image);
    
    // update the region uniform to reflect the geo extent of the image:
    const GeoExtent& imageExtent = preparedImage.getExtent();
    osg::Vec4 tileTransform;
    getImageTransform(tileExtent, imageExtent, tileTransform);

    // access the region uniform, creating or growing it if necessary:
    ArrayUniform regionUni( "region", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 );
    if ( regionUni.isValid() )
    {
        int layerOffset = slot * 8;
        for (int i = 0; i < 4; ++i)
            regionUni.setElement( layerOffset + i, tileTransform[i]);
        //region->dirty();
    }
    
    if ( layout.isBlendingEnabled( layerUID ) && regionUni.isValid() )
    {
        osg::Uniform* secondarySampler = ensureSampler( stateSet, 1 );
        osg::Texture2DArray* parentTexture = 0;
        const unsigned parentLayerOffset = slot * 8 + 4;
        if ( parentStateSet )
        {
            ArrayUniform parentRegion( "region", osg::Uniform::FLOAT, parentStateSet, layout.getMaxUsedSlot()+1 );

            //osg::Uniform* parentRegion = s_getRegionUniform( parentStateSet,
            //                                                 layout );
            GeoExtent parentExtent(tileKey.createParentKey().getExtent());
            float widthRatio, heightRatio;
            parentRegion.getElement(slot * 8 + 2, widthRatio);
            parentRegion.getElement(slot * 8 + 3, heightRatio);
            float parentImageWidth =  parentExtent.width() / widthRatio;
            float parentImageHeight = parentExtent.height() / heightRatio;
            float xRatio, yRatio;
            parentRegion.getElement(slot * 8, xRatio);
            parentRegion.getElement(slot * 8 + 1, yRatio);
            float ParentImageXmin = parentExtent.xMin() - xRatio * parentImageWidth;
            float ParentImageYmin = parentExtent.yMin() - yRatio * parentImageHeight;
            regionUni.setElement(parentLayerOffset,
                               static_cast<float>((tileExtent.xMin() - ParentImageXmin) / parentImageWidth));
            regionUni.setElement(parentLayerOffset + 1,
                               static_cast<float>((tileExtent.yMin() - ParentImageYmin) / parentImageHeight));
            regionUni.setElement(parentLayerOffset + 2,
                               static_cast<float>(tileExtent.width() / parentImageWidth));
            regionUni.setElement(parentLayerOffset + 3,
                               static_cast<float>(tileExtent.height() / parentImageHeight));
            //regionUni.dirty();
            parentTexture = static_cast<osg::Texture2DArray*>(parentStateSet->getTextureAttribute(0, osg::StateAttribute::TEXTURE));
        }
        else
        {
            // setting the parent transform values to -1 disabled blending for this layer. #hack -gw
            for (int i = 0; i < 4; ++i)
                regionUni.setElement(parentLayerOffset + i, tileTransform[i]);
        }

        if (parentTexture)
            stateSet->setTextureAttribute(1, parentTexture, osg::StateAttribute::ON);
        else
            secondarySampler->set(0);

        // update the timestamp on the image layer to support fade-in blending.
        float now = (float)osg::Timer::instance()->delta_s( osg::Timer::instance()->getStartTick(), osg::Timer::instance()->tick() );
        ArrayUniform stampUniform( "osgearth_SlotStamp", osg::Uniform::FLOAT, stateSet, layout.getMaxUsedSlot()+1 );
        stampUniform.setElement( slot, now );
    }
}
예제 #4
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 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() );
        if ( r.succeeded() )
        {            
            ImageUtils::normalizeImage( r.getImage() );
            return GeoImage( r.releaseImage(), key.getExtent() );
        }
        else
        {
            //OE_INFO << LC << getName() << " : " << key.str() << " cache miss" << 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;
}
예제 #5
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;

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

            GeoImage image = createImageFromTileSource( *k, progress );
            if ( image.valid() )
            {
                ImageUtils::normalizeImage(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.
                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 ) );
    }
    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;
}
예제 #6
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() );
        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;
    }

    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 && getCachePolicy().isCacheReadable() )
    {
        ReadResult r = cacheBin->readImage( key.str() );
        if ( r.succeeded() )
        {
            cachedImage = r.releaseImage();
            ImageUtils::normalizeImage( cachedImage.get() );            
            bool expired = getCachePolicy().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 ( 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::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;        
        // 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;
}
예제 #7
0
GeoImage
ImageLayer::assembleImage(const TileKey& key, ProgressCallback* progress)
{
    // If we got here, asset that there's a non-null layer profile.
    if (!getProfile())
    {
        setStatus(Status::Error(Status::AssertionFailure, "assembleImage with undefined profile"));
        return GeoImage::INVALID;
    }

    GeoImage mosaicedImage, result;

    // Scale the extent if necessary to apply an "edge buffer"
    GeoExtent ext = key.getExtent();
    if ( options().edgeBufferRatio().isSet() )
    {
        double ratio = options().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 = createImageImplementation( *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 = createImageImplementation( 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 << "assembleImage: 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(), 
            options().reprojectedTileSize().get(),
            options().reprojectedTileSize().get(),
            options().driver()->bilinearReprojection().get());
    }

    // Process images with full alpha to properly support MP blending.
    if (result.valid() && 
        options().featherPixels() == true &&
        isCoverage() == false)
    {
        ImageUtils::featherAlphaRegions( result.getImage() );
    }

    return result;
}
예제 #8
0
GeoImage
ImageLayer::createImageInKeyProfile(const TileKey&    key, 
                                    ProgressCallback* progress)
{
    // If the layer is disabled, bail out.
    if ( !getEnabled() )
    {
        return GeoImage::INVALID;
    }

    // Make sure the request is in range.
    // TODO: perhaps this should be a call to mayHaveData(key) instead.
    if ( !isKeyInLegalRange(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() );
    

    // Can we continue? Only if either:
    //  a) there is a valid tile source plugin;
    //  b) a tile source is not expected, meaning the subclass overrides getHeightField; or
    //  c) we are in cache-only mode and there is a valid cache bin.
    bool canContinue =
        getTileSource() ||
        !isTileSourceExpected() ||
        (policy.isCacheOnly() && cacheBin != 0L);

    if (!canContinue)
    {
        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;
        }
    }
    
    if (key.getProfile()->isHorizEquivalentTo(getProfile()))
    {
        result = createImageImplementation(key, progress);
    }
    else
    {
        // If the profiles are different, use a compositing method to assemble the tile.
        result = assembleImage( 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;
}