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() ); } } } } }
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 ElevationLayerVector::populateHeightField(osg::HeightField* hf, const TileKey& key, const Profile* haeProfile, ElevationInterpolation interpolation, ProgressCallback* progress ) const { // heightfield must already exist. if ( !hf ) return false; // if the caller provided an "HAE map profile", he wants an HAE elevation grid even if // the map profile has a vertical datum. This is the usual case when building the 3D // terrain, for example. Construct a temporary key that doesn't have the vertical // datum info and use that to query the elevation data. TileKey keyToUse = key; if ( haeProfile ) { keyToUse = TileKey(key.getLOD(), key.getTileX(), key.getTileY(), haeProfile ); } // Collect the valid layers for this tile. LayerAndKeyVector contenders; LayerAndKeyVector offsets; // Track the number of layers that would return fallback data. unsigned numFallbackLayers = 0; // Check them in reverse order since the highest priority is last. for(ElevationLayerVector::const_reverse_iterator i = this->rbegin(); i != this->rend(); ++i) { ElevationLayer* layer = i->get(); if ( layer->getEnabled() && layer->getVisible() ) { // calculate the resolution-mapped key (adjusted for tile resolution differential). TileKey mappedKey = keyToUse.mapResolution( hf->getNumColumns(), layer->getTileSize() ); bool useLayer = true; TileKey bestKey( mappedKey ); // Is there a tilesource? If not we are cache-only and cannot reject the layer. if ( layer->getTileSource() ) { // Check whether the non-mapped key is valid according to the user's min/max level settings: if ( !layer->isKeyInRange(key) ) { useLayer = false; } // Find the "best available" mapped key from the tile source: else { if ( layer->getTileSource()->getBestAvailableTileKey(mappedKey, bestKey) ) { // If the bestKey is not the mappedKey, this layer is providing // fallback data (data at a lower resolution than requested) if ( mappedKey != bestKey ) { numFallbackLayers++; } } else { useLayer = false; } } } if ( useLayer ) { if ( layer->isOffset() ) { offsets.push_back( std::make_pair(layer, bestKey) ); } else { contenders.push_back( std::make_pair(layer, bestKey) ); } } } } // nothing? bail out. if ( contenders.empty() && offsets.empty() ) { return false; } // if everything is fallback data, bail out. if ( contenders.size() + offsets.size() == numFallbackLayers ) { return false; } // Sample the layers into our target. unsigned numColumns = hf->getNumColumns(); unsigned numRows = hf->getNumRows(); double xmin = key.getExtent().xMin(); double ymin = key.getExtent().yMin(); double dx = key.getExtent().width() / (double)(numColumns-1); double dy = key.getExtent().height() / (double)(numRows-1); // If the incoming heightfield requests a positive border width, // we need to adjust the extents so that we request data outside the // extent of the tile key: unsigned border = hf->getBorderWidth(); if (border > 0u) { dx = key.getExtent().width() / (double)(numColumns - (border*2+1)); dy = key.getExtent().height() / (double)(numRows - (border*2+1)); xmin -= dx * (double)border; ymin -= dy * (double)border; } // We will load the actual heightfields on demand. We might not need them all. #if 0 GeoHeightFieldVector heightFields(contenders.size()); GeoHeightFieldVector offsetFields(offsets.size()); std::vector<bool> heightFallback(contenders.size(), false); std::vector<bool> heightFailed(contenders.size(), false); std::vector<bool> offsetFailed(offsets.size(), false); #else GeoHeightFieldVector heightFields[9]; GeoHeightFieldVector offsetFields[9]; //(offsets.size()); std::vector<bool> heightFallback[9]; //(contenders.size(), false); std::vector<bool> heightFailed[9]; //(contenders.size(), false); std::vector<bool> offsetFailed[9]; //(offsets.size(), false); for (int n = 0; n < 9; ++n) { heightFields[n].resize(contenders.size()); offsetFields[n].resize(offsets.size()); heightFallback[n].assign(9, false); heightFailed[n].assign(9, false); offsetFailed[n].assign(9, false); } #endif // The maximum number of heightfields to keep in this local cache unsigned int maxHeightFields = 50; unsigned numHeightFieldsInCache = 0; const SpatialReference* keySRS = keyToUse.getProfile()->getSRS(); bool realData = false; unsigned int total = numColumns * numRows; unsigned int completed = 0; int nodataCount = 0; for (unsigned c = 0; c < numColumns; ++c) { double x = xmin + (dx * (double)c); for (unsigned r = 0; r < numRows; ++r) { double y = ymin + (dy * (double)r); // Collect elevations from each layer as necessary. bool resolved = false; for(int i=0; i<contenders.size() && !resolved; ++i) { ElevationLayer* layer = contenders[i].first.get(); TileKey contenderKey = contenders[i].second; // If there is a border, the edge points may not fall within the key extents // and we may need to fetch a neighboring key. int n = 4; // index 4 is the center/default tile if (border > 0u && !contenderKey.getExtent().contains(x, y)) { int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0; int dTy = y < contenderKey.getExtent().yMin() ? +1 : y > contenderKey.getExtent().yMax() ? -1 : 0; contenderKey = contenderKey.createNeighborKey(dTx, dTy); n = (dTy+1)*3 + (dTx+1); } if ( heightFailed[n][i] ) continue; TileKey actualKey = contenderKey; GeoHeightField& layerHF = heightFields[n][i]; if (!layerHF.valid()) { // We couldn't get the heightfield from the cache, so try to create it. // We also fallback on parent layers to make sure that we have data at the location even if it's fallback. while (!layerHF.valid() && actualKey.valid()) { layerHF = layer->createHeightField(actualKey, progress); if (!layerHF.valid()) { actualKey = actualKey.createParentKey(); } } // Mark this layer as fallback if necessary. if (layerHF.valid()) { heightFallback[n][i] = (actualKey != contenderKey); // actualKey != contenders[i].second; numHeightFieldsInCache++; } else { heightFailed[n][i] = true; continue; } } if (layerHF.valid()) { bool isFallback = heightFallback[n][i]; // We only have real data if this is not a fallback heightfield. if (!isFallback) { realData = true; } float elevation; if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation)) { if ( elevation != NO_DATA_VALUE ) { resolved = true; hf->setHeight(c, r, elevation); } else { ++nodataCount; } } } // Clear the heightfield cache if we have too many heightfields in the cache. if (numHeightFieldsInCache >= maxHeightFields) { //OE_NOTICE << "Clearing cache" << std::endl; for (unsigned int j = 0; j < 9; ++j) { for (unsigned int k = 0; k < heightFields[j].size(); k++) { heightFields[j][k] = GeoHeightField::INVALID; heightFallback[j][k] = false; } } numHeightFieldsInCache = 0; } } for(int i=offsets.size()-1; i>=0; --i) { TileKey contenderKey = offsets[i].second; // If there is a border, the edge points may not fall within the key extents // and we may need to fetch a neighboring key. int n = 4; // index 4 is the center/default tile if (border > 0u && !contenderKey.getExtent().contains(x, y)) { int dTx = x < contenderKey.getExtent().xMin() ? -1 : x > contenderKey.getExtent().xMax() ? +1 : 0; int dTy = y < contenderKey.getExtent().yMin() ? +1 : x > contenderKey.getExtent().yMax() ? -1 : 0; contenderKey = contenderKey.createNeighborKey(dTx, dTy); n = (dTy+1)*3 + (dTx+1); } if ( offsetFailed[n][i] == true ) continue; GeoHeightField& layerHF = offsetFields[n][i]; if ( !layerHF.valid() ) { ElevationLayer* offset = offsets[i].first.get(); layerHF = offset->createHeightField(contenderKey, progress); if ( !layerHF.valid() ) { offsetFailed[n][i] = true; continue; } } // If we actually got a layer then we have real data realData = true; float elevation = 0.0f; if (layerHF.getElevation(keySRS, x, y, interpolation, keySRS, elevation) && elevation != NO_DATA_VALUE) { hf->getHeight(c, r) += elevation; } } } } // Return whether or not we actually read any real data return realData; }