void ElevationLayer::assembleHeightField(const TileKey& key, osg::ref_ptr<osg::HeightField>& out_hf, osg::ref_ptr<NormalMap>& out_normalMap, ProgressCallback* progress) { // Collect the heightfields for each of the intersecting tiles. GeoHeightFieldVector heightFields; //Determine the intersecting keys std::vector< TileKey > intersectingTiles; if (key.getLOD() > 0u) { getProfile()->getIntersectingTiles(key, intersectingTiles); } else { // LOD is zero - check whether the LOD mapping went out of range, and if so, // fall back until we get valid tiles. This can happen when you have two // profiles with very different tile schemes, and the "equivalent LOD" // surpasses the max data LOD of the tile source. unsigned numTilesThatMayHaveData = 0u; int intersectionLOD = getProfile()->getEquivalentLOD(key.getProfile(), key.getLOD()); while (numTilesThatMayHaveData == 0u && intersectionLOD >= 0) { intersectingTiles.clear(); getProfile()->getIntersectingTiles(key.getExtent(), intersectionLOD, intersectingTiles); for (unsigned int i = 0; i < intersectingTiles.size(); ++i) { const TileKey& layerKey = intersectingTiles[i]; if (mayHaveData(layerKey) == true) { ++numTilesThatMayHaveData; } } --intersectionLOD; } } // collect heightfield for each intersecting key. Note, we're hitting the // underlying tile source here, so there's no vetical datum shifts happening yet. // we will do that later. if ( intersectingTiles.size() > 0 ) { for (unsigned int i = 0; i < intersectingTiles.size(); ++i) { const TileKey& layerKey = intersectingTiles[i]; if ( isKeyInLegalRange(layerKey) ) { osg::ref_ptr<osg::HeightField> hf; osg::ref_ptr<NormalMap> normalMap; createImplementation(layerKey, hf, normalMap, progress); if (hf.valid()) { heightFields.push_back( GeoHeightField(hf.get(), normalMap.get(), layerKey.getExtent()) ); } } } // If we actually got a HeightField, resample/reproject it to match the incoming TileKey's extents. if (heightFields.size() > 0) { unsigned int width = 0; unsigned int height = 0; for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr) { if (itr->getHeightField()->getNumColumns() > width) width = itr->getHeightField()->getNumColumns(); if (itr->getHeightField()->getNumRows() > height) height = itr->getHeightField()->getNumRows(); } //Now sort the heightfields by resolution to make sure we're sampling the highest resolution one first. std::sort( heightFields.begin(), heightFields.end(), GeoHeightField::SortByResolutionFunctor()); out_hf = new osg::HeightField(); out_hf->allocate(width, height); out_normalMap = new NormalMap(width, height); //Go ahead and set up the heightfield so we don't have to worry about it later double minx, miny, maxx, maxy; key.getExtent().getBounds(minx, miny, maxx, maxy); double dx = (maxx - minx)/(double)(width-1); double dy = (maxy - miny)/(double)(height-1); //Create the new heightfield by sampling all of them. for (unsigned int c = 0; c < width; ++c) { double x = minx + (dx * (double)c); for (unsigned r = 0; r < height; ++r) { double y = miny + (dy * (double)r); //For each sample point, try each heightfield. The first one with a valid elevation wins. float elevation = NO_DATA_VALUE; osg::Vec3 normal(0,0,1); for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr) { // get the elevation value, at the same time transforming it vertically into the // requesting key's vertical datum. float e = 0.0; osg::Vec3 n; if (itr->getElevationAndNormal(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e, n)) { elevation = e; normal = n; break; } } out_hf->setHeight( c, r, elevation ); out_normalMap->set( c, r, normal ); } } } else { //if (progress && progress->message().empty()) // progress->message() = "assemble yielded no heightfields"; } } else { //if (progress && progress->message().empty()) // progress->message() = "assemble yielded no intersecting tiles"; } // If the progress was cancelled clear out any of the output data. if (progress && progress->isCanceled()) { out_hf = 0; out_normalMap = 0; } }
osg::HeightField* ElevationLayer::assembleHeightFieldFromTileSource(const TileKey& key, ProgressCallback* progress) { osg::HeightField* result = 0L; // Collect the heightfields for each of the intersecting tiles. GeoHeightFieldVector heightFields; //Determine the intersecting keys std::vector< TileKey > intersectingTiles; getProfile()->getIntersectingTiles( key, intersectingTiles ); // collect heightfield for each intersecting key. Note, we're hitting the // underlying tile source here, so there's no vetical datum shifts happening yet. // we will do that later. if ( intersectingTiles.size() > 0 ) { for (unsigned int i = 0; i < intersectingTiles.size(); ++i) { const TileKey& layerKey = intersectingTiles[i]; if ( isKeyValid(layerKey) ) { osg::HeightField* hf = createHeightFieldFromTileSource( layerKey, progress ); if ( hf ) { heightFields.push_back( GeoHeightField(hf, layerKey.getExtent()) ); } else { //We couldn't get a heightfield at the given key so fall back on parent tiles TileKey parentKey = layerKey.createParentKey(); while (!hf && parentKey.valid()) { hf = createHeightFieldFromTileSource( parentKey, progress ); if (hf) { heightFields.push_back( GeoHeightField(hf, parentKey.getExtent()) ); break; } parentKey = parentKey.createParentKey(); } } } } } // If we actually got a HeightField, resample/reproject it to match the incoming TileKey's extents. if (heightFields.size() > 0) { unsigned int width = 0; unsigned int height = 0; for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr) { if (itr->getHeightField()->getNumColumns() > width) width = itr->getHeightField()->getNumColumns(); if (itr->getHeightField()->getNumRows() > height) height = itr->getHeightField()->getNumRows(); } result = new osg::HeightField(); result->allocate(width, height); //Go ahead and set up the heightfield so we don't have to worry about it later double minx, miny, maxx, maxy; key.getExtent().getBounds(minx, miny, maxx, maxy); double dx = (maxx - minx)/(double)(width-1); double dy = (maxy - miny)/(double)(height-1); //Create the new heightfield by sampling all of them. for (unsigned int c = 0; c < width; ++c) { double x = minx + (dx * (double)c); for (unsigned r = 0; r < height; ++r) { double y = miny + (dy * (double)r); //For each sample point, try each heightfield. The first one with a valid elevation wins. float elevation = NO_DATA_VALUE; for (GeoHeightFieldVector::iterator itr = heightFields.begin(); itr != heightFields.end(); ++itr) { // get the elevation value, at the same time transforming it vertically into the // requesting key's vertical datum. float e = 0.0; if (itr->getElevation(key.getExtent().getSRS(), x, y, INTERP_BILINEAR, key.getExtent().getSRS(), e)) { elevation = e; break; } } result->setHeight( c, r, elevation ); } } } return result; }