void CacheSeed::seed( Map* map ) { if ( !map->getCache() ) { OE_WARN << LC << "Warning: No cache defined; aborting." << std::endl; return; } std::vector<TileKey> keys; map->getProfile()->getRootKeys(keys); //Add the map's entire extent if we don't have one specified. if (_extents.empty()) { addExtent( map->getProfile()->getExtent() ); } bool hasCaches = false; int src_min_level = INT_MAX; unsigned int src_max_level = 0; MapFrame mapf( map, Map::TERRAIN_LAYERS, "CacheSeed::seed" ); //Assumes the the TileSource will perform the caching for us when we call createImage for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); i++ ) { ImageLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ImageLayerOptions& opt = layer->getImageLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if ( !src ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } else if ( src->getCachePolicyHint() == CachePolicy::NO_CACHE ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; } else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } for( ElevationLayerVector::const_iterator i = mapf.elevationLayers().begin(); i != mapf.elevationLayers().end(); i++ ) { ElevationLayer* layer = i->get(); TileSource* src = layer->getTileSource(); const ElevationLayerOptions& opt = layer->getElevationLayerOptions(); if ( layer->isCacheOnly() ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" is set to cache-only; skipping." << std::endl; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource; skipping." << std::endl; } else if ( src->getCachePolicyHint() == CachePolicy::NO_CACHE ) { OE_WARN << LC << "Warning: Layer \"" << layer->getName() << "\" does not support seeding; skipping." << std::endl; } else if ( !layer->getCache() ) { OE_WARN << LC << "Notice: Layer \"" << layer->getName() << "\" has no cache defined; skipping." << std::endl; } else { hasCaches = true; if (opt.minLevel().isSet() && (int)opt.minLevel().get() < src_min_level) src_min_level = opt.minLevel().get(); if (opt.maxLevel().isSet() && opt.maxLevel().get() > src_max_level) src_max_level = opt.maxLevel().get(); } } if ( !hasCaches ) { OE_WARN << LC << "There are either no caches defined in the map, or no sources to cache; aborting." << std::endl; return; } if ( src_max_level > 0 && src_max_level < _maxLevel ) { _maxLevel = src_max_level; } OE_NOTICE << LC << "Maximum cache level will be " << _maxLevel << std::endl; osg::Timer_t startTime = osg::Timer::instance()->tick(); //Estimate the number of tiles _total = 0; for (unsigned int level = _minLevel; level <= _maxLevel; level++) { double coverageRatio = 0.0; if (_extents.empty()) { unsigned int wide, high; map->getProfile()->getNumTiles( level, wide, high ); _total += (wide * high); } else { for (std::vector< GeoExtent >::const_iterator itr = _extents.begin(); itr != _extents.end(); itr++) { const GeoExtent& extent = *itr; double boundsArea = extent.area(); TileKey ll = map->getProfile()->createTileKey(extent.xMin(), extent.yMin(), level); TileKey ur = map->getProfile()->createTileKey(extent.xMax(), extent.yMax(), level); int tilesWide = ur.getTileX() - ll.getTileX() + 1; int tilesHigh = ll.getTileY() - ur.getTileY() + 1; int tilesAtLevel = tilesWide * tilesHigh; //OE_NOTICE << "Tiles at level " << level << "=" << tilesAtLevel << std::endl; bool hasData = false; for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); itr++) { TileSource* src = itr->get()->getTileSource(); if (src) { if (src->hasDataAtLOD( level )) { //Compute the percent coverage of this dataset on the current extent if (src->getDataExtents().size() > 0) { double cov = 0.0; for (unsigned int j = 0; j < src->getDataExtents().size(); j++) { GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS()); GeoExtent intersection = b.intersectionSameSRS( extent ); if (intersection.isValid()) { double coverage = intersection.area() / boundsArea; cov += coverage; //Assumes the extents aren't overlapping } } if (coverageRatio < cov) coverageRatio = cov; } else { //We have no way of knowing how much coverage we have coverageRatio = 1.0; } hasData = true; break; } } } for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); itr++) { TileSource* src = itr->get()->getTileSource(); if (src) { if (src->hasDataAtLOD( level )) { //Compute the percent coverage of this dataset on the current extent if (src->getDataExtents().size() > 0) { double cov = 0.0; for (unsigned int j = 0; j < src->getDataExtents().size(); j++) { GeoExtent b = src->getDataExtents()[j].transform( extent.getSRS()); GeoExtent intersection = b.intersectionSameSRS( extent ); if (intersection.isValid()) { double coverage = intersection.area() / boundsArea; cov += coverage; //Assumes the extents aren't overlapping } } if (coverageRatio < cov) coverageRatio = cov; } else { //We have no way of knowing how much coverage we have coverageRatio = 1.0; } hasData = true; break; } } } //Adjust the coverage ratio by a fudge factor to try to keep it from being too small, //tiles are either processed or not and the ratio is exact so will cover tiles partially //and potentially be too small double adjust = 4.0; coverageRatio = osg::clampBetween(coverageRatio * adjust, 0.0, 1.0); //OE_NOTICE << level << " CoverageRatio = " << coverageRatio << std::endl; if (hasData) { _total += (int)ceil(coverageRatio * (double)tilesAtLevel ); } } } } //Adjust the # of tiles again to be bigger than computed to avoid giving false hope _total *= 2; osg::Timer_t endTime = osg::Timer::instance()->tick(); //OE_NOTICE << "Counted tiles in " << osg::Timer::instance()->delta_s(startTime, endTime) << " s" << std::endl; OE_INFO << "Processing ~" << _total << " tiles" << std::endl; for (unsigned int i = 0; i < keys.size(); ++i) { processKey( mapf, keys[i] ); } _total = _completed; if ( _progress.valid()) _progress->reportProgress(_completed, _total, 0, 1, "Finished"); }
GeoImage ImageLayer::createImageFromTileSource(const TileKey& key, ProgressCallback* progress, bool forceFallback, bool& out_isFallback) { // Results: // // * return an osg::Image matching the key extent is all goes well; // // * return NULL to indicate that the key exceeds the maximum LOD of the source data, // and that the engine may need to generate a "fallback" tile if necessary; // // deprecated: // * return an "empty image" if the LOD is valid BUT the key does not intersect the // source's data extents. out_isFallback = false; TileSource* source = getTileSource(); if ( !source ) return GeoImage::INVALID; // If the profiles are different, use a compositing method to assemble the tile. if ( !key.getProfile()->isEquivalentTo( getProfile() ) ) { return assembleImageFromTileSource( key, progress, out_isFallback ); } // Fail is the image is blacklisted. // ..unless there will be a fallback attempt. if ( source->getBlacklist()->contains( key.getTileId() ) && !forceFallback ) { OE_DEBUG << LC << "createImageFromTileSource: blacklisted(" << key.str() << ")" << std::endl; return GeoImage::INVALID; } // Fail if no data is available for this key. if ( !source->hasDataAtLOD( key.getLevelOfDetail() ) && !forceFallback ) { OE_DEBUG << LC << "createImageFromTileSource: hasDataAtLOD(" << key.str() << ") == false" << std::endl; return GeoImage::INVALID; } if ( !source->hasDataInExtent( key.getExtent() ) ) { OE_DEBUG << LC << "createImageFromTileSource: hasDataInExtent(" << key.str() << ") == false" << std::endl; return GeoImage::INVALID; } // Good to go, ask the tile source for an image: osg::ref_ptr<TileSource::ImageOperation> op = _preCacheOp; osg::ref_ptr<osg::Image> result; TileKey finalKey = key; bool fellBack = false; if ( forceFallback ) { while( !result.valid() && finalKey.valid() ) { if ( !source->getBlacklist()->contains( finalKey.getTileId() ) ) { result = source->createImage( finalKey, op.get(), progress ); if ( result.valid() ) { if ( finalKey.getLevelOfDetail() != key.getLevelOfDetail() ) { // crop the fallback image to match the input key, and ensure that it remains the // same pixel size; because chances are if we're requesting a fallback that we're // planning to mosaic it later, and the mosaicer requires same-size images. GeoImage raw( result.get(), finalKey.getExtent() ); GeoImage cropped = raw.crop( key.getExtent(), true, raw.getImage()->s(), raw.getImage()->t(), *_runtimeOptions.driver()->bilinearReprojection() ); result = cropped.takeImage(); fellBack = true; } } } if ( !result.valid() ) { finalKey = finalKey.createParentKey(); out_isFallback = true; } } if ( !result.valid() ) { result = 0L; //result = _emptyImage.get(); finalKey = key; } } else { result = source->createImage( key, op.get(), progress ); } // Process images with full alpha to properly support MP blending. if ( result != 0L ) { ImageUtils::featherAlphaRegions( result.get() ); } // If image creation failed (but was not intentionally canceled), // blacklist this tile for future requests. if ( result == 0L && (!progress || !progress->isCanceled()) ) { source->getBlacklist()->add( key.getTileId() ); } return GeoImage(result.get(), key.getExtent()); }