bool SubstituteModelFilter::process(const FeatureList& features, const MarkerSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; //std::map< Feature*, osg::ref_ptr<osg::Node> > featureModels; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // evaluate the marker URI expression: StringExpression uriEx = *symbol->url(); URI markerURI( input->eval(uriEx, &context), uriEx.uriContext() ); // find the corresponding marker in the cache MarkerResource* marker = 0L; MarkerCache::Record rec = _markerCache.get( markerURI ); if ( rec.valid() ) { marker = rec.value(); } else { marker = new MarkerResource(); marker->uri() = markerURI; _markerCache.insert( markerURI, marker ); } // evalute the scale expression (if there is one) float scale = 1.0f; osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); if ( scale == 0.0 ) scale = 1.0; scaleMatrix = osg::Matrix::scale( scale, scale, scale ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( markerURI, scale ); osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { model = context.resourceCache()->getMarkerNode( marker ); if ( scale != 1.0f && dynamic_cast<osg::AutoTransform*>( model.get() ) ) { // clone the old AutoTransform, set the new scale, and copy over its children. osg::AutoTransform* oldAT = dynamic_cast<osg::AutoTransform*>(model.get()); osg::AutoTransform* newAT = osg::clone( oldAT ); // make a scaler and put it between the new AutoTransform and its kids osg::MatrixTransform* scaler = new osg::MatrixTransform(osg::Matrix::scale(scale,scale,scale)); for( unsigned i=0; i<newAT->getNumChildren(); ++i ) scaler->addChild( newAT->getChild(0) ); newAT->removeChildren(0, newAT->getNumChildren()); newAT->addChild( scaler ); model = newAT; } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, rotation ); mat = rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } else { mat = scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->addChild( model.get() ); attachPoint->addChild( xform ); // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } return true; }
bool SubstituteModelFilter::process(const FeatureList& features, const InstanceSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { // Establish SRS information: bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); const SpatialReference* targetSRS = context.getSession()->getMapInfo().getSRS(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; // keep track of failed URIs so we don't waste time or warning messages on them std::set< URI > missing; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); const ModelSymbol* modelSymbol = dynamic_cast<const ModelSymbol*>(symbol); const IconSymbol* iconSymbol = dynamic_cast<const IconSymbol*> (symbol); NumericExpression headingEx; if ( modelSymbol ) headingEx = *modelSymbol->heading(); for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // evaluate the instance URI expression: StringExpression uriEx = *symbol->url(); URI instanceURI( input->eval(uriEx, &context), uriEx.uriContext() ); // find the corresponding marker in the cache osg::ref_ptr<InstanceResource> instance; if ( !findResource(instanceURI, symbol, context, missing, instance) ) continue; // evalute the scale expression (if there is one) float scale = 1.0f; osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); if ( scale == 0.0 ) scale = 1.0; if ( scale != 1.0 ) _normalScalingRequired = true; scaleMatrix = osg::Matrix::scale( scale, scale, scale ); } osg::Matrixd rotationMatrix; if ( modelSymbol && modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( instanceURI, scale ); // cache nodes per instance. osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { context.resourceCache()->getInstanceNode( instance.get(), model ); // if icon decluttering is off, install an AutoTransform. if ( iconSymbol ) { if ( iconSymbol->declutter() == true ) { Decluttering::setEnabled( model->getOrCreateStateSet(), true ); } else if ( dynamic_cast<osg::AutoTransform*>(model.get()) == 0L ) { osg::AutoTransform* at = new osg::AutoTransform(); at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN ); at->setAutoScaleToScreen( true ); at->addChild( model ); model = at; } } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // if necessary, transform the points to the target SRS: if ( !makeECEF && !targetSRS->isEquivalentTo(context.profile()->getSRS()) ) { context.profile()->getSRS()->transform( geom->asVector(), targetSRS ); } for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, targetSRS, rotation ); mat = rotationMatrix * rotation * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } else { mat = rotationMatrix * scaleMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->setDataVariance( osg::Object::STATIC ); xform->addChild( model.get() ); attachPoint->addChild( xform ); if ( context.featureIndex() && !_useDrawInstanced ) { context.featureIndex()->tagNode( xform, input ); } // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } if ( iconSymbol ) { // activate decluttering for icons if requested if ( iconSymbol->declutter() == true ) { Decluttering::setEnabled( attachPoint->getOrCreateStateSet(), true ); } // activate horizon culling if we are in geocentric space if ( context.getSession() && context.getSession()->getMapInfo().isGeocentric() ) { HorizonCullingProgram::install( attachPoint->getOrCreateStateSet() ); } } // active DrawInstanced if required: if ( _useDrawInstanced && Registry::capabilities().supportsDrawInstanced() ) { DrawInstanced::convertGraphToUseDrawInstanced( attachPoint ); // install a shader program to render draw-instanced. DrawInstanced::install( attachPoint->getOrCreateStateSet() ); } return true; }
osgText::Text* TextSymbolizer::create(Feature* feature, const FilterContext* context, const std::string& text ) const { osgText::Text* t = new osgText::Text(); osgText::String::Encoding textEncoding = osgText::String::ENCODING_UNDEFINED; if ( _symbol.valid() && _symbol->encoding().isSet() ) { switch(_symbol->encoding().value()) { case TextSymbol::ENCODING_ASCII: textEncoding = osgText::String::ENCODING_ASCII; break; case TextSymbol::ENCODING_UTF8: textEncoding = osgText::String::ENCODING_UTF8; break; case TextSymbol::ENCODING_UTF16: textEncoding = osgText::String::ENCODING_UTF16; break; case TextSymbol::ENCODING_UTF32: textEncoding = osgText::String::ENCODING_UTF32; break; default: textEncoding = osgText::String::ENCODING_UNDEFINED; break; } } if ( !text.empty() ) { t->setText( text, textEncoding ); } else if ( _symbol.valid() && _symbol->content().isSet() ) { StringExpression expr = *_symbol->content(); std::string newText = feature ? feature->eval(expr, context) : expr.eval(); t->setText( newText, textEncoding ); } if ( _symbol.valid() && _symbol->pixelOffset().isSet() ) { t->setPosition( osg::Vec3(_symbol->pixelOffset()->x(), _symbol->pixelOffset()->y(), 0.0f) ); } // //TODO: relacate in annotationutils... // t->setPosition( osg::Vec3( // positionOffset.x() + _symbol->pixelOffset()->x(), // positionOffset.y() + _symbol->pixelOffset()->y(), // positionOffset.z() ) ); //} // TODO: retian in annotationutils... //else //{ // t->setPosition( positionOffset ); //} //TODO: resonsider defaults here t->setCharacterSizeMode( osgText::Text::OBJECT_COORDS ); #if 0 t->setAutoRotateToScreen( false ); t->setCharacterSizeMode( osgText::Text::OBJECT_COORDS ); #endif t->setCharacterSize( _symbol.valid() && _symbol->size().isSet() ? *_symbol->size() : 16.0f ); t->setColor( _symbol.valid() && _symbol->fill().isSet() ? _symbol->fill()->color() : Color::White ); osgText::Font* font = 0L; if ( _symbol.valid() && _symbol->font().isSet() ) font = osgText::readFontFile( *_symbol->font() ); if ( !font ) font = Registry::instance()->getDefaultFont(); if ( font ) t->setFont( font ); if ( _symbol.valid() ) { // they're the same enum. osgText::Text::AlignmentType at = (osgText::Text::AlignmentType)_symbol->alignment().value(); t->setAlignment( at ); } if ( _symbol.valid() && _symbol->halo().isSet() ) { t->setBackdropColor( _symbol->halo()->color() ); t->setBackdropType( osgText::Text::OUTLINE ); } else if ( !_symbol.valid() ) { // if no symbol at all is provided, default to using a black halo. t->setBackdropColor( osg::Vec4(.3,.3,.3,1) ); t->setBackdropType( osgText::Text::OUTLINE ); } return t; }
/** * Querys the feature source; * Visits each feature and uses the Style Expression to resolve its style class; * Sorts the features into bins based on style class; * Compiles each bin into a separate style group; * Adds the resulting style groups to the provided parent. */ void FeatureModelGraph::queryAndSortIntoStyleGroups(const Query& query, const StringExpression& styleExpr, FeatureSourceIndex* index, osg::Group* parent) { // the profile of the features const FeatureProfile* featureProfile = _session->getFeatureSource()->getFeatureProfile(); // get the extent of the full set of feature data: const GeoExtent& extent = featureProfile->getExtent(); // query the feature source: osg::ref_ptr<FeatureCursor> cursor = _session->getFeatureSource()->createFeatureCursor( query ); if ( !cursor.valid() ) return; // establish the working bounds and a context: Bounds bounds = query.bounds().isSet() ? *query.bounds() : extent.bounds(); FilterContext context( _session.get(), featureProfile, GeoExtent(featureProfile->getSRS(), bounds), index ); StringExpression styleExprCopy( styleExpr ); // visit each feature and run the expression to sort it into a bin. std::map<std::string, FeatureList> styleBins; while( cursor->hasMore() ) { osg::ref_ptr<Feature> feature = cursor->nextFeature(); if ( feature.valid() ) { const std::string& styleString = feature->eval( styleExprCopy, &context ); styleBins[styleString].push_back( feature.get() ); } } // next create a style group per bin. for( std::map<std::string,FeatureList>::iterator i = styleBins.begin(); i != styleBins.end(); ++i ) { const std::string& styleString = i->first; FeatureList& workingSet = i->second; // resolve the style: Style combinedStyle; // if the style string begins with an open bracket, it's an inline style definition. if ( styleString.length() > 0 && styleString.at(0) == '{' ) { Config conf( "style", styleString ); conf.setReferrer( styleExpr.uriContext().referrer() ); conf.set( "type", "text/css" ); combinedStyle = Style(conf); } // otherwise, look up the style in the stylesheet. Do NOT fall back on a default // style in this case: for style expressions, the user must be explicity about // default styling; this is because there is no other way to exclude unwanted // features. else { const Style* selectedStyle = _session->styles()->getStyle(styleString, false); if ( selectedStyle ) combinedStyle = *selectedStyle; } // if there is a valid style, create the node and add it. (Otherwise we will skip // the feature.) if ( !combinedStyle.empty() ) { osg::Group* styleGroup = createStyleGroup(combinedStyle, workingSet, context); if ( styleGroup ) parent->addChild( styleGroup ); } } }
bool SubstituteModelFilter::process(const FeatureList& features, const InstanceSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { // Establish SRS information: bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); const SpatialReference* targetSRS = context.getSession()->getMapInfo().getSRS(); // first, go through the features and build the model cache. Apply the model matrix' scale // factor to any AutoTransforms directly (cloning them as necessary) std::map< std::pair<URI, float>, osg::ref_ptr<osg::Node> > uniqueModels; // URI cache speeds up URI creation since it can be slow. osgEarth::fast_map<std::string, URI> uriCache; // keep track of failed URIs so we don't waste time or warning messages on them std::set< URI > missing; StringExpression uriEx = *symbol->url(); NumericExpression scaleEx = *symbol->scale(); const ModelSymbol* modelSymbol = dynamic_cast<const ModelSymbol*>(symbol); const IconSymbol* iconSymbol = dynamic_cast<const IconSymbol*> (symbol); NumericExpression headingEx; NumericExpression scaleXEx; NumericExpression scaleYEx; NumericExpression scaleZEx; if ( modelSymbol ) { headingEx = *modelSymbol->heading(); scaleXEx = *modelSymbol->scaleX(); scaleYEx = *modelSymbol->scaleY(); scaleZEx = *modelSymbol->scaleZ(); } for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // Run a feature pre-processing script. if ( symbol->script().isSet() ) { StringExpression scriptExpr(symbol->script().get()); input->eval( scriptExpr, &context ); } // evaluate the instance URI expression: const std::string& st = input->eval(uriEx, &context); URI& instanceURI = uriCache[st]; if(instanceURI.empty()) // Create a map, to reuse URI's, since they take a long time to create { instanceURI = URI( st, uriEx.uriContext() ); } // find the corresponding marker in the cache osg::ref_ptr<InstanceResource> instance; if ( !findResource(instanceURI, symbol, context, missing, instance) ) continue; // evalute the scale expression (if there is one) float scale = 1.0f; osg::Vec3d scaleVec(1.0, 1.0, 1.0); osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); scaleVec.set(scale, scale, scale); } if ( modelSymbol ) { if ( modelSymbol->scaleX().isSet() ) { scaleVec.x() *= input->eval( scaleXEx, &context ); } if ( modelSymbol->scaleY().isSet() ) { scaleVec.y() *= input->eval( scaleYEx, &context ); } if ( modelSymbol->scaleZ().isSet() ) { scaleVec.z() *= input->eval( scaleZEx, &context ); } } if ( scaleVec.x() == 0.0 ) scaleVec.x() = 1.0; if ( scaleVec.y() == 0.0 ) scaleVec.y() = 1.0; if ( scaleVec.z() == 0.0 ) scaleVec.z() = 1.0; scaleMatrix = osg::Matrix::scale( scaleVec ); osg::Matrixd rotationMatrix; if ( modelSymbol && modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } // how that we have a marker source, create a node for it std::pair<URI,float> key( instanceURI, iconSymbol? scale : 1.0f ); //use 1.0 for models, since we don't want unique models based on scaling // cache nodes per instance. osg::ref_ptr<osg::Node>& model = uniqueModels[key]; if ( !model.valid() ) { // Always clone the cached instance so we're not processing data that's // already in the scene graph. -gw context.resourceCache()->cloneOrCreateInstanceNode(instance.get(), model, context.getDBOptions()); // if icon decluttering is off, install an AutoTransform. if ( iconSymbol ) { if ( iconSymbol->declutter() == true ) { ScreenSpaceLayout::activate(model->getOrCreateStateSet()); } else if ( dynamic_cast<osg::AutoTransform*>(model.get()) == 0L ) { osg::AutoTransform* at = new osg::AutoTransform(); at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN ); at->setAutoScaleToScreen( true ); at->addChild( model ); model = at; } } } if ( model.valid() ) { GeometryIterator gi( input->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // if necessary, transform the points to the target SRS: if ( !makeECEF && !targetSRS->isEquivalentTo(context.profile()->getSRS()) ) { context.profile()->getSRS()->transform( geom->asVector(), targetSRS ); } for( unsigned i=0; i<geom->size(); ++i ) { osg::Matrixd mat; // need to recalcluate expression-based data per-point, not just per-feature! float scale = 1.0f; osg::Vec3d scaleVec(1.0, 1.0, 1.0); osg::Matrixd scaleMatrix; if ( symbol->scale().isSet() ) { scale = input->eval( scaleEx, &context ); scaleVec.set(scale, scale, scale); } if ( modelSymbol ) { if ( modelSymbol->scaleX().isSet() ) { scaleVec.x() *= input->eval( scaleXEx, &context ); } if ( modelSymbol->scaleY().isSet() ) { scaleVec.y() *= input->eval( scaleYEx, &context ); } if ( modelSymbol->scaleZ().isSet() ) { scaleVec.z() *= input->eval( scaleZEx, &context ); } } if ( scaleVec.x() == 0.0 ) scaleVec.x() = 1.0; if ( scaleVec.y() == 0.0 ) scaleVec.y() = 1.0; if ( scaleVec.z() == 0.0 ) scaleVec.z() = 1.0; scaleMatrix = osg::Matrix::scale( scaleVec ); if ( modelSymbol->heading().isSet() ) { float heading = input->eval(headingEx, &context); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } osg::Vec3d point = (*geom)[i]; if ( makeECEF ) { // the "rotation" element lets us re-orient the instance to ensure it's pointing up. We // could take a shortcut and just use the current extent's local2world matrix for this, // but if the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, context.profile()->getSRS(), point, targetSRS, rotation ); mat = scaleMatrix * rotationMatrix * rotation * osg::Matrixd::translate( point ) * _world2local; } else { mat = scaleMatrix * rotationMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->setDataVariance( osg::Object::STATIC ); xform->addChild( model.get() ); attachPoint->addChild( xform ); // Only tag nodes if we aren't using clustering. if ( context.featureIndex() && !_cluster) { context.featureIndex()->tagNode( xform, input ); } // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr, &context); if ( !name.empty() ) xform->setName( name ); } } } } } if ( iconSymbol ) { // activate decluttering for icons if requested if ( iconSymbol->declutter() == true ) { ScreenSpaceLayout::activate(attachPoint->getOrCreateStateSet()); } // activate horizon culling if we are in geocentric space if ( context.getSession() && context.getSession()->getMapInfo().isGeocentric() ) { // should this use clipping, or a horizon cull callback? //HorizonCullingProgram::install( attachPoint->getOrCreateStateSet() ); attachPoint->getOrCreateStateSet()->setMode(GL_CLIP_DISTANCE0, 1); } } // active DrawInstanced if required: if ( _useDrawInstanced ) { DrawInstanced::convertGraphToUseDrawInstanced( attachPoint ); // install a shader program to render draw-instanced. DrawInstanced::install( attachPoint->getOrCreateStateSet() ); } return true; }