void StreamingTile::installRequests( const MapFrame& mapf, int stamp ) { StreamingTerrainNode* terrain = getStreamingTerrain(); OSGTileFactory* tileFactory = terrain->getTileFactory(); bool hasElevationLayer; { Threading::ScopedReadLock sharedLock( _tileLayersMutex ); hasElevationLayer = this->getElevationLayer() != NULL; } if ( hasElevationLayer ) { resetElevationRequests( mapf ); } // safely loop through the map layers and schedule imagery updates for each: for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { updateImagery( i->get(), mapf, tileFactory ); } _requestsInstalled = true; }
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; }
void TileModelFactory::buildElevation(const TileKey& key, const MapFrame& frame, bool accumulate, TileModel* model, ProgressCallback* progress) { const MapInfo& mapInfo = frame.getMapInfo(); const osgEarth::ElevationInterpolation& interp = frame.getMapOptions().elevationInterpolation().get(); // Request a heightfield from the map, falling back on lower resolution tiles // if necessary (fallback=true) osg::ref_ptr<osg::HeightField> hf; bool isFallback = false; if (_hfCache->getOrCreateHeightField(frame, key, accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress)) { model->_elevationData = TileModel::ElevationData( hf, GeoLocator::createForKey( key, mapInfo ), isFallback ); // Edge normalization: requires adjacency information if ( _terrainOptions.normalizeEdges() == true ) { for( int x=-1; x<=1; x++ ) { for( int y=-1; y<=1; y++ ) { if ( x != 0 || y != 0 ) { TileKey nk = key.createNeighborKey(x, y); if ( nk.valid() ) { osg::ref_ptr<osg::HeightField> hf; if (_hfCache->getOrCreateHeightField(frame, nk, accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) ) { model->_elevationData.setNeighbor( x, y, hf.get() ); } } } } } // parent too. if ( key.getLOD() > 0 ) { osg::ref_ptr<osg::HeightField> hf; if ( _hfCache->getOrCreateHeightField(frame, key.createParentKey(), accumulate, hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) ) { model->_elevationData.setParent( hf.get() ); } } } } }
FilterContext ClampFilter::push( FeatureList& features, const FilterContext& cx ) { const Session* session = cx.getSession(); if ( !session ) { OE_WARN << LC << "No session - session is required for elevation clamping" << std::endl; return cx; } // the map against which we'll be doing elevation clamping MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); const SpatialReference* featureSRS = cx.profile()->getSRS(); bool isGeocentric = session->getMapInfo().isGeocentric(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); if ( isGeocentric ) { // convert to map coords: cx.toWorld( geom ); mapSRS->transformFromECEF( geom ); // populate the elevations: eq.getElevations( geom, mapSRS ); // convert back to geocentric: mapSRS->transformToECEF( geom ); cx.toLocal( geom ); } else { // clamps the entire array to the highest available resolution. eq.getElevations( geom, featureSRS ); } } } return cx; }
void MapLayer::setMap(MapFrame *map) { Q_D(MapLayer); if (map == d->map) return; MapFrame *m = d->map; d->map = map; if (m) m->unregisterLayer(this); if (d->map) d->map->registerLayer(this); emit mapChanged(d->map); }
bool ElevationPool::getTile(const TileKey& key, MapFrame& frame, osg::ref_ptr<ElevationPool::Tile>& output) { // Synchronize the MapFrame to its Map; if there's an update, // clear out the internal cache and MRU. if ( frame.needsSync() ) { if (frame.sync()) { // Probably unnecessary because the Map itself will clear the pool. clear(); } } OE_START_TIMER(get); const double timeout = 30.0; osg::ref_ptr<Tile> tile; while( tryTile(key, frame, tile) && !tile.valid() && OE_GET_TIMER(get) < timeout) { // condition: another thread is working on fetching the tile from the map, // so wait and try again later. Do this until we succeed or time out. OpenThreads::Thread::YieldCurrentThread(); } if ( !tile.valid() && OE_GET_TIMER(get) >= timeout ) { // this means we timed out trying to fetch the map tile. OE_TEST << LC << "Timeout fetching tile " << key.str() << std::endl; } if ( tile.valid() ) { if ( tile->_hf.valid() ) { // got a valid tile, so push it to the query set. output = tile.get(); } else { OE_WARN << LC << "Got a tile with an invalid HF (" << key.str() << ")\n"; } } return tile.valid(); }
void StreamingTerrainNode::updateTaskServiceThreads( const MapFrame& mapf ) { //Get the maximum elevation weight float elevationWeight = 0.0f; for (ElevationLayerVector::const_iterator itr = mapf.elevationLayers().begin(); itr != mapf.elevationLayers().end(); ++itr) { ElevationLayer* layer = itr->get(); float w = layer->getElevationLayerOptions().loadingWeight().value(); if (w > elevationWeight) elevationWeight = w; } float totalImageWeight = 0.0f; for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) { totalImageWeight += itr->get()->getImageLayerOptions().loadingWeight().value(); } float totalWeight = elevationWeight + totalImageWeight; if (elevationWeight > 0.0f) { //Determine how many threads each layer gets int numElevationThreads = (int)osg::round((float)_numLoadingThreads * (elevationWeight / totalWeight )); OE_INFO << LC << "Elevation Threads = " << numElevationThreads << std::endl; getElevationTaskService()->setNumThreads( numElevationThreads ); } for (ImageLayerVector::const_iterator itr = mapf.imageLayers().begin(); itr != mapf.imageLayers().end(); ++itr) { const TerrainLayerOptions& opt = itr->get()->getImageLayerOptions(); int imageThreads = (int)osg::round((float)_numLoadingThreads * (opt.loadingWeight().value() / totalWeight )); OE_INFO << LC << "Image Threads for " << itr->get()->getName() << " = " << imageThreads << std::endl; getImageryTaskService( itr->get()->getUID() )->setNumThreads( imageThreads ); } }
bool ElevationPool::fetchTileFromMap(const TileKey& key, MapFrame& frame, Tile* tile) { tile->_loadTime = osg::Timer::instance()->tick(); osg::ref_ptr<osg::HeightField> hf = new osg::HeightField(); hf->allocate( _tileSize, _tileSize ); // Initialize the heightfield to nodata hf->getFloatArray()->assign( hf->getFloatArray()->size(), NO_DATA_VALUE ); TileKey keyToUse = key; while( !tile->_hf.valid() && keyToUse.valid() ) { bool ok; if (_layers.empty()) { OE_TEST << LC << "Populating from FULL MAP (" << keyToUse.str() << ")\n"; ok = frame.populateHeightField(hf, keyToUse, false /*heightsAsHAE*/, 0L); } else { OE_TEST << LC << "Populating from layers (" << keyToUse.str() << ")\n"; ok = _layers.populateHeightFieldAndNormalMap(hf.get(), 0L, keyToUse, 0L, INTERP_BILINEAR, 0L); } if (ok) { tile->_hf = GeoHeightField( hf.get(), keyToUse.getExtent() ); tile->_bounds = keyToUse.getExtent().bounds(); } else { keyToUse = keyToUse.createParentKey(); } } return tile->_hf.valid(); }
void TileModelFactory::createTileModel(const TileKey& key, const MapFrame& frame, bool accumulate, osg::ref_ptr<TileModel>& out_model, ProgressCallback* progress) { osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() ); model->_useParentData = _terrainReqs->parentTexturesRequired(); model->_tileKey = key; model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo()); OE_START_TIMER(fetch_imagery); // Fetch the image data and make color layers. unsigned index = 0; unsigned order = 0; for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() && layer->isKeyInRange(key) ) { BuildColorData build; build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, _liveTiles.get(), model.get() ); bool addedToModel = build.execute(progress); if ( addedToModel ) { // only bump the order if we added something to the data model. order++; } } } if (progress) progress->stats()["fetch_imagery_time"] += OE_STOP_TIMER(fetch_imagery); // make an elevation layer. OE_START_TIMER(fetch_elevation); buildElevation(key, frame, accumulate, _terrainReqs->elevationTexturesRequired(), model.get(), progress); if (progress) progress->stats()["fetch_elevation_time"] += OE_STOP_TIMER(fetch_elevation); // make a normal map layer (if necessary) if ( _terrainReqs->normalTexturesRequired() ) { OE_START_TIMER(fetch_normalmap); buildNormalMap(key, frame, accumulate, model.get(), progress); if (progress) progress->stats()["fetch_normalmap_time"] += OE_STOP_TIMER(fetch_normalmap); } // If nothing was added, not even a fallback heightfield, something went // horribly wrong. Leave without a tile model. Chances are that a parent tile // not not found in the live-tile registry. if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() ) { return; } // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it // as fallback data of course) if ( !model->_elevationData.getHeightField() ) { osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 ); model->_elevationData = TileModel::ElevationData( hf, GeoLocator::createForKey(key, frame.getMapInfo()), true ); } // look up the parent model and cache it. osg::ref_ptr<TileNode> parentTile; if ( _liveTiles->get(key.createParentKey(), parentTile) ) { model->_parentModel = parentTile->getTileModel(); } out_model = model.release(); }
void TileModelFactory::buildNormalMap(const TileKey& key, const MapFrame& frame, bool accumulate, TileModel* model, ProgressCallback* progress) { const MapInfo& mapInfo = frame.getMapInfo(); const osgEarth::ElevationInterpolation& interp = frame.getMapOptions().elevationInterpolation().get(); // Request a heightfield from the map, falling back on lower resolution tiles // if necessary (fallback=true) osg::ref_ptr<osg::HeightField> hf; osg::ref_ptr<osg::HeightField> parentHF; osg::ref_ptr<const TileModel> parentModel; bool isFallback = false; unsigned minNormalLOD = _terrainOptions.minNormalMapLOD().isSet() ? _terrainOptions.minNormalMapLOD().get() : 0u; if ( key.getLOD() >= minNormalLOD ) { // look up the parent's heightfield to use as a template TileKey parentKey = key.createParentKey(); if ( accumulate ) { osg::ref_ptr<TileNode> parentNode; if (_liveTiles->get(parentKey, parentNode)) { parentModel = parentNode->getTileModel(); parentHF = parentModel->_normalData.getHeightField(); if ( parentHF->getNumColumns() == EMPTY_NORMAL_MAP_SIZE ) parentHF = 0L; } } // Make a new heightfield: if (_normalHFCache->getOrCreateHeightField(frame, key, parentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress)) { if ( isFallback && parentModel.valid() ) { model->_normalData = parentModel->_normalData; model->_normalData._fallbackData = true; } else { model->_normalData = TileModel::NormalData( hf, GeoLocator::createForKey( key, mapInfo ), isFallback ); } } } else { // empty HF must be at least 2x2 for normal texture gen to work hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), EMPTY_NORMAL_MAP_SIZE, EMPTY_NORMAL_MAP_SIZE, true ); model->_normalData = TileModel::NormalData( hf, GeoLocator::createForKey( key, mapInfo ), false ); } if ( isFallback && parentModel.valid() ) { model->_normalTexture = parentModel->_normalTexture.get(); } else { model->generateNormalTexture(); } }
void TileModelFactory::buildElevation(const TileKey& key, const MapFrame& frame, bool accumulate, bool buildTexture, TileModel* model, ProgressCallback* progress) { const MapInfo& mapInfo = frame.getMapInfo(); const osgEarth::ElevationInterpolation& interp = frame.getMapOptions().elevationInterpolation().get(); // Request a heightfield from the map, falling back on lower resolution tiles // if necessary (fallback=true) osg::ref_ptr<osg::HeightField> hf; bool isFallback = false; // look up the parent's heightfield to use as a template osg::ref_ptr<osg::HeightField> parentHF; TileKey parentKey = key.createParentKey(); if ( accumulate ) { osg::ref_ptr<TileNode> parentNode; if (_liveTiles->get(parentKey, parentNode)) { parentHF = parentNode->getTileModel()->_elevationData.getHeightField(); if ( _debug && key.getLOD() > 0 && !parentHF.valid() ) { OE_NOTICE << LC << "Could not find a parent tile HF for " << key.str() << "\n"; } } } // Make a new heightfield: if (_meshHFCache->getOrCreateHeightField(frame, key, parentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress)) { model->_elevationData = TileModel::ElevationData( hf, GeoLocator::createForKey( key, mapInfo ), isFallback ); // Edge normalization: requires adjacency information if ( _terrainOptions.normalizeEdges() == true ) { for( int x=-1; x<=1; x++ ) { for( int y=-1; y<=1; y++ ) { if ( x != 0 || y != 0 ) { TileKey neighborKey = key.createNeighborKey(x, y); if ( neighborKey.valid() ) { osg::ref_ptr<osg::HeightField> neighborParentHF; if ( accumulate ) { TileKey neighborParentKey = neighborKey.createParentKey(); if (neighborParentKey == parentKey) { neighborParentHF = parentHF; } else { osg::ref_ptr<TileNode> neighborParentNode; if (_liveTiles->get(neighborParentKey, neighborParentNode)) { neighborParentHF = neighborParentNode->getTileModel()->_elevationData.getHeightField(); } } } // only pull the tile if we have a valid parent HF for it -- otherwise // you might get a flat tile when upsampling data. if ( neighborParentHF.valid() ) { osg::ref_ptr<osg::HeightField> hf; if (_meshHFCache->getOrCreateHeightField(frame, neighborKey, neighborParentHF.get(), hf, isFallback, SAMPLE_FIRST_VALID, interp, progress) ) { model->_elevationData.setNeighbor( x, y, hf.get() ); } } } } } } // parent too. if ( parentHF.valid() ) { model->_elevationData.setParent( parentHF.get() ); } } if ( buildTexture ) { model->generateElevationTexture(); } } }
bool HeightFieldCache::getOrCreateHeightField(const MapFrame& frame, const TileKey& key, //bool cummulative, const osg::HeightField* parent_hf, osg::ref_ptr<osg::HeightField>& out_hf, bool& out_isFallback, ElevationSamplePolicy samplePolicy, ElevationInterpolation interp, ProgressCallback* progress ) { // default out_isFallback = false; // check the quick cache. HFKey cachekey; cachekey._key = key; cachekey._revision = frame.getRevision(); cachekey._samplePolicy = samplePolicy; if (progress) progress->stats()["hfcache_try_count"] += 1; bool hit = false; LRUCache<HFKey,HFValue>::Record rec; if ( _cache.get(cachekey, rec) ) { out_hf = rec.value()._hf.get(); out_isFallback = rec.value()._isFallback; if (progress) { progress->stats()["hfcache_hit_count"] += 1; progress->stats()["hfcache_hit_rate"] = progress->stats()["hfcache_hit_count"]/progress->stats()["hfcache_try_count"]; } return true; } // Find the parent tile and start with its heightfield. if ( parent_hf ) { TileKey parentKey = key.createParentKey(); out_hf = HeightFieldUtils::createSubSample( parent_hf, parentKey.getExtent(), key.getExtent(), interp ); if ( !out_hf.valid() && ((int)key.getLOD())-1 > _firstLOD ) { // This most likely means that a parent tile expired while we were building the child. // No harm done in that case as this tile will soo be discarded as well. OE_DEBUG << "MP HFC: Unable to find tile " << key.str() << " in the live tile registry" << std::endl; return false; } } if ( !out_hf.valid() ) { //TODO. // This sets the elevation tile size; query size for all tiles. out_hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), _tileSize, _tileSize, true ); } bool populated = frame.populateHeightField( out_hf, key, true, // convertToHAE samplePolicy, progress ); // Treat Plate Carre specially by scaling the height values. (There is no need // to do this with an empty heightfield) const MapInfo& mapInfo = frame.getMapInfo(); if ( mapInfo.isPlateCarre() ) { HeightFieldUtils::scaleHeightFieldToDegrees( out_hf.get() ); } // cache it. HFValue cacheval; cacheval._hf = out_hf.get(); cacheval._isFallback = !populated; _cache.insert( cachekey, cacheval ); out_isFallback = !populated; return true; }
bool HeightFieldCache::getOrCreateHeightField(const MapFrame& frame, const TileKey& key, const osg::HeightField* parent_hf, osg::ref_ptr<osg::HeightField>& out_hf, bool& out_isFallback, ElevationSamplePolicy samplePolicy, ElevationInterpolation interp, ProgressCallback* progress ) { // default out_isFallback = false; // check the quick cache. HFKey cachekey; cachekey._key = key; cachekey._revision = frame.getRevision(); cachekey._samplePolicy = samplePolicy; if (progress) progress->stats()["hfcache_try_count"] += 1; LRUCache<HFKey,HFValue>::Record rec; if ( _enabled && _cache.get(cachekey, rec) ) { // Found it in the cache. out_hf = rec.value()._hf.get(); out_isFallback = rec.value()._isFallback; if (progress) { progress->stats()["hfcache_hit_count"] += 1; progress->stats()["hfcache_hit_rate"] = progress->stats()["hfcache_hit_count"]/progress->stats()["hfcache_try_count"]; } } else { // Not in the cache, so we need to create a HF. TileKey parentKey = key.createParentKey(); // Elevation "smoothing" uses the parent HF as the starting point for building // a new tile. This will cause lower-resolution data to propagate down the tree // and fill in any gaps in higher-resolution data. The result will be an elevation // grid that is "smoother" but not neccessarily as accurate. if ( _useParentAsReferenceHF && parent_hf && parentKey.valid() ) { out_hf = HeightFieldUtils::createSubSample( parent_hf, parentKey.getExtent(), key.getExtent(), interp ); } // If we are not smoothing, or we have no parent data, start with a basic // MSL=0 reference heightfield instead. if ( !out_hf.valid() ) { out_hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), _tileSize, _tileSize, 0u ); } // Next, populate it with data from the Map. The map will overwrite our starting // data with real data from the elevation stack. bool populated = frame.populateHeightField( out_hf, key, true, // convertToHAE progress ); // If the map failed to provide any suitable data sources at all, replace the // heightfield with data from its parent (if available). if ( !populated ) { if ( parentKey.valid() && parent_hf ) { out_hf = HeightFieldUtils::createSubSample( parent_hf, parentKey.getExtent(), key.getExtent(), interp ); } if ( !out_hf.valid() ) { // NOTE: This is probably no longer be possible, but check anyway for completeness. return false; } } // ONLY cache the new heightfield if a parent HF existed. Otherwise the new HF // may contain invalid data. This can happen if this task runs to completion // while the tile's parent expires from the scene graph. In that case the result // of this task will be discarded. Therefore we should not cache the result here. // This was causing intermittent rare "flat tiles" to appear in the terrain. if ( _enabled && parent_hf ) { // cache it. HFValue cacheval; cacheval._hf = out_hf.get(); cacheval._isFallback = !populated; _cache.insert( cachekey, cacheval ); } out_isFallback = !populated; } return true; }
void TerrainRenderData::setup(const MapFrame& frame, const RenderBindings& bindings, unsigned frameNum, osgUtil::CullVisitor* cv) { _bindings = &bindings; // Create a new State object to track sampler and uniform settings _drawState = new DrawState(); _drawState->_frame = frameNum; _drawState->_bindings = &bindings; // Make a drawable for each rendering pass (i.e. each render-able map layer). for(LayerVector::const_iterator i = frame.layers().begin(); i != frame.layers().end(); ++i) { Layer* layer = i->get(); if (layer->getEnabled()) { if (layer->getRenderType() == Layer::RENDERTYPE_TILE || layer->getRenderType() == Layer::RENDERTYPE_PATCH) { bool render = true; // If this is an image layer, check the enabled/visible states. VisibleLayer* visLayer = dynamic_cast<VisibleLayer*>(layer); if (visLayer) { render = visLayer->getVisible(); } if (render) { ImageLayer* imgLayer = dynamic_cast<ImageLayer*>(layer); // Make a list of "global" layers. There are layers whose data is not // represented in the TerrainTileModel, like a splatting layer or a patch // layer. The data for these is dynamic and not based on data fetched. if (imgLayer == 0L && layer->getRenderType() == Layer::RENDERTYPE_TILE) { tileLayers().push_back(layer); addLayerDrawable(layer); } else if (layer->getRenderType() == Layer::RENDERTYPE_PATCH) { PatchLayer* patchLayer = static_cast<PatchLayer*>(layer); // asumption! if (patchLayer->getAcceptCallback() != 0L && patchLayer->getAcceptCallback()->acceptLayer(*cv, cv->getCurrentCamera())) { patchLayers().push_back(dynamic_cast<PatchLayer*>(layer)); addLayerDrawable(layer); } } else { addLayerDrawable(layer); } } } } } // Include a "blank" layer for missing data. LayerDrawable* blank = addLayerDrawable(0L); blank->getOrCreateStateSet()->setDefine("OE_TERRAIN_RENDER_IMAGERY", osg::StateAttribute::OFF); }
// called from the UPDATE TRAVERSAL, because this method can potentially alter // the scene graph. bool StreamingTile::serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked ) { //Don't do anything until we have been added to the scene graph if (!_hasBeenTraversed) return false; bool tileModified = false; if ( !_requestsInstalled ) return false; // First service the tile generator: if ( _tileGenRequest.valid() && _tileGenRequest->isCompleted() ) { CustomTerrainTechnique* tech = dynamic_cast<CustomTerrainTechnique*>( getTerrainTechnique() ); if ( tech ) { //TODO: consider waiting to apply if there are still more tile updates in the queue. if ( _tileUpdates.size() == 0 ) { tileModified = tech->applyTileUpdates(); } } _tileGenRequest = 0L; } // now deal with imagery. const LoadingPolicy& lp = getStreamingTerrain()->getLoadingPolicy(); StreamingTerrainNode* terrain = getStreamingTerrain(); //Check each layer independently. for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* imageLayer = i->get(); bool checkForFinalImagery = false; CustomColorLayer colorLayer; if ( getCustomColorLayer( imageLayer->getUID(), colorLayer ) ) { if ( lp.mode() == LoadingPolicy::MODE_PREEMPTIVE ) { // in preemptive mode, always check for the final imagery - there are no intermediate // placeholders. checkForFinalImagery = true; } else if (lp.mode() == LoadingPolicy::MODE_SEQUENTIAL && readyForNewImagery(imageLayer, colorLayer.getLevelOfDetail()) ) { // in sequential mode, we have to incrementally increase imagery resolution by // creating placeholders based of parent tiles, one LOD at a time. if ( colorLayer.getLevelOfDetail() + 1 < (int)_key.getLevelOfDetail() ) { // if the parent's image LOD is higher than ours, replace ours with the parent's // since it is a higher-resolution placeholder: if ( _family[Relative::PARENT].getImageLOD(colorLayer.getUID()) > colorLayer.getLevelOfDetail() ) { osg::ref_ptr<Tile> parentTile; getStreamingTerrain()->getTile( _family[Relative::PARENT].tileID, parentTile, !tileTableLocked ); // Set the color layer to the parent color layer as a placeholder. CustomColorLayer parentColorLayer; if ( parentTile->getCustomColorLayer( colorLayer.getUID(), parentColorLayer ) ) { this->setCustomColorLayer( parentColorLayer ); } // ... and queue up an update request. queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, colorLayer.getUID() ); } } else { // we've gone as far as we can with placeholders; time to check for the // final imagery tile. checkForFinalImagery = true; } } } if ( checkForFinalImagery ) { // Then the image requests: for( TaskRequestList::iterator itr = _requests.begin(); itr != _requests.end(); ) { bool increment = true; TileColorLayerRequest* r = static_cast<TileColorLayerRequest*>( itr->get() ); //We only care about the current layer we are checking if ( r->_layerUID == imageLayer->getUID() ) { if ( itr->get()->isCompleted() ) { if ( r->wasCanceled() ) { //Reset the cancelled task to IDLE and give it a new progress callback. r->setState( TaskRequest::STATE_IDLE ); r->setProgressCallback( new StampedProgressCallback( r, terrain->getImageryTaskService( r->_layerUID ))); r->reset(); } else // success.. { //See if we even care about the request if ( !mapf.getImageLayerByUID( r->_layerUID ) ) { //The maplayer was probably deleted OE_DEBUG << "Layer uid=" << r->_layerUID << " no longer exists, ignoring TileColorLayerRequest " << std::endl; itr = _requests.erase(itr); increment = false; } else { CustomColorLayerRef* result = static_cast<CustomColorLayerRef*>( r->getResult() ); if ( result ) { this->setCustomColorLayer( result->_layer ); queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, r->_layerUID ); //OE_NOTICE << "Complete IR (" << _key.str() << ") layer=" << r->_layerId << std::endl; // remove from the list (don't reference "r" after this!) itr = _requests.erase( itr ); increment = false; } else { if (r->_numTries > r->_maxTries) { CustomColorLayer oldLayer; if ( this->getCustomColorLayer( r->_layerUID, oldLayer ) ) { // apply the old color layer but with a new LOD. this->setCustomColorLayer( CustomColorLayer( oldLayer.getMapLayer(), oldLayer.getImage(), oldLayer.getLocator(), _key.getLevelOfDetail(), _key )); itr = _requests.erase( itr ); increment = false; OE_DEBUG << "Tried (" << _key.str() << ") (layer uid=" << r->_layerUID << "), too many times, moving on...." << std::endl; } } else { OE_DEBUG << "IReq error (" << _key.str() << ") (layer uid=" << r->_layerUID << "), retrying" << std::endl; //The color layer request failed, probably due to a server error. Reset it. r->setState( TaskRequest::STATE_IDLE ); r->reset(); } } } } } } if ( increment ) ++itr; } } } // Finally, the elevation requests: if ( _hasElevation && !_elevationLayerUpToDate && _elevRequest.valid() && _elevPlaceholderRequest.valid() ) { // First, check is the Main elevation request is done. If so, we will now have the final HF data // and can shut down the elevation requests for this tile. if ( _elevRequest->isCompleted() ) { if ( _elevRequest->wasCanceled() ) { // If the request was canceled, reset it to IDLE and reset the callback. On the next _elevRequest->setState( TaskRequest::STATE_IDLE ); _elevRequest->setProgressCallback( new ProgressCallback() ); _elevRequest->reset(); } else // success: { // if the elevation request succeeded, install the new elevation layer! TileElevationLayerRequest* r = static_cast<TileElevationLayerRequest*>( _elevRequest.get() ); osg::ref_ptr<osgTerrain::HeightFieldLayer> newHFLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() ); if ( newHFLayer.valid() && newHFLayer->getHeightField() != NULL ) { newHFLayer->getHeightField()->setSkirtHeight( terrain->getTileFactory()->getTerrainOptions().heightFieldSkirtRatio().get() * this->getBound().radius() ); // need to write-lock the layer data since we'll be changing it: { Threading::ScopedWriteLock lock( _tileLayersMutex ); this->setElevationLayer( newHFLayer.get() ); this->dirtyBound(); } // the tile needs rebuilding. This will kick off a TileGenRequest. queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); // finalize the LOD marker for this tile, so other tiles can see where we are. _elevationLOD = _key.getLevelOfDetail(); #ifdef PREEMPTIVE_DEBUG OE_NOTICE << "Tile (" << _key.str() << ") final HF, LOD (" << _elevationLOD << ")" << std::endl; #endif // this was the final elev request, so mark elevation as DONE. _elevationLayerUpToDate = true; // GW- just reset these and leave them alone and let cancelRequests() take care of cleanup later. // done with our Elevation requests! //_elevRequest = 0L; //_elevPlaceholderRequest = 0L; } else { //We've tried to get the tile's elevation but couldn't. Just mark the elevation layer as up to date and move on. _elevationLOD = _key.getLevelOfDetail(); _elevationLayerUpToDate = true; //This code will retry indefinitely. We need to have a way to limit the number of retries since //it will block neighbor tiles from loading. //_elevRequest->setState( TaskRequest::STATE_IDLE ); //_elevRequest->reset(); } } } else if ( _elevPlaceholderRequest->isCompleted() ) { TileElevationPlaceholderLayerRequest* r = static_cast<TileElevationPlaceholderLayerRequest*>(_elevPlaceholderRequest.get()); if ( r->wasCanceled() ) { r->setState( TaskRequest::STATE_IDLE ); r->setProgressCallback( new ProgressCallback() ); r->reset(); } else // success: { osg::ref_ptr<osgTerrain::HeightFieldLayer> newPhLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() ); if ( newPhLayer.valid() && newPhLayer->getHeightField() != NULL ) { // install the new elevation layer. { Threading::ScopedWriteLock lock( _tileLayersMutex ); this->setElevationLayer( newPhLayer.get() ); this->dirtyBound(); } // tile needs to be recompiled. queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); // update the elevation LOD for this tile, now that the new HF data is installed. This will // allow other tiles to see where this tile's HF data is. _elevationLOD = r->_nextLOD; #ifdef PREEMPTIVE_DEBUG OE_NOTICE << "..tile (" << _key.str() << ") is now at (" << _elevationLOD << ")" << std::endl; #endif } _elevPlaceholderRequest->setState( TaskRequest::STATE_IDLE ); _elevPlaceholderRequest->reset(); } } } // if we have a new TileGenRequest, queue it up now. if ( _tileUpdates.size() > 0 && !_tileGenRequest.valid() ) // _tileGenNeeded && !_tileGenRequest.valid()) { _tileGenRequest = new TileGenRequest( this, _tileUpdates.front() ); _tileUpdates.pop(); //OE_NOTICE << "tile (" << _key.str() << ") queuing new tile gen" << std::endl; getStreamingTerrain()->getTileGenerationTaskService()->add( _tileGenRequest.get() ); } return tileModified; }
void AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx ) { const Session* session = cx.getSession(); // the map against which we'll be doing elevation clamping //MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); MapFrame mapf = session->createMapFrame( (Map::ModelParts)(Map::TERRAIN_LAYERS | Map::MODEL_LAYERS) ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); osg::ref_ptr<const SpatialReference> featureSRS = cx.profile()->getSRS(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); // want a result even if it's low res eq.setFallBackOnNoData( true ); NumericExpression scaleExpr; if ( _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); // whether to record the min/max height-above-terrain values. bool collectHATs = _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN || _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE; // whether to clamp every vertex (or just the centroid) bool perVertex = _altitude->binding() == AltitudeSymbol::BINDING_VERTEX; // whether the SRS's have a compatible vertical datum. bool vertEquiv = featureSRS->isVertEquivalentTo( mapSRS ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double maxTerrainZ = -DBL_MAX; double minTerrainZ = DBL_MAX; double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); double offsetZ = 0.0; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // Absolute heights in Z. Only need to collect the HATs; the geometry // remains unchanged. if ( _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ) { if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - elevations[i]; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - centroidElevation; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Heights-above-ground in Z. Need to resolve this to an absolute number // and record HATs along the way. else if ( _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ? SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L; if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = elevations[i] + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = centroidElevation + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Clamp - replace the geometry's Z with the terrain height. else // CLAMP_TO_TERRAIN { if ( perVertex ) { eq.getElevations( geom->asVector(), featureSRS, true, _maxRes ); // if necessary, transform the Z values (which are now in the map SRS) back // into the feature's SRS. if ( !vertEquiv ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); osg::Vec3d tempgeo; for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; featureSRSwithMapVertDatum->transform(p, featureSRS, p); } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; osg::ref_ptr<const SpatialReference> featureSRSWithMapVertDatum; if ( !vertEquiv ) featureSRSWithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() = centroidElevation; if ( !vertEquiv ) { featureSRSWithMapVertDatum->transform(p, featureSRS, p); } } } } } if ( !collectHATs ) { for( Geometry::iterator i = geom->begin(); i != geom->end(); ++i ) { i->z() *= scaleZ; i->z() += offsetZ; } } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } if ( minTerrainZ != DBL_MAX ) { feature->set( "__min_terrain_z", minTerrainZ ); feature->set( "__max_terrain_z", maxTerrainZ ); } } }
void TileModelFactory::createTileModel(const TileKey& key, const MapFrame& frame, osg::ref_ptr<TileModel>& out_model) //, //bool& out_hasRealData) { osg::ref_ptr<TileModel> model = new TileModel( frame.getRevision(), frame.getMapInfo() ); model->_tileKey = key; model->_tileLocator = GeoLocator::createForKey(key, frame.getMapInfo()); // Fetch the image data and make color layers. unsigned order = 0; for( ImageLayerVector::const_iterator i = frame.imageLayers().begin(); i != frame.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); if ( layer->getEnabled() ) { BuildColorData build; build.init( key, layer, order, frame.getMapInfo(), _terrainOptions, model.get() ); bool addedToModel = build.execute(); if ( addedToModel ) { // only bump the order if we added something to the data model. order++; } } } // make an elevation layer. BuildElevationData build; build.init( key, frame, _terrainOptions, model.get(), _hfCache ); build.execute(); // Bail out now if there's no data to be had. if ( model->_colorData.size() == 0 && !model->_elevationData.getHeightField() ) { return; } // OK we are making a tile, so if there's no heightfield yet, make an empty one (and mark it // as fallback data of course) if ( !model->_elevationData.getHeightField() ) { osg::HeightField* hf = HeightFieldUtils::createReferenceHeightField( key.getExtent(), 15, 15 ); model->_elevationData = TileModel::ElevationData( hf, GeoLocator::createForKey(key, frame.getMapInfo()), true ); } // look up the parent model and cache it. osg::ref_ptr<TileNode> parentTile; if ( _liveTiles->get(key.createParentKey(), parentTile) ) model->_parentModel = parentTile->getTileModel(); out_model = model.release(); }
void HClusterDlg::OnSave(wxCommandEvent& event ) { wxString field_name = m_textbox->GetValue(); if (field_name.IsEmpty()) { wxString err_msg = _("Please enter a field name for saving clustering results."); wxMessageDialog dlg(NULL, err_msg, _("Error"), wxOK | wxICON_ERROR); dlg.ShowModal(); return; } // save to table int time=0; int col = table_int->FindColId(field_name); if ( col == wxNOT_FOUND) { int col_insert_pos = table_int->GetNumberCols(); int time_steps = 1; int m_length_val = GdaConst::default_dbf_long_len; int m_decimals_val = 0; col = table_int->InsertCol(GdaConst::long64_type, field_name, col_insert_pos, time_steps, m_length_val, m_decimals_val); } else { // detect if column is integer field, if not raise a warning if (table_int->GetColType(col) != GdaConst::long64_type ) { wxString msg = _("This field name already exists (non-integer type). Please input a unique name."); wxMessageDialog dlg(this, msg, _("Warning"), wxOK | wxICON_WARNING ); dlg.ShowModal(); return; } } if (col > 0) { vector<bool> clusters_undef(rows, false); table_int->SetColData(col, time, clusters); table_int->SetColUndefined(col, time, clusters_undef); } // summary CreateSummary(clusters); // show a cluster map if (project->IsTableOnlyProject()) { return; } std::vector<GdaVarTools::VarInfo> new_var_info; std::vector<int> new_col_ids; new_col_ids.resize(1); new_var_info.resize(1); new_col_ids[0] = col; new_var_info[0].time = 0; // Set Primary GdaVarTools::VarInfo attributes new_var_info[0].name = field_name; new_var_info[0].is_time_variant = table_int->IsColTimeVariant(col); table_int->GetMinMaxVals(new_col_ids[0], new_var_info[0].min, new_var_info[0].max); new_var_info[0].sync_with_global_time = new_var_info[0].is_time_variant; new_var_info[0].fixed_scale = true; MapFrame* nf = new MapFrame(parent, project, new_var_info, new_col_ids, CatClassification::unique_values, MapCanvas::no_smoothing, 4, boost::uuids::nil_uuid(), wxDefaultPosition, GdaConst::map_default_size); wxString ttl; ttl << "Hierachical " << _("Cluster Map ") << "("; ttl << combo_n->GetValue(); ttl << " clusters)"; nf->SetTitle(ttl); }