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 <osgEarthSymbology/Style> 00020 #include <osgEarthSymbology/CssUtils> 00021 #include <osgEarthSymbology/SLD> 00022 #include <osgEarth/HTTPClient> 00023 #include <algorithm> 00024 00025 using namespace osgEarth; 00026 using namespace osgEarth::Symbology; 00027 00028 //------------------------------------------------------------------------ 00029 00030 #undef LC 00031 #define LC "[Style] " 00032 00033 Style::Style( const std::string& name ) : 00034 _name( name ) 00035 { 00036 //NOP 00037 } 00038 00039 Style::Style(const Style& rhs) : 00040 _name ( rhs._name ), 00041 _symbols ( rhs._symbols ), 00042 _origType( rhs._origType ), 00043 _origData( rhs._origData ), 00044 _uri ( rhs._uri ) 00045 { 00046 //nop 00047 } 00048 00049 void Style::addSymbol(Symbol* symbol) 00050 { 00051 _symbols.push_back(symbol); 00052 } 00053 00054 bool Style::removeSymbol(Symbol* symbol) 00055 { 00056 SymbolList::iterator it = find(_symbols.begin(), _symbols.end(), symbol); 00057 if (it == _symbols.end()) 00058 return false; 00059 00060 _symbols.erase(it); 00061 00062 return true; 00063 } 00064 00065 Style::Style( const Config& conf ) 00066 { 00067 mergeConfig( conf ); 00068 } 00069 00070 Style 00071 Style::combineWith( const Style& rhs ) const 00072 { 00073 // start by deep-cloning this style. 00074 Config conf = getConfig( false ); 00075 Style newStyle( conf ); 00076 00077 // next, merge in the symbology from the other style. 00078 newStyle.mergeConfig( rhs.getConfig(false) ); 00079 00080 if ( !this->empty() && !rhs.empty() ) 00081 newStyle.setName( _name + ":" + rhs.getName() ); 00082 else if ( !this->empty() && rhs.empty() ) 00083 newStyle.setName( _name ); 00084 else if ( this->empty() && !rhs.empty() ) 00085 newStyle.setName( rhs.getName() ); 00086 else 00087 newStyle.setName( _name ); 00088 00089 return newStyle; 00090 } 00091 00092 void 00093 Style::mergeConfig( const Config& conf ) 00094 { 00095 if ( _name.empty() ) 00096 _name = conf.value( "name" ); 00097 00098 // if there's no explicit name, use the KEY as the name. 00099 if ( _name.empty() ) 00100 _name = conf.key(); 00101 00102 conf.getIfSet( "url", _uri ); // named "url" for back compat 00103 00104 _origType = conf.value( "type" ); 00105 00106 if ( conf.value( "type" ) == "text/css" ) 00107 { 00108 _origData = conf.value(); 00109 } 00110 else 00111 { 00112 Config symbolConf = conf.child( "symbols" ); 00113 if ( !symbolConf.empty() ) 00114 { 00115 for( ConfigSet::const_iterator i = symbolConf.children().begin(); i != symbolConf.children().end(); ++i ) 00116 { 00117 const Config& c = *i; 00118 00119 if ( c.key() == "text" ) 00120 { 00121 getOrCreateSymbol<TextSymbol>()->mergeConfig( c ); 00122 } 00123 else if ( c.key() == "point" ) 00124 { 00125 getOrCreateSymbol<PointSymbol>()->mergeConfig( c ); 00126 } 00127 else if ( c.key() == "line" ) 00128 { 00129 getOrCreateSymbol<LineSymbol>()->mergeConfig( c ); 00130 } 00131 else if ( c.key() == "polygon" ) 00132 { 00133 getOrCreateSymbol<PolygonSymbol>()->mergeConfig( c ); 00134 } 00135 else if ( c.key() == "extrusion" ) 00136 { 00137 getOrCreateSymbol<ExtrusionSymbol>()->mergeConfig( c ); 00138 } 00139 else if ( c.key() == "altitude" ) 00140 { 00141 getOrCreateSymbol<AltitudeSymbol>()->mergeConfig( c ); 00142 } 00143 else if ( c.key() == "marker" ) 00144 { 00145 getOrCreateSymbol<MarkerSymbol>()->mergeConfig( c ); 00146 } 00147 } 00148 } 00149 } 00150 } 00151 00152 Config 00153 Style::getConfig( bool keepOrigType ) const 00154 { 00155 Config conf( "style" ); 00156 conf.attr("name") = _name; 00157 00158 conf.addIfSet( "url", _uri ); 00159 00160 if ( _origType == "text/css" && keepOrigType ) 00161 { 00162 conf.attr("type") = _origType; 00163 conf.value() = _origData; 00164 } 00165 else 00166 { 00167 Config symbolsConf( "symbols" ); 00168 for( SymbolList::const_iterator i = _symbols.begin(); i != _symbols.end(); ++i ) 00169 { 00170 symbolsConf.addChild( i->get()->getConfig() ); 00171 } 00172 conf.addChild( symbolsConf ); 00173 } 00174 00175 return conf; 00176 } 00177 00178 //------------------------------------------------------------------------ 00179 00180 #undef LC 00181 #define LC "[StyleSelector] " 00182 00183 StyleSelector::StyleSelector( const Config& conf ) 00184 { 00185 mergeConfig( conf ); 00186 } 00187 00188 std::string 00189 StyleSelector::getSelectedStyleName() const 00190 { 00191 return _styleName.isSet() ? *_styleName : _name; 00192 } 00193 00194 void 00195 StyleSelector::mergeConfig( const Config& conf ) 00196 { 00197 _name = conf.value( "name" ); 00198 conf.getIfSet( "style", _styleName ); // backwards compatibility 00199 conf.getIfSet( "class", _styleName ); 00200 conf.getObjIfSet( "query", _query ); 00201 } 00202 00203 Config 00204 StyleSelector::getConfig() const 00205 { 00206 Config conf( "selector" ); 00207 conf.add( "name", _name ); 00208 conf.addIfSet( "class", _styleName ); 00209 conf.addObjIfSet( "query", _query ); 00210 return conf; 00211 } 00212 00213 //------------------------------------------------------------------------ 00214 00215 #undef LC 00216 #define LC "[StyleSheet] " 00217 00218 StyleSheet::StyleSheet( const Config& conf ) 00219 { 00220 mergeConfig( conf ); 00221 } 00222 00223 void 00224 StyleSheet::addStyle( const Style& style ) 00225 { 00226 _styles[ style.getName() ] = style; 00227 } 00228 00229 void 00230 StyleSheet::removeStyle( const std::string& name ) 00231 { 00232 _styles.erase( name ); 00233 } 00234 00235 Style* 00236 StyleSheet::getStyle( const std::string& name, bool fallBackOnDefault ) 00237 { 00238 StyleMap::iterator i = _styles.find( name ); 00239 if ( i != _styles.end() ) { 00240 return &i->second; 00241 } 00242 else if ( name.length() > 1 && name.at(0) == '#' ) { 00243 std::string nameWithoutHash = name.substr( 1 ); 00244 return getStyle( nameWithoutHash, fallBackOnDefault ); 00245 } 00246 else if ( fallBackOnDefault ) { 00247 return getDefaultStyle(); 00248 } 00249 else { 00250 return 0L; 00251 } 00252 } 00253 00254 const Style* 00255 StyleSheet::getStyle( const std::string& name, bool fallBackOnDefault ) const 00256 { 00257 StyleMap::const_iterator i = _styles.find( name ); 00258 if ( i != _styles.end() ) { 00259 return &i->second; 00260 } 00261 else if ( name.length() > 1 && name.at(0) == '#' ) { 00262 std::string nameWithoutHash = name.substr( 1 ); 00263 return getStyle( nameWithoutHash, fallBackOnDefault ); 00264 } 00265 else if ( fallBackOnDefault ) { 00266 return getDefaultStyle(); 00267 } 00268 else { 00269 return 0L; 00270 } 00271 } 00272 00273 Style* 00274 StyleSheet::getDefaultStyle() 00275 { 00276 if ( _styles.find( "default" ) != _styles.end() ) { 00277 return &_styles.find( "default" )->second; 00278 } 00279 else if ( _styles.find( "" ) != _styles.end() ) { 00280 return &_styles.find( "" )->second; 00281 } 00282 if ( _styles.size() > 0 ) { 00283 return &_styles.begin()->second; 00284 } 00285 else { 00286 // insert the empty style and return it. 00287 _styles["default"] = _emptyStyle; 00288 return &_styles.begin()->second; 00289 } 00290 } 00291 00292 const Style* 00293 StyleSheet::getDefaultStyle() const 00294 { 00295 if ( _styles.size() == 1 ) { 00296 return &_styles.begin()->second; 00297 } 00298 else if ( _styles.find( "default" ) != _styles.end() ) { 00299 return &_styles.find( "default" )->second; 00300 } 00301 else if ( _styles.find( "" ) != _styles.end() ) { 00302 return &_styles.find( "" )->second; 00303 } 00304 else { 00305 return &_emptyStyle; 00306 } 00307 } 00308 00309 void 00310 StyleSheet::addResourceLibrary( const std::string& name, ResourceLibrary* lib ) 00311 { 00312 _libraries[name] = lib; 00313 } 00314 00315 ResourceLibrary* 00316 StyleSheet::getResourceLibrary( const std::string& name ) const 00317 { 00318 ResourceLibraryMap::const_iterator i = _libraries.find( name ); 00319 return i != _libraries.end() ? i->second.get() : 0L; 00320 } 00321 00322 Config 00323 StyleSheet::getConfig() const 00324 { 00325 Config conf; 00326 for( StyleSelectorList::const_iterator i = _selectors.begin(); i != _selectors.end(); ++i ) 00327 { 00328 conf.add( "selector", i->getConfig() ); 00329 } 00330 for( StyleMap::const_iterator i = _styles.begin(); i != _styles.end(); ++i ) 00331 { 00332 conf.add( "style", i->second.getConfig() ); 00333 } 00334 return conf; 00335 } 00336 00337 void 00338 StyleSheet::mergeConfig( const Config& conf ) 00339 { 00340 _uriContext = conf.uriContext(); 00341 00342 // read in any resource library references 00343 ConfigSet libraries = conf.children( "library" ); 00344 for( ConfigSet::iterator i = libraries.begin(); i != libraries.end(); ++i ) 00345 { 00346 std::string name = i->value("name"); 00347 if ( name.empty() ) { 00348 OE_WARN << LC << "Resource library missing required 'name' attribute" << std::endl; 00349 continue; 00350 } 00351 00352 URI uri( i->value("url"), i->uriContext() ); 00353 if ( uri.empty() ) { 00354 OE_WARN << LC << "Resource library missing required 'url' element" << std::endl; 00355 continue; 00356 } 00357 00358 osg::ref_ptr<ResourceLibrary> reslib = ResourceLibrary::create( uri ); 00359 if ( !reslib.valid() ) { 00360 OE_WARN << LC << "Resource library creation failed" << std::endl; 00361 continue; 00362 } 00363 00364 addResourceLibrary( name, reslib.get() ); 00365 } 00366 00367 // read any style class definitions. either "class" or "selector" is allowed 00368 ConfigSet selectors = conf.children( "selector" ); 00369 if ( selectors.empty() ) selectors = conf.children( "class" ); 00370 for( ConfigSet::iterator i = selectors.begin(); i != selectors.end(); ++i ) 00371 { 00372 _selectors.push_back( StyleSelector( *i ) ); 00373 } 00374 00375 // read in the actual styles 00376 ConfigSet styles = conf.children( "style" ); 00377 for( ConfigSet::iterator i = styles.begin(); i != styles.end(); ++i ) 00378 { 00379 const Config& styleConf = *i; 00380 00381 if ( styleConf.value("type") == "text/css" ) 00382 { 00383 // read the inline data: 00384 std::string cssString = styleConf.value(); 00385 00386 // if there's a URL, read the CSS from the URL: 00387 if ( styleConf.hasValue("url") ) 00388 { 00389 URI uri( styleConf.value("url"), styleConf.uriContext() ); 00390 HTTPClient::readString( uri.full(), cssString ); 00391 } 00392 00393 // a CSS style definition can actually contain multiple styles. Read them 00394 // and create one style for each in the catalog. 00395 std::stringstream buf( cssString ); 00396 Config css = CssUtils::readConfig( buf ); 00397 css.setURIContext( styleConf.uriContext( ) ); 00398 00399 const ConfigSet children = css.children(); 00400 for(ConfigSet::const_iterator j = children.begin(); j != children.end(); ++j ) 00401 { 00402 Style style( styleConf ); 00403 00404 if ( SLDReader::readStyleFromCSSParams( *j, style ) ) 00405 _styles[ j->key() ] = style; 00406 } 00407 } 00408 else 00409 { 00410 Style style( styleConf ); 00411 _styles[ style.getName() ] = style; 00412 } 00413 } 00414 } 00415