bool MapEngine::hasMoreLevels( Map* map, const TileKey* key ) { Threading::ScopedReadLock lock( map->getMapDataMutex() ); bool more_levels = false; int max_level = 0; for ( MapLayerList::const_iterator i = map->getImageMapLayers().begin(); i != map->getImageMapLayers().end(); i++ ) { if ( !i->get()->maxLevel().isSet() || key->getLevelOfDetail() < i->get()->maxLevel().get() ) { more_levels = true; break; } } if ( !more_levels ) { for( MapLayerList::const_iterator j = map->getHeightFieldMapLayers().begin(); j != map->getHeightFieldMapLayers().end(); j++ ) { if ( !j->get()->maxLevel().isSet() || key->getLevelOfDetail() < j->get()->maxLevel().get() ) { more_levels = true; break; } } } return more_levels; }
static Config mapToConfig( Map* map, const MapEngineProperties& ep ) { Config conf( ELEM_MAP ); conf.attr( ATTR_NAME ) = map->getName(); conf.add( "engine_properties", ep.toConfig() ); //Write the coordinate system std::string cs; if (map->getCoordinateSystemType() == Map::CSTYPE_GEOCENTRIC) cs = "geocentric"; else if (map->getCoordinateSystemType() == Map::CSTYPE_PROJECTED) cs = "projected"; else if ( map->getCoordinateSystemType() == Map::CSTYPE_GEOCENTRIC_CUBE) cs = "cube"; else { OE_NOTICE << "[osgEarth::EarthFile] Unhandled CoordinateSystemType " << std::endl; return Config(); } conf.attr( ATTR_CSTYPE ) = cs; //Write all the image sources for( MapLayerList::const_iterator i = map->getImageMapLayers().begin(); i != map->getImageMapLayers().end(); i++ ) { conf.add( i->get()->toConfig() ); //conf.add( writeLayer( i->get() ) ); } //Write all the heightfield sources for (MapLayerList::const_iterator i = map->getHeightFieldMapLayers().begin(); i != map->getHeightFieldMapLayers().end(); i++ ) { conf.add( i->get()->toConfig() ); //conf.add( writeLayer( i->get() ) ); } //Write all the model layers for(ModelLayerList::const_iterator i = map->getModelLayers().begin(); i != map->getModelLayers().end(); i++ ) { conf.add( writeLayer( i->get() ) ); } //Terrain mask layer, if necc. if ( map->getTerrainMaskLayer() ) { conf.add( writeLayer( map->getTerrainMaskLayer() ) ); } //TODO: Get this from the getCache call itself, not a CacheConfig. if ( map->cacheConfig().isSet() ) { conf.add( map->cacheConfig()->toConfig( ELEM_CACHE ) ); } if ( map->profileConfig().isSet() ) { conf.add( map->profileConfig()->toConfig( ELEM_PROFILE ) ); } return conf; }
void CacheSeed::seed( Map* map ) { Threading::ScopedReadLock lock( map->getMapDataMutex() ); if (!map->getCache()) { OE_WARN << "Warning: Map does not have a cache defined, please define a cache." << std::endl; return; } osg::ref_ptr<MapEngine> engine = new MapEngine(); //map->createMapEngine(); std::vector< osg::ref_ptr<TileKey> > keys; map->getProfile()->getRootKeys(keys); //Set the default bounds to the entire profile if the user didn't override the bounds if (_bounds.xMin() == 0 && _bounds.yMin() == 0 && _bounds.xMax() == 0 && _bounds.yMax() == 0) { const GeoExtent& mapEx = map->getProfile()->getExtent(); _bounds = Bounds( mapEx.xMin(), mapEx.yMin(), mapEx.xMax(), mapEx.yMax() ); } bool hasCaches = false; int src_min_level = INT_MAX; int src_max_level = 0; //Assumes the the TileSource will perform the caching for us when we call createImage for( MapLayerList::const_iterator i = map->getImageMapLayers().begin(); i != map->getImageMapLayers().end(); i++ ) { MapLayer* layer = i->get(); TileSource* src = i->get()->getTileSource(); if (layer->cacheOnly().get()) { OE_WARN << "Warning: Cannot seed b/c Layer \"" << layer->getName() << "\" is cache only." << std::endl; return; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource." << std::endl; } else if ( !src->supportsPersistentCaching() ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" does not support seeding." << std::endl; } else if ( !layer->getCache() ) { OE_NOTICE << "Notice: Layer \"" << layer->getName() << "\" has no persistent cache defined; skipping." << std::endl; } else { hasCaches = true; if (layer->minLevel().isSet() && layer->minLevel().get() < src_min_level) src_min_level = layer->minLevel().get(); if (layer->maxLevel().isSet() && layer->maxLevel().get() > src_max_level) src_max_level = layer->maxLevel().get(); } } for( MapLayerList::const_iterator i = map->getHeightFieldMapLayers().begin(); i != map->getHeightFieldMapLayers().end(); i++ ) { MapLayer* layer = i->get(); TileSource* src = i->get()->getTileSource(); if (layer->cacheOnly().get()) { OE_WARN << "Warning: Cannot seed b/c Layer \"" << layer->getName() << "\" is cache only." << std::endl; return; } else if (!src) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" could not create TileSource." << std::endl; } else if ( !src->supportsPersistentCaching() ) { OE_WARN << "Warning: Layer \"" << layer->getName() << "\" does not support seeding." << std::endl; } else if ( !layer->getCache() ) { OE_NOTICE << "Notice: Layer \"" << src->getName() << "\" has no persistent cache defined; skipping." << std::endl; } else { hasCaches = true; if (layer->minLevel().isSet() && layer->minLevel().get() < src_min_level) src_min_level = layer->minLevel().get(); if (layer->maxLevel().isSet() && layer->maxLevel().get() > src_max_level) src_max_level = layer->maxLevel().get(); } } if (!hasCaches) { OE_NOTICE << "There are either no caches defined in the map, or no sources to cache. Exiting." << std::endl; return; } if ( src_max_level > 0 && src_max_level < _maxLevel ) { _maxLevel = src_max_level; } OE_NOTICE << "Maximum cache level will be " << _maxLevel << std::endl; for (unsigned int i = 0; i < keys.size(); ++i) { processKey( map, engine.get(), keys[i].get() ); } }
osg::Node* MapEngine::createPopulatedTile( Map* map, VersionedTerrain* terrain, const TileKey* key, bool wrapInPagedLOD, bool fallback, bool &validData ) { Threading::ScopedReadLock lock( map->getMapDataMutex() ); bool isProjected = map->getCoordinateSystemType() == Map::CSTYPE_PROJECTED; bool isPlateCarre = isProjected && map->getProfile()->getSRS()->isGeographic(); bool isGeocentric = !isProjected; double xmin, ymin, xmax, ymax; key->getGeoExtent().getBounds( xmin, ymin, xmax, ymax ); GeoImageList image_tiles; const MapLayerList& imageMapLayers = map->getImageMapLayers(); const MapLayerList& hfMapLayers = map->getHeightFieldMapLayers(); // Collect the image layers bool empty_map = imageMapLayers.size() == 0 && hfMapLayers.size() == 0; // Whether to use a special mercator locator instead of reprojecting data to spherical mercator bool useMercatorLocator = true; // Create the images for the tile for( MapLayerList::const_iterator i = imageMapLayers.begin(); i != imageMapLayers.end(); i++ ) { MapLayer* layer = i->get(); osg::ref_ptr<GeoImage> image; //Only create images if the key is valid if ( layer->isKeyValid( key ) ) { if ( _L2cache ) image = _L2cache->createImage( layer, key ); else image = layer->createImage( key ); } image_tiles.push_back(image.get()); // if any one of the layers explicity disables the merc fast path, disable for the whole thing: if ( layer->useMercatorFastPath().isSetTo( false ) ) useMercatorLocator = false; } bool hasElevation = false; //Create the heightfield for the tile osg::ref_ptr<osg::HeightField> hf; if ( hfMapLayers.size() > 0 ) { hf = map->createHeightField( key, false, _engineProps.elevationInterpolation().value()); } //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one. Otherwise you can run into the situation //where you could have an inset heightfield on one hemisphere and the whole other hemisphere won't show up. if (map->isGeocentric() && key->getLevelOfDetail() <= 1 && !hf.valid()) { hf = createEmptyHeightField( key ); } hasElevation = hf.valid(); //Determine if we've created any images unsigned int numValidImages = 0; for (unsigned int i = 0; i < image_tiles.size(); ++i) { if (image_tiles[i].valid()) numValidImages++; } //If we couldn't create any imagery or heightfields, bail out if (!hf.valid() && (numValidImages == 0) && !empty_map) { OE_DEBUG << LC << "Could not create any imagery or heightfields for " << key->str() <<". Not building tile" << std::endl; validData = false; //If we're not asked to fallback on previous LOD's and we have no data, return NULL if (!fallback) { return NULL; } } else { validData = true; } //Try to interpolate any missing image layers from parent tiles for (unsigned int i = 0; i < imageMapLayers.size(); i++ ) { if (!image_tiles[i].valid()) { GeoImage* image = NULL; if (imageMapLayers[i]->isKeyValid(key)) { //If the key was valid and we have no image, then something possibly went wrong with the image creation such as a server being busy. image = createValidGeoImage(imageMapLayers[i].get(), key); } //If we still couldn't create an image, either something is really wrong or the key wasn't valid, so just create a transparent placeholder image if (!image) { //If the image is not valid, create an empty texture as a placeholder image = new GeoImage(ImageUtils::createEmptyImage(), key->getGeoExtent()); } //Assign the new image to the proper place in the list image_tiles[i] = image; } } //Fill in missing heightfield information from parent tiles if (!hf.valid()) { //We have no heightfield sources, if ( hfMapLayers.size() == 0 ) { hf = createEmptyHeightField( key ); } else { //Try to get a heightfield again, but this time fallback on parent tiles hf = map->createHeightField( key, true, _engineProps.elevationInterpolation().value()); if (!hf.valid()) { //We couldn't get any heightfield, so just create an empty one. hf = createEmptyHeightField( key ); } else { hasElevation = true; } } } // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees if ( isPlateCarre ) { HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() ); } osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, map ); osgTerrain::HeightFieldLayer* hf_layer = new osgTerrain::HeightFieldLayer(); hf_layer->setLocator( locator.get() ); hf_layer->setHeightField( hf.get() ); VersionedTile* tile = new VersionedTile( key, locator.get() ); tile->setTerrainTechnique( osg::clone(terrain->getTerrainTechniquePrototype(), osg::CopyOp::DEEP_COPY_ALL) ); tile->setVerticalScale( _engineProps.verticalScale().value() ); tile->setLocator( locator.get() ); tile->setElevationLayer( hf_layer ); tile->setRequiresNormals( true ); tile->setDataVariance(osg::Object::DYNAMIC); //Attach an updatecallback to normalize the edges of TerrainTiles. if (hasElevation && _engineProps.normalizeEdges().get() ) { tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback()); tile->setDataVariance(osg::Object::DYNAMIC); } //Assign the terrain system to the TerrainTile. //It is very important the terrain system is set while the MapConfig's sourceMutex is locked. //This registers the terrain tile so that adding/removing layers are always in sync. If you don't do this //you can end up with a situation where the database pager is waiting to merge a tile, then a layer is added, then //the tile is finally merged and is out of sync. double min_units_per_pixel = DBL_MAX; int layer = 0; // create contour layer: if (map->getContourTransferFunction() != NULL) { osgTerrain::ContourLayer* contourLayer(new osgTerrain::ContourLayer(map->getContourTransferFunction())); contourLayer->setMagFilter(_engineProps.getContourMagFilter().value()); contourLayer->setMinFilter(_engineProps.getContourMinFilter().value()); tile->setColorLayer(layer,contourLayer); ++layer; } for (unsigned int i = 0; i < image_tiles.size(); ++i) { if (image_tiles[i].valid()) { double img_xmin, img_ymin, img_xmax, img_ymax; //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image osg::ref_ptr<GeoLocator> img_locator; GeoImage* geo_image = image_tiles[i].get(); // Use a special locator for mercator images (instead of reprojecting) if (map->getProfile()->getSRS()->isGeographic() && geo_image->getSRS()->isMercator() && useMercatorLocator ) { GeoExtent geog_ext = image_tiles[i]->getExtent().transform(image_tiles[i]->getExtent().getSRS()->getGeographicSRS()); geog_ext.getBounds( img_xmin, img_ymin, img_xmax, img_ymax ); img_locator = key->getProfile()->getSRS()->createLocator( img_xmin, img_ymin, img_xmax, img_ymax ); img_locator = new MercatorLocator( *img_locator.get(), geo_image->getExtent() ); } else { image_tiles[i]->getExtent().getBounds( img_xmin, img_ymin, img_xmax, img_ymax ); img_locator = key->getProfile()->getSRS()->createLocator( img_xmin, img_ymin, img_xmax, img_ymax, isPlateCarre ); } if ( isGeocentric ) img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC ); TransparentLayer* img_layer = new TransparentLayer(geo_image->getImage(), imageMapLayers[i].get()); img_layer->setLevelOfDetail( key->getLevelOfDetail() ); img_layer->setName( imageMapLayers[i]->getName() ); img_layer->setLocator( img_locator.get()); img_layer->setMinFilter( imageMapLayers[i]->getMinFilter().value()); img_layer->setMagFilter( imageMapLayers[i]->getMagFilter().value()); double upp = geo_image->getUnitsPerPixel(); // Scale the units per pixel to degrees if the image is mercator (and the key is geo) if ( geo_image->getSRS()->isMercator() && key->getGeoExtent().getSRS()->isGeographic() ) upp *= 1.0f/111319.0f; min_units_per_pixel = osg::minimum(upp, min_units_per_pixel); tile->setColorLayer( layer, img_layer ); layer++; } } osg::BoundingSphere bs = tile->getBound(); double max_range = 1e10; double radius = bs.radius(); #if 1 double min_range = radius * _engineProps.minTileRangeFactor().get(); osg::LOD::RangeMode mode = osg::LOD::DISTANCE_FROM_EYE_POINT; #else double width = key->getGeoExtent().width(); if (min_units_per_pixel == DBL_MAX) min_units_per_pixel = width/256.0; double min_range = (width / min_units_per_pixel) * _engineProps.getMinTileRangeFactor(); osg::LOD::RangeMode mode = osg::LOD::PIXEL_SIZE_ON_SCREEN; #endif // a skirt hides cracks when transitioning between LODs: hf->setSkirtHeight(radius * _engineProps.heightFieldSkirtRatio().get() ); // for now, cluster culling does not work for CUBE rendering bool isCube = map->getCoordinateSystemType() == Map::CSTYPE_GEOCENTRIC_CUBE; if ( isGeocentric && !isCube ) { //TODO: Work on cluster culling computation for cube faces osg::ClusterCullingCallback* ccc = createClusterCullingCallback(tile, locator->getEllipsoidModel() ); tile->setCullCallback( ccc ); } // Wait until now, when the tile is fully baked, to assign the terrain to the tile. // Placeholder tiles might try to locate this tile as an ancestor, and access its layers // and locators...so they must be intact before making this tile available via setTerrain. // // If there's already a placeholder tile registered, this will be ignored. If there isn't, // this will register the new tile. tile->setTerrain( terrain ); terrain->registerTile( tile ); // Set the tile's revision to the current terrain revision tile->setTerrainRevision( static_cast<VersionedTerrain*>(terrain)->getRevision() ); if ( _engineProps.loadingPolicy()->mode() != LoadingPolicy::MODE_STANDARD && key->getLevelOfDetail()) { tile->setUseLayerRequests( true ); tile->setHasElevationHint( hasElevation ); } tile->setTerrainRevision( terrain->getRevision() ); tile->setDataVariance( osg::Object::DYNAMIC ); osg::Node* result = 0L; if (wrapInPagedLOD) { // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new osg::PagedLOD(); plod->setCenter( bs.center() ); plod->addChild( tile, min_range, max_range ); std::string filename = createURI( map->getId(), key ); //Only add the next tile if it hasn't been blacklisted bool isBlacklisted = osgEarth::Registry::instance()->isBlacklisted( filename ); if (!isBlacklisted && key->getLevelOfDetail() < this->getEngineProperties().maxLOD().value() && validData ) { plod->setFileName( 1, filename ); plod->setRange( 1, 0.0, min_range ); } else { plod->setRange( 0, 0, FLT_MAX ); } #if USE_FILELOCATIONCALLBACK osgDB::Options* options = new osgDB::Options; options->setFileLocationCallback( new osgEarth::FileLocationCallback); plod->setDatabaseOptions( options ); #endif result = plod; if ( tile->getUseLayerRequests() ) result->addCullCallback( new TileImageBackfillCallback() ); } else { result = tile; } return result; }
bool MapEngine::isCached(Map* map, const osgEarth::TileKey *key) { Threading::ScopedReadLock lock( map->getMapDataMutex() ); const Profile* mapProfile = key->getProfile(); //Check the imagery layers for( MapLayerList::const_iterator i = map->getImageMapLayers().begin(); i != map->getImageMapLayers().end(); i++ ) { MapLayer* layer = i->get(); osg::ref_ptr< Cache > cache = layer->getCache(); if (!cache.valid()) return false; std::vector< osg::ref_ptr< const TileKey > > keys; if ( map->getProfile()->isEquivalentTo( layer->getProfile() ) ) { keys.push_back( key ); } else { layer->getProfile()->getIntersectingTiles( key, keys ); } for (unsigned int j = 0; j < keys.size(); ++j) { if ( layer->isKeyValid( keys[j].get() ) ) { if ( !cache->isCached( keys[j].get(), layer->getName(), layer->cacheFormat().value() ) ) { return false; } } } } //Check the elevation layers for( MapLayerList::const_iterator i = map->getHeightFieldMapLayers().begin(); i != map->getHeightFieldMapLayers().end(); i++ ) { MapLayer* layer = i->get(); osg::ref_ptr< Cache > cache = layer->getCache(); if (!cache.valid()) return false; std::vector< osg::ref_ptr< const TileKey > > keys; if ( map->getProfile()->isEquivalentTo( layer->getProfile() ) ) { keys.push_back( key ); } else { layer->getProfile()->getIntersectingTiles( key, keys ); } for (unsigned int j = 0; j < keys.size(); ++j) { if ( layer->isKeyValid( keys[j].get() ) ) { if ( !cache->isCached( keys[j].get(), layer->getName(), layer->cacheFormat().value() ) ) { return false; } } } } return true; }