int main(int argc, char** argv) { osg::ArgumentParser arguments(&argc,argv); osgViewer::Viewer viewer(arguments); // Start by creating the map: MapOptions mapOptions; mapOptions.cachePolicy() = CachePolicy::NO_CACHE; Map* map = new Map( mapOptions ); // Create out image layer with a custom tile source. ImageLayerOptions options( "custom" ); CustomTileSource* tileSource = new CustomTileSource(); tileSource->open(); map->addImageLayer( new ImageLayer(options, tileSource) ); // That's it, the map is ready; now create a MapNode to render the Map: MapNode* mapNode = new MapNode( map ); viewer.setSceneData( mapNode ); viewer.setCameraManipulator( new EarthManipulator() ); // add some stock OSG handlers: viewer.addEventHandler(new osgViewer::StatsHandler()); viewer.addEventHandler(new osgViewer::WindowSizeHandler()); viewer.addEventHandler(new osgGA::StateSetManipulator(viewer.getCamera()->getOrCreateStateSet())); return viewer.run(); }
virtual ReadResult readNode(const std::string& fileName, const osgDB::Options* options) const { std::string ext = osgDB::getFileExtension( fileName ); if ( !acceptsExtension( ext ) ) return ReadResult::FILE_NOT_HANDLED; // See if the filename starts with server: and strip it off. This will trick OSG into // passing in the filename to our plugin instead of using the CURL plugin if the filename // contains a URL. So, if you want to read a URL, you can use the following format // osgDB::readNodeFile("server:http://myserver/myearth.earth"). This should only be // necessary for the first level as the other files will have a tilekey prepended to them. if ((fileName.length() > 7) && (fileName.substr(0, 7) == "server:")) return readNode(fileName.substr(7), options); if ( fileName == "__globe.earth" ) { return ReadResult( new MapNode() ); } else if ( fileName == "__cube.earth" ) { MapOptions options; options.coordSysType() = MapOptions::CSTYPE_GEOCENTRIC_CUBE; return ReadResult( new MapNode( new Map(options) ) ); } else { std::string fullFileName = fileName; if ( !osgDB::containsServerAddress( fileName ) ) { fullFileName = osgDB::findDataFile( fileName, options ); if (fullFileName.empty()) return ReadResult::FILE_NOT_FOUND; } osgEarth::ReadResult r = URI(fullFileName).readString( options ); if ( r.failed() ) return ReadResult::ERROR_IN_READING_FILE; // Since we're now passing off control to the stream, we have to pass along the // reference URI as well.. osg::ref_ptr<osgDB::Options> myOptions = Registry::instance()->cloneOrCreateOptions(options); URIContext( fullFileName ).apply( myOptions.get() ); std::stringstream in( r.getString() ); return readNode( in, myOptions.get() ); } }
/** * Makes a simple projected MapNode that contains a basemap of the world */ MapNode* makeMiniMapNode( ) { MapOptions mapOpt; mapOpt.coordSysType() = MapOptions::CSTYPE_PROJECTED; mapOpt.profile() = ProfileOptions("plate-carre"); Map* map = new Map( mapOpt ); GDALOptions basemapOpt; basemapOpt.url() = "../data/world.tif"; map->addImageLayer( new ImageLayer( ImageLayerOptions("basemap", basemapOpt) ) ); // That's it, the map is ready; now create a MapNode to render the Map: MapNodeOptions mapNodeOptions; mapNodeOptions.enableLighting() = false; return new MapNode( map, mapNodeOptions ); }
Map::Impl::Impl(RendererFrontend& frontend_, MapObserver& observer_, std::shared_ptr<FileSource> fileSource_, const MapOptions& mapOptions) : observer(observer_), rendererFrontend(frontend_), transform(observer, mapOptions.constrainMode(), mapOptions.viewportMode()), mode(mapOptions.mapMode()), pixelRatio(mapOptions.pixelRatio()), crossSourceCollisions(mapOptions.crossSourceCollisions()), fileSource(std::move(fileSource_)), style(std::make_unique<style::Style>(*fileSource, pixelRatio)), annotationManager(*style) { transform.setNorthOrientation(mapOptions.northOrientation()); style->impl->setObserver(this); rendererFrontend.setObserver(*this); transform.resize(mapOptions.size()); }
void SimpleOceanNode::rebuild() { this->removeChildren( 0, this->getNumChildren() ); osg::ref_ptr<MapNode> mapNode; if (_parentMapNode.lock(mapNode)) { const MapOptions& parentMapOptions = mapNode->getMap()->getMapOptions(); const MapNodeOptions& parentMapNodeOptions = mapNode->getMapNodeOptions(); // set up the map to "match" the parent map: MapOptions mo; mo.coordSysType() = parentMapOptions.coordSysType(); mo.profile() = mapNode->getMap()->getProfile()->toProfileOptions(); // new data model for the ocean: Map* oceanMap = new Map( mo ); // ditto with the map node options: MapNodeOptions mno; if ( mno.enableLighting().isSet() ) mno.enableLighting() = *mno.enableLighting(); RexTerrainEngineOptions terrainoptions; terrainoptions.enableBlending() = true; // gotsta blend with the main node terrainoptions.color() = baseColor().get(); terrainoptions.tileSize() = 5; mno.setTerrainOptions( terrainoptions ); // make the ocean's map node: MapNode* oceanMapNode = new MapNode( oceanMap, mno ); // set up the shaders. osg::StateSet* ss = this->getOrCreateStateSet(); // if the caller requested a mask layer, install that now. if ( maskLayer().isSet() ) { if ( !maskLayer()->maxLevel().isSet() ) { // set the max subdivision level if it's not already specified in the // mask layer options: maskLayer()->maxLevel() = maxLOD().get(); } // make sure the mask is shared (so we can access it from our shader) // and invisible (so we can't see it) maskLayer()->shared() = true; maskLayer()->visible() = false; ImageLayer* layer = new ImageLayer("ocean-mask", maskLayer().get()); oceanMap->addLayer( layer ); ss->setDefine("OE_SIMPLE_OCEAN_USE_MASK"); OE_INFO << LC << "Using mask layer \"" << layer->getName() << "\"\n"; } // otherwise, install a "proxy layer" that will use the elevation data in the map // to determine where the ocean is. This approach is limited in that it cannot // detect the difference between ocean and inland areas that are below sea level. else { // install an "elevation proxy" layer that reads elevation tiles from the // parent map and turns them into encoded images for our shader to use. ImageLayerOptions epo( "ocean-proxy" ); epo.cachePolicy() = CachePolicy::NO_CACHE; epo.shared() = true; epo.visible() = false; epo.shareTexUniformName() = "oe_ocean_proxyTex"; epo.shareTexMatUniformName() = "oe_ocean_proxyMat"; oceanMap->addLayer( new ElevationProxyImageLayer(mapNode->getMap(), epo) ); OE_INFO << LC << "Using elevation proxy layer\n"; } this->addChild( oceanMapNode ); // install the shaders on the ocean map node. VirtualProgram* vp = VirtualProgram::getOrCreate( ss ); vp->setName( "osgEarth SimpleOcean" ); Shaders shaders; shaders.loadAll(vp, 0L); // set up the options uniforms. _seaLevel = new osg::Uniform(osg::Uniform::FLOAT, "ocean_seaLevel"); ss->addUniform( _seaLevel.get() ); _lowFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_lowFeather"); ss->addUniform( _lowFeather.get() ); _highFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_highFeather"); ss->addUniform( _highFeather.get() ); _baseColor = new osg::Uniform(osg::Uniform::FLOAT_VEC4, "ocean_baseColor"); ss->addUniform( _baseColor.get() ); _maxRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_max_range"); ss->addUniform( _maxRange.get() ); _fadeRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_fade_range"); ss->addUniform( _fadeRange.get() ); _alphaUniform = new osg::Uniform(osg::Uniform::FLOAT, "oe_ocean_alpha"); ss->addUniform( _alphaUniform.get() ); // disable depth writes. ss->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0.0, 1.0, false) ); // load up a surface texture osg::ref_ptr<osg::Image> surfaceImage; if ( textureURI().isSet() ) { surfaceImage = textureURI()->getImage(); } //if ( !surfaceImage.valid() ) //{ // surfaceImage = createSurfaceImage(); //} if ( surfaceImage.valid() ) { osg::Texture2D* tex = new osg::Texture2D( surfaceImage.get() ); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); tex->setWrap ( osg::Texture::WRAP_S, osg::Texture::REPEAT ); tex->setWrap ( osg::Texture::WRAP_T, osg::Texture::REPEAT ); ss->setTextureAttributeAndModes( 5, tex, 1 ); ss->getOrCreateUniform( "ocean_surface_tex", osg::Uniform::SAMPLER_2D )->set( 5 ); ss->setDefine("OE_SIMPLE_OCEAN_USE_TEXTURE"); OE_INFO << LC << "Using a surface texture (" << surfaceImage->getFileName() << ")\n"; } // remove backface culling so we can see underwater // (use OVERRIDE since the terrain engine sets back face culling.) ss->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE ); // Material. osg::Material* m = new osgEarth::MaterialGL3(); m->setAmbient(m->FRONT_AND_BACK, osg::Vec4(.5,.5,.5,1)); m->setDiffuse(m->FRONT_AND_BACK, osg::Vec4(1,1,1,1)); m->setSpecular(m->FRONT_AND_BACK, osg::Vec4(0.2,0.2,0.2,1)); m->setEmission(m->FRONT_AND_BACK, osg::Vec4(0,0,0,1)); m->setShininess(m->FRONT_AND_BACK, 40.0); ss->setAttributeAndModes(m, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); // force apply options: applyOptions(); } }
void OceanSurfaceContainer::rebuild() { this->removeChildren( 0, this->getNumChildren() ); if ( _parentMapNode.valid() ) { const MapOptions& parentMapOptions = _parentMapNode->getMap()->getMapOptions(); const MapNodeOptions& parentMapNodeOptions = _parentMapNode->getMapNodeOptions(); // set up the map to "match" the parent map: MapOptions mo; mo.coordSysType() = parentMapOptions.coordSysType(); mo.profile() = _parentMapNode->getMap()->getProfile()->toProfileOptions(); // new data model for the ocean: Map* oceanMap = new Map( mo ); // ditto with the map node options: MapNodeOptions mno; if ( mno.enableLighting().isSet() ) mno.enableLighting() = *mno.enableLighting(); QuadTreeTerrainEngineOptions to; to.heightFieldSkirtRatio() = 0.0; // don't want to see skirts to.clusterCulling() = false; // want to see underwater to.enableBlending() = true; // gotsta blend with the main node mno.setTerrainOptions( to ); // make the ocean's map node: MapNode* oceanMapNode = new MapNode( oceanMap, mno ); // install a custom compositor. Must do this before adding any image layers. oceanMapNode->setCompositorTechnique( new OceanCompositor() ); // install an "elevation proxy" layer that reads elevation tiles from the // parent map and turns them into encoded images for our shader to use. ImageLayerOptions epo( "ocean-proxy" ); epo.cachePolicy() = CachePolicy::NO_CACHE; epo.maxLevel() = *_options.maxLOD(); oceanMap->addImageLayer( new ElevationProxyImageLayer(_parentMapNode->getMap(), epo) ); this->addChild( oceanMapNode ); // set up the options uniforms. osg::StateSet* ss = this->getOrCreateStateSet(); _seaLevel = new osg::Uniform(osg::Uniform::FLOAT, "ocean_seaLevel"); ss->addUniform( _seaLevel.get() ); _lowFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_lowFeather"); ss->addUniform( _lowFeather.get() ); _highFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_highFeather"); ss->addUniform( _highFeather.get() ); _baseColor = new osg::Uniform(osg::Uniform::FLOAT_VEC4, "ocean_baseColor"); ss->addUniform( _baseColor.get() ); // trick to prevent z-fighting.. ss->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0.0, 1.0, false) ); ss->setRenderBinDetails( 15, "RenderBin" ); // load up a surface texture ss->getOrCreateUniform( "ocean_has_tex1", osg::Uniform::BOOL )->set( false ); if ( _options.textureURI().isSet() ) { //TODO: enable cache support here: osg::Image* image = _options.textureURI()->getImage(); if ( image ) { osg::Texture2D* tex = new osg::Texture2D( image ); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); tex->setWrap ( osg::Texture::WRAP_S, osg::Texture::REPEAT ); tex->setWrap ( osg::Texture::WRAP_T, osg::Texture::REPEAT ); ss->setTextureAttributeAndModes( 1, tex, 1 ); ss->getOrCreateUniform( "ocean_tex1", osg::Uniform::SAMPLER_2D )->set( 1 ); ss->getOrCreateUniform( "ocean_has_tex1", osg::Uniform::BOOL )->set( true ); } } // remove backface culling so we can see underwater // (use OVERRIDE since the terrain engine sets back face culling.) ss->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE ); apply( _options ); } }
void GlobePlugin::setupMap() { QSettings settings; QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); TMSCacheOptions cacheOptions; cacheOptions.setPath( cacheDirectory.toStdString() ); MapOptions mapOptions; mapOptions.cache() = cacheOptions; osgEarth::Map *map = new osgEarth::Map( mapOptions ); //Default image layer GDALOptions driverOptions; driverOptions.url() = QDir::cleanPath( QgsApplication::pkgDataPath() + "/globe/world.tif" ).toStdString(); ImageLayerOptions layerOptions( "world", driverOptions ); layerOptions.cacheEnabled() = false; map->addImageLayer( new osgEarth::ImageLayer( layerOptions ) ); MapNodeOptions nodeOptions; //nodeOptions.proxySettings() = //nodeOptions.enableLighting() = false; //LoadingPolicy loadingPolicy( LoadingPolicy::MODE_SEQUENTIAL ); TerrainOptions terrainOptions; //terrainOptions.loadingPolicy() = loadingPolicy; terrainOptions.compositingTechnique() = TerrainOptions::COMPOSITING_MULTITEXTURE_FFP; nodeOptions.setTerrainOptions( terrainOptions ); // The MapNode will render the Map object in the scene graph. mMapNode = new osgEarth::MapNode( map, nodeOptions ); //prefill cache if ( !QFile::exists( cacheDirectory + "/worldwind_srtm" ) ) { copyFolder( QgsApplication::pkgDataPath() + "/globe/data/worldwind_srtm", cacheDirectory + "/globe/worldwind_srtm" ); } mRootNode = new osg::Group(); mRootNode->addChild( mMapNode ); // Add layers to the map layersChanged(); // model placement utils mElevationManager = new osgEarth::Util::ElevationManager( mMapNode->getMap() ); mElevationManager->setTechnique( osgEarth::Util::ElevationManager::TECHNIQUE_GEOMETRIC ); mElevationManager->setMaxTilesToCache( 50 ); mObjectPlacer = new osgEarth::Util::ObjectPlacer( mMapNode ); // place 3D model on point layer if ( mSettingsDialog.modelLayer() && !mSettingsDialog.modelPath().isEmpty() ) { osg::Node* model = osgDB::readNodeFile( mSettingsDialog.modelPath().toStdString() ); if ( model ) { QgsVectorLayer* layer = mSettingsDialog.modelLayer(); QgsAttributeList fetchAttributes; layer->select( fetchAttributes ); //TODO: select only visible features QgsFeature feature; while ( layer->nextFeature( feature ) ) { QgsPoint point = feature.geometry()->asPoint(); placeNode( model, point.y(), point.x() ); } } } }
void SimpleOceanNode::rebuild() { this->removeChildren( 0, this->getNumChildren() ); if ( _parentMapNode.valid() ) { const MapOptions& parentMapOptions = _parentMapNode->getMap()->getMapOptions(); const MapNodeOptions& parentMapNodeOptions = _parentMapNode->getMapNodeOptions(); // set up the map to "match" the parent map: MapOptions mo; mo.coordSysType() = parentMapOptions.coordSysType(); mo.profile() = _parentMapNode->getMap()->getProfile()->toProfileOptions(); // new data model for the ocean: Map* oceanMap = new Map( mo ); // ditto with the map node options: MapNodeOptions mno; if ( mno.enableLighting().isSet() ) mno.enableLighting() = *mno.enableLighting(); MPTerrainEngineOptions mpoptions; mpoptions.heightFieldSkirtRatio() = 0.0; // don't want to see skirts mpoptions.minLOD() = _options.maxLOD().get(); // weird, I know // so we can the surface from underwater: mpoptions.clusterCulling() = false; // want to see underwater mpoptions.enableBlending() = true; // gotsta blend with the main node mpoptions.color() = _options.baseColor().get(); mno.setTerrainOptions( mpoptions ); // make the ocean's map node: MapNode* oceanMapNode = new MapNode( oceanMap, mno ); // if the caller requested a mask layer, install that now. if ( _options.maskLayer().isSet() ) { if ( !_options.maskLayer()->maxLevel().isSet() ) { // set the max subdivision level if it's not already specified in the // mask layer options: _options.maskLayer()->maxLevel() = *_options.maxLOD(); } // make sure the mask is shared (so we can access it from our shader) // and invisible (so we can't see it) _options.maskLayer()->shared() = true; _options.maskLayer()->visible() = false; ImageLayer* maskLayer = new ImageLayer( "ocean-mask", *_options.maskLayer() ); oceanMap->addImageLayer( maskLayer ); } // otherwise, install a "proxy layer" that will use the elevation data in the map // to determine where the ocean is. This approach is limited in that it cannot // detect the difference between ocean and inland areas that are below sea level. else { // install an "elevation proxy" layer that reads elevation tiles from the // parent map and turns them into encoded images for our shader to use. ImageLayerOptions epo( "ocean-proxy" ); epo.cachePolicy() = CachePolicy::NO_CACHE; //epo.maxLevel() = *_options.maxLOD(); oceanMap->addImageLayer( new ElevationProxyImageLayer(_parentMapNode->getMap(), epo) ); } this->addChild( oceanMapNode ); // set up the shaders. osg::StateSet* ss = this->getOrCreateStateSet(); // install the shaders on the ocean map node. VirtualProgram* vp = VirtualProgram::getOrCreate( ss ); vp->setName( "osgEarth SimpleOcean" ); // use the appropriate shader for the active technique: std::string vertSource = _options.maskLayer().isSet() ? source_vertMask : source_vertProxy; std::string fragSource = _options.maskLayer().isSet() ? source_fragMask : source_fragProxy; vp->setFunction( "oe_ocean_vertex", vertSource, ShaderComp::LOCATION_VERTEX_VIEW ); vp->setFunction( "oe_ocean_fragment", fragSource, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.6f ); // install the slot attribute(s) ss->getOrCreateUniform( "ocean_data", osg::Uniform::SAMPLER_2D )->set( 0 ); // set up the options uniforms. _seaLevel = new osg::Uniform(osg::Uniform::FLOAT, "ocean_seaLevel"); ss->addUniform( _seaLevel.get() ); _lowFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_lowFeather"); ss->addUniform( _lowFeather.get() ); _highFeather = new osg::Uniform(osg::Uniform::FLOAT, "ocean_highFeather"); ss->addUniform( _highFeather.get() ); _baseColor = new osg::Uniform(osg::Uniform::FLOAT_VEC4, "ocean_baseColor"); ss->addUniform( _baseColor.get() ); _maxRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_max_range"); ss->addUniform( _maxRange.get() ); _fadeRange = new osg::Uniform(osg::Uniform::FLOAT, "ocean_fade_range"); ss->addUniform( _fadeRange.get() ); // trick to mitigate z-fighting.. ss->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0.0, 1.0, false) ); ss->setRenderingHint( osg::StateSet::TRANSPARENT_BIN ); // load up a surface texture osg::ref_ptr<osg::Image> surfaceImage; ss->getOrCreateUniform( "ocean_has_surface_tex", osg::Uniform::BOOL )->set( false ); if ( _options.textureURI().isSet() ) { //TODO: enable cache support here? surfaceImage = _options.textureURI()->getImage(); } if ( !surfaceImage.valid() ) { surfaceImage = createSurfaceImage(); } if ( surfaceImage.valid() ) { osg::Texture2D* tex = new osg::Texture2D( surfaceImage.get() ); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); tex->setWrap ( osg::Texture::WRAP_S, osg::Texture::REPEAT ); tex->setWrap ( osg::Texture::WRAP_T, osg::Texture::REPEAT ); ss->setTextureAttributeAndModes( 2, tex, 1 ); ss->getOrCreateUniform( "ocean_surface_tex", osg::Uniform::SAMPLER_2D )->set( 2 ); ss->getOrCreateUniform( "ocean_has_surface_tex", osg::Uniform::BOOL )->set( true ); } // remove backface culling so we can see underwater // (use OVERRIDE since the terrain engine sets back face culling.) ss->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE ); // Material. osg::Material* m = new osg::Material(); m->setAmbient(m->FRONT_AND_BACK, osg::Vec4(0,0,0,1)); m->setDiffuse(m->FRONT_AND_BACK, osg::Vec4(1,1,1,1)); m->setSpecular(m->FRONT_AND_BACK, osg::Vec4(0.1,0.1,0.1,1)); m->setEmission(m->FRONT_AND_BACK, osg::Vec4(0,0,0,1)); m->setShininess(m->FRONT_AND_BACK, 32.0); ss->setAttributeAndModes(m, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE ); // force apply options: applyOptions(); } }
void GlobePlugin::setupMap() { QSettings settings; QString cacheDirectory = settings.value( "cache/directory", QgsApplication::qgisSettingsDirPath() + "cache" ).toString(); #if OSGEARTH_VERSION_GREATER_OR_EQUAL( 2, 2, 0 ) FileSystemCacheOptions cacheOptions; cacheOptions.rootPath() = cacheDirectory.toStdString(); #else TMSCacheOptions cacheOptions; cacheOptions.setPath( cacheDirectory.toStdString() ); #endif MapOptions mapOptions; mapOptions.cache() = cacheOptions; osgEarth::Map *map = new osgEarth::Map( mapOptions ); //Default image layer /* GDALOptions driverOptions; driverOptions.url() = QDir::cleanPath( QgsApplication::pkgDataPath() + "/globe/world.tif" ).toStdString(); ImageLayerOptions layerOptions( "world", driverOptions ); map->addImageLayer( new osgEarth::ImageLayer( layerOptions ) ); */ MapNodeOptions nodeOptions; //nodeOptions.proxySettings() = //nodeOptions.enableLighting() = false; //LoadingPolicy loadingPolicy( LoadingPolicy::MODE_SEQUENTIAL ); TerrainOptions terrainOptions; //terrainOptions.loadingPolicy() = loadingPolicy; terrainOptions.compositingTechnique() = TerrainOptions::COMPOSITING_MULTITEXTURE_FFP; //terrainOptions.lodFallOff() = 6.0; nodeOptions.setTerrainOptions( terrainOptions ); // The MapNode will render the Map object in the scene graph. mMapNode = new osgEarth::MapNode( map, nodeOptions ); if ( settings.value( "/Plugin-Globe/baseLayerEnabled", true ).toBool() ) { setBaseMap( settings.value( "/Plugin-Globe/baseLayerURL", "http://readymap.org/readymap/tiles/1.0.0/7/" ).toString() ); } mRootNode = new osg::Group(); mRootNode->addChild( mMapNode ); // Add layers to the map imageLayersChanged(); elevationLayersChanged(); // model placement utils #ifdef HAVE_OSGEARTH_ELEVATION_QUERY #else mElevationManager = new osgEarth::Util::ElevationManager( mMapNode->getMap() ); mElevationManager->setTechnique( osgEarth::Util::ElevationManager::TECHNIQUE_GEOMETRIC ); mElevationManager->setMaxTilesToCache( 50 ); mObjectPlacer = new osgEarth::Util::ObjectPlacer( mMapNode ); // place 3D model on point layer if ( mSettingsDialog->modelLayer() && !mSettingsDialog->modelPath().isEmpty() ) { osg::Node* model = osgDB::readNodeFile( mSettingsDialog->modelPath().toStdString() ); if ( model ) { QgsVectorLayer* layer = mSettingsDialog->modelLayer(); QgsFeatureIterator fit = layer->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( QgsAttributeList() ) ); //TODO: select only visible features QgsFeature feature; while ( fit.nextFeature( feature ) ) { QgsPoint point = feature.geometry()->asPoint(); placeNode( model, point.y(), point.x() ); } } } #endif }