osgEarth 2.1.1
|
00001 /* -*-c++-*- */ 00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph 00003 * Copyright 2008-2010 Pelican Mapping 00004 * http://osgearth.org 00005 * 00006 * osgEarth is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 */ 00019 #include "GeometryCompiler" 00020 #include <osgEarthFeatures/BuildGeometryFilter> 00021 #include <osgEarthFeatures/BuildTextFilter> 00022 #include <osgEarthFeatures/AltitudeFilter> 00023 #include <osgEarthFeatures/CentroidFilter> 00024 #include <osgEarthFeatures/ExtrudeGeometryFilter> 00025 #include <osgEarthFeatures/ScatterFilter> 00026 #include <osgEarthFeatures/SubstituteModelFilter> 00027 #include <osgEarthFeatures/TransformFilter> 00028 #include <osg/MatrixTransform> 00029 #include <osg/Timer> 00030 #include <osgDB/WriteFile> 00031 00032 #define LC "[GeometryCompiler] " 00033 00034 using namespace osgEarth; 00035 using namespace osgEarth::Features; 00036 using namespace osgEarth::Symbology; 00037 00038 //----------------------------------------------------------------------- 00039 00040 namespace 00041 { 00042 osg::ref_ptr<PointSymbol> s_defaultPointSymbol = new PointSymbol(); 00043 osg::ref_ptr<LineSymbol> s_defaultLineSymbol = new LineSymbol(); 00044 osg::ref_ptr<PolygonSymbol> s_defaultPolygonSymbol = new PolygonSymbol(); 00045 } 00046 00047 //----------------------------------------------------------------------- 00048 00049 GeometryCompilerOptions::GeometryCompilerOptions( const ConfigOptions& conf ) : 00050 ConfigOptions( conf ), 00051 _maxGranularity_deg( 5.0 ), 00052 _mergeGeometry( false ), 00053 _clustering( true ) 00054 { 00055 fromConfig(_conf); 00056 } 00057 00058 void 00059 GeometryCompilerOptions::fromConfig( const Config& conf ) 00060 { 00061 conf.getIfSet ( "max_granularity", _maxGranularity_deg ); 00062 conf.getIfSet ( "merge_geometry", _mergeGeometry ); 00063 conf.getIfSet ( "clustering", _clustering ); 00064 conf.getObjIfSet( "feature_name", _featureNameExpr ); 00065 conf.getIfSet ( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); 00066 conf.getIfSet ( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); 00067 } 00068 00069 Config 00070 GeometryCompilerOptions::getConfig() const 00071 { 00072 Config conf = ConfigOptions::getConfig(); 00073 conf.addIfSet ( "max_granularity", _maxGranularity_deg ); 00074 conf.addIfSet ( "merge_geometry", _mergeGeometry ); 00075 conf.addIfSet ( "clustering", _clustering ); 00076 conf.addObjIfSet( "feature_name", _featureNameExpr ); 00077 conf.addIfSet ( "geo_interpolation", "great_circle", _geoInterp, GEOINTERP_GREAT_CIRCLE ); 00078 conf.addIfSet ( "geo_interpolation", "rhumb_line", _geoInterp, GEOINTERP_RHUMB_LINE ); 00079 return conf; 00080 } 00081 00082 void 00083 GeometryCompilerOptions::mergeConfig( const Config& conf ) 00084 { 00085 ConfigOptions::mergeConfig( conf ); 00086 fromConfig( conf ); 00087 } 00088 00089 //----------------------------------------------------------------------- 00090 00091 GeometryCompiler::GeometryCompiler() 00092 { 00093 //nop 00094 } 00095 00096 GeometryCompiler::GeometryCompiler( const GeometryCompilerOptions& options ) : 00097 _options( options ) 00098 { 00099 //nop 00100 } 00101 00102 osg::Node* 00103 GeometryCompiler::compile(Feature* feature, 00104 const Style& style, 00105 const FilterContext& context) 00106 { 00107 if ( !context.profile() ) { 00108 OE_WARN << LC << "Valid feature profile required" << std::endl; 00109 return 0L; 00110 } 00111 00112 //if ( style.empty() ) { 00113 // OE_WARN << LC << "Non-empty style required" << std::endl; 00114 // return 0L; 00115 //} 00116 00117 FeatureList workingSet; 00118 workingSet.push_back(feature); 00119 return compile(workingSet, style, context); 00120 } 00121 00122 osg::Node* 00123 GeometryCompiler::compile(FeatureCursor* cursor, 00124 const Style& style, 00125 const FilterContext& context) 00126 00127 { 00128 if ( !context.profile() ) { 00129 OE_WARN << LC << "Valid feature profile required" << std::endl; 00130 return 0L; 00131 } 00132 00133 //if ( style.empty() ) { 00134 // OE_WARN << LC << "Non-empty style required" << std::endl; 00135 // return 0L; 00136 //} 00137 00138 // start by making a working copy of the feature set 00139 FeatureList workingSet; 00140 cursor->fill( workingSet ); 00141 00142 return compile(workingSet, style, context); 00143 } 00144 00145 osg::Node* 00146 GeometryCompiler::compile(FeatureList& workingSet, 00147 const Style& style, 00148 const FilterContext& context) 00149 { 00150 #ifdef PROFILING 00151 osg::Timer_t p_start = osg::Timer::instance()->tick(); 00152 unsigned p_features = workingSet.size(); 00153 #endif 00154 00155 osg::ref_ptr<osg::Group> resultGroup = new osg::Group(); 00156 00157 // create a filter context that will track feature data through the process 00158 FilterContext sharedCX = context; 00159 if ( !sharedCX.extent().isSet() ) 00160 sharedCX.extent() = sharedCX.profile()->getExtent(); 00161 00162 // only localize coordinates if the map is geocentric AND the extent is 00163 // less than 180 degrees. 00164 const MapInfo& mi = sharedCX.getSession()->getMapInfo(); 00165 GeoExtent workingExtent = sharedCX.extent()->transform( sharedCX.profile()->getSRS()->getGeographicSRS() ); 00166 bool localize = mi.isGeocentric() && workingExtent.width() < 180.0; 00167 00168 // go through the Style and figure out which filters to use. 00169 const MarkerSymbol* marker = style.get<MarkerSymbol>(); 00170 const PointSymbol* point = style.get<PointSymbol>(); 00171 const LineSymbol* line = style.get<LineSymbol>(); 00172 const PolygonSymbol* polygon = style.get<PolygonSymbol>(); 00173 const ExtrusionSymbol* extrusion = style.get<ExtrusionSymbol>(); 00174 const AltitudeSymbol* altitude = style.get<AltitudeSymbol>(); 00175 const TextSymbol* text = style.get<TextSymbol>(); 00176 00177 // if the style was empty, use some defaults based on the geometry type of the 00178 // first feature. 00179 if ( style.empty() && workingSet.size() > 0 ) 00180 { 00181 Feature* first = workingSet.begin()->get(); 00182 Geometry* geom = first->getGeometry(); 00183 if ( geom ) 00184 { 00185 switch( geom->getComponentType() ) 00186 { 00187 case Geometry::TYPE_LINESTRING: 00188 case Geometry::TYPE_RING: 00189 line = s_defaultLineSymbol.get(); break; 00190 case Geometry::TYPE_POINTSET: 00191 point = s_defaultPointSymbol.get(); break; 00192 case Geometry::TYPE_POLYGON: 00193 polygon = s_defaultPolygonSymbol.get(); break; 00194 } 00195 } 00196 } 00197 00198 if (_options.resampleMode().isSet()) 00199 { 00200 ResampleFilter resample; 00201 resample.resampleMode() = *_options.resampleMode(); 00202 if (_options.resampleMaxLength().isSet()) 00203 { 00204 resample.maxLength() = *_options.resampleMaxLength(); 00205 } 00206 sharedCX = resample.push( workingSet, sharedCX ); 00207 } 00208 00209 bool altRequired = 00210 altitude && ( 00211 altitude->clamping() != AltitudeSymbol::CLAMP_NONE || 00212 altitude->verticalOffset().isSet() || 00213 altitude->verticalScale().isSet() ); 00214 00215 // model substitution 00216 if ( marker ) 00217 { 00218 // use a separate filter context since we'll be munging the data 00219 FilterContext markerCX = sharedCX; 00220 00221 if ( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM || 00222 marker->placement() == MarkerSymbol::PLACEMENT_INTERVAL ) 00223 { 00224 ScatterFilter scatter; 00225 scatter.setDensity( *marker->density() ); 00226 scatter.setRandom( marker->placement() == MarkerSymbol::PLACEMENT_RANDOM ); 00227 scatter.setRandomSeed( *marker->randomSeed() ); 00228 markerCX = scatter.push( workingSet, markerCX ); 00229 } 00230 else if ( marker->placement() == MarkerSymbol::PLACEMENT_CENTROID ) 00231 { 00232 CentroidFilter centroid; 00233 centroid.push( workingSet, markerCX ); 00234 } 00235 00236 if ( altRequired ) 00237 { 00238 AltitudeFilter clamp; 00239 clamp.setPropertiesFromStyle( style ); 00240 markerCX = clamp.push( workingSet, markerCX ); 00241 00242 // don't set this; we changed the input data. 00243 //altRequired = false; 00244 } 00245 00246 SubstituteModelFilter sub( style ); 00247 if ( marker->scale().isSet() ) 00248 { 00249 //Turn on GL_NORMALIZE so lighting works properly 00250 resultGroup->getOrCreateStateSet()->setMode(GL_NORMALIZE, osg::StateAttribute::ON ); 00251 //sub.setModelMatrix( osg::Matrixd::scale( *marker->scale() ) ); 00252 } 00253 00254 sub.setClustering( *_options.clustering() ); 00255 if ( _options.featureName().isSet() ) 00256 sub.setFeatureNameExpr( *_options.featureName() ); 00257 00258 osg::Node* node = sub.push( workingSet, markerCX ); 00259 if ( node ) 00260 { 00261 resultGroup->addChild( node ); 00262 } 00263 } 00264 00265 // extruded geometry 00266 if ( extrusion ) 00267 { 00268 if ( altRequired ) 00269 { 00270 AltitudeFilter clamp; 00271 clamp.setPropertiesFromStyle( style ); 00272 sharedCX = clamp.push( workingSet, sharedCX ); 00273 altRequired = false; 00274 } 00275 00276 ExtrudeGeometryFilter extrude; 00277 extrude.setStyle( style ); 00278 00279 // apply per-feature naming if requested. 00280 if ( _options.featureName().isSet() ) 00281 extrude.setFeatureNameExpr( *_options.featureName() ); 00282 00283 osg::Node* node = extrude.push( workingSet, sharedCX ); 00284 if ( node ) 00285 { 00286 resultGroup->addChild( node ); 00287 } 00288 } 00289 00290 // simple geometry 00291 else if ( point || line || polygon ) 00292 { 00293 if ( altRequired ) 00294 { 00295 AltitudeFilter clamp; 00296 clamp.setPropertiesFromStyle( style ); 00297 sharedCX = clamp.push( workingSet, sharedCX ); 00298 altRequired = false; 00299 } 00300 00301 BuildGeometryFilter filter( style ); 00302 if ( _options.maxGranularity().isSet() ) 00303 filter.maxGranularity() = *_options.maxGranularity(); 00304 if ( _options.geoInterp().isSet() ) 00305 filter.geoInterp() = *_options.geoInterp(); 00306 if ( _options.mergeGeometry().isSet() ) 00307 filter.mergeGeometry() = *_options.mergeGeometry(); 00308 if ( _options.featureName().isSet() ) 00309 filter.featureName() = *_options.featureName(); 00310 00311 osg::Node* node = filter.push( workingSet, sharedCX ); 00312 if ( node ) 00313 { 00314 resultGroup->addChild( node ); 00315 } 00316 } 00317 00318 if ( text ) 00319 { 00320 if ( altRequired ) 00321 { 00322 AltitudeFilter clamp; 00323 clamp.setPropertiesFromStyle( style ); 00324 sharedCX = clamp.push( workingSet, sharedCX ); 00325 altRequired = false; 00326 } 00327 00328 BuildTextFilter filter( style ); 00329 osg::Node* node = filter.push( workingSet, sharedCX ); 00330 if ( node ) 00331 { 00332 resultGroup->addChild( node ); 00333 } 00334 } 00335 00336 resultGroup->getOrCreateStateSet()->setMode( GL_BLEND, 1 ); 00337 00338 //osgDB::writeNodeFile( *(resultGroup.get()), "out.osg" ); 00339 00340 #ifdef PROFILING 00341 osg::Timer_t p_end = osg::Timer::instance()->tick(); 00342 OE_INFO << LC 00343 << "features = " << p_features << 00344 << " ,time = " << osg::Timer::instance()->delta_s(p_start, p_end) << " s." << std::endl; 00345 #endif 00346 00347 return resultGroup.release(); 00348 }