void AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx ) { NumericExpression scaleExpr; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); double offsetZ = 0.0; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( Geometry::iterator g = geom->begin(); g != geom->end(); ++g ) { g->z() *= scaleZ; g->z() += offsetZ; if ( g->z() < minHAT ) minHAT = g->z(); if ( g->z() > maxHAT ) maxHAT = g->z(); } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } } }
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; }
//clustering: // troll the external model for geodes. for each geode, create a geode in the target // model. then, for each geometry in that geode, replicate it once for each instance of // the model in the feature batch and transform the actual verts to a local offset // relative to the tile centroid. Finally, reassemble all the geodes and optimize. // hopefully stateset sharing etc will work out. we may need to strip out LODs too. bool SubstituteModelFilter::cluster(const FeatureList& features, const MarkerSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { MarkerToFeatures markerToFeatures; // first, sort the features into buckets, each bucket corresponding to a // unique marker. for (FeatureList::const_iterator i = features.begin(); i != features.end(); ++i) { Feature* f = i->get(); // resolve the URI for the marker: StringExpression uriEx( *symbol->url() ); URI markerURI( f->eval( uriEx, &context ), uriEx.uriContext() ); // find and load the corresponding marker model. We're using the session-level // object store to cache models. This is thread-safe sine we are always going // to CLONE the model before using it. osg::ref_ptr<osg::Node> model = context.getSession()->getObject<osg::Node>( markerURI.full() ); if ( !model.valid() ) { osg::ref_ptr<MarkerResource> mres = new MarkerResource(); mres->uri() = markerURI; model = mres->createNode( context.getSession()->getDBOptions() ); if ( model.valid() ) { // store it, but only if there isn't already one in there. context.getSession()->putObject( markerURI.full(), model.get(), false ); } } if ( model.valid() ) { MarkerToFeatures::iterator itr = markerToFeatures.find( model.get() ); if (itr == markerToFeatures.end()) markerToFeatures[ model.get() ].push_back( f ); else itr->second.push_back( f ); } } //For each model, cluster the features that use that marker for (MarkerToFeatures::iterator i = markerToFeatures.begin(); i != markerToFeatures.end(); ++i) { osg::Node* prototype = i->first; // we're using the Session cache since we know we'll be cloning. if ( prototype ) { osg::Node* clone = osg::clone( prototype, osg::CopyOp::DEEP_COPY_ALL ); // ..and apply the clustering to the copy. ClusterVisitor cv( i->second, symbol, this, context ); clone->accept( cv ); attachPoint->addChild( clone ); } } return true; }
void apply( osg::Geode& geode ) { // save the geode's drawables.. osg::Geode::DrawableList old_drawables = geode.getDrawableList(); //OE_DEBUG << "ClusterVisitor geode " << &geode << " featureNode=" << _featureNode << " drawables=" << old_drawables.size() << std::endl; // ..and clear out the drawables list. geode.removeDrawables( 0, geode.getNumDrawables() ); // foreach each drawable that was originally in the geode... for( osg::Geode::DrawableList::iterator i = old_drawables.begin(); i != old_drawables.end(); i++ ) { osg::Geometry* originalDrawable = dynamic_cast<osg::Geometry*>( i->get() ); if ( !originalDrawable ) continue; // go through the list of input features... for( FeatureList::const_iterator j = _features.begin(); j != _features.end(); j++ ) { Feature* feature = j->get(); osg::Matrixd scaleMatrix; if ( _symbol->scale().isSet() ) { double scale = feature->eval( _scaleExpr, &_cx ); scaleMatrix.makeScale( scale, scale, scale ); } osg::Matrixd rotationMatrix; if ( _modelSymbol && _modelSymbol->heading().isSet() ) { float heading = feature->eval( _headingExpr, &_cx ); rotationMatrix.makeRotate( osg::Quat(osg::DegreesToRadians(heading), osg::Vec3(0,0,1)) ); } GeometryIterator gi( feature->getGeometry(), false ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // if necessary, transform the points to the target SRS: if ( !_makeECEF && !_targetSRS->isEquivalentTo(_srs) ) { _srs->transform( geom->asVector(), _targetSRS ); } for( Geometry::const_iterator k = geom->begin(); k != geom->end(); ++k ) { osg::Vec3d point = *k; osg::Matrixd mat; if ( _makeECEF ) { osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( point, _srs, point, _targetSRS, rotation ); mat = rotationMatrix * rotation * scaleMatrix * osg::Matrixd::translate(point) * _f2n->world2local(); } else { mat = rotationMatrix * scaleMatrix * osg::Matrixd::translate(point) * _f2n->world2local(); } // clone the source drawable once for each input feature. osg::ref_ptr<osg::Geometry> newDrawable = osg::clone( originalDrawable, osg::CopyOp::DEEP_COPY_ARRAYS | osg::CopyOp::DEEP_COPY_PRIMITIVES ); osg::Vec3Array* verts = dynamic_cast<osg::Vec3Array*>( newDrawable->getVertexArray() ); if ( verts ) { for( osg::Vec3Array::iterator v = verts->begin(); v != verts->end(); ++v ) { (*v).set( (*v) * mat ); } // add the new cloned, translated drawable back to the geode. geode.addDrawable( newDrawable.get() ); if ( _cx.featureIndex() ) _cx.featureIndex()->tagPrimitiveSets( newDrawable.get(), feature ); } } } } } geode.dirtyBound(); MeshConsolidator::run( geode ); osg::NodeVisitor::apply( geode ); }
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; }
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; }
bool BuildGeometryFilter::process( FeatureList& features, const FilterContext& context ) { bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; const Style& myStyle = input->style().isSet() ? *input->style() : _style; bool setLinePropsHere = input->style().isSet(); // otherwise it will be set globally, we assume float width = 1.0f; bool hasPolyOutline = false; const PointSymbol* pointSymbol = myStyle.get<PointSymbol>(); const LineSymbol* lineSymbol = myStyle.get<LineSymbol>(); const PolygonSymbol* polySymbol = myStyle.get<PolygonSymbol>(); // resolve the geometry type from the component type and the symbology: Geometry::Type renderType = Geometry::TYPE_UNKNOWN; // First priority is a matching part type and symbol: if ( polySymbol != 0L && part->getType() == Geometry::TYPE_POLYGON ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L && part->isLinear() ) { renderType = part->getType(); } else if ( pointSymbol != 0L && part->getType() == Geometry::TYPE_POINTSET ) { renderType = Geometry::TYPE_POINTSET; } // Second priority is the symbol: else if ( polySymbol != 0L ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L ) { if ( part->getType() == Geometry::TYPE_POLYGON ) renderType = Geometry::TYPE_RING; else renderType = Geometry::TYPE_LINESTRING; } else if ( pointSymbol != 0L ) { renderType = Geometry::TYPE_POINTSET; } // No symbol? just use the geometry type. else { renderType = part->getType(); } // validate the geometry: if ( renderType == Geometry::TYPE_POLYGON && part->size() < 3 ) continue; else if ( (renderType == Geometry::TYPE_LINESTRING || renderType == Geometry::TYPE_RING) && part->size() < 2 ) continue; // resolve the color: osg::Vec4f primaryColor = polySymbol ? osg::Vec4f(polySymbol->fill()->color()) : lineSymbol ? osg::Vec4f(lineSymbol->stroke()->color()) : pointSymbol ? osg::Vec4f(pointSymbol->fill()->color()) : osg::Vec4f(1,1,1,1); osg::Geometry* osgGeom = new osg::Geometry(); osgGeom->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = 0L; if ( renderType == Geometry::TYPE_POLYGON ) { buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom); allPoints = static_cast<osg::Vec3Array*>( osgGeom->getVertexArray() ); } else { // line or point geometry GLenum primMode = renderType == Geometry::TYPE_LINESTRING ? GL_LINE_STRIP : renderType == Geometry::TYPE_RING ? GL_LINE_LOOP : GL_POINTS; allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); osgGeom->setVertexArray( allPoints ); applyLineAndPointSymbology( osgGeom->getOrCreateStateSet(), lineSymbol, pointSymbol ); if ( primMode == GL_POINTS && allPoints->size() == 1 ) { const osg::Vec3d& center = (*allPoints)[0]; osgGeom->setInitialBound( osg::BoundingBox(center-osg::Vec3(.5,.5,.5), center+osg::Vec3(.5,.5,.5)) ); } } if (allPoints->getVertexBufferObject()) allPoints->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); // subdivide the mesh if necessary to conform to an ECEF globe: if ( makeECEF && renderType != Geometry::TYPE_POINTSET ) { // check for explicit tessellation disable: const LineSymbol* line = _style.get<LineSymbol>(); bool disableTess = line && line->tessellation().isSetTo(0); if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } } // assign the primary color: #if USE_SINGLE_COLOR osg::Vec4Array* colors = new osg::Vec4Array( 1 ); (*colors)[0] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); #else osg::Vec4Array* colors = new osg::Vec4Array( osgGeom->getVertexArray()->getNumElements() ); //allPoints->size() ); for(unsigned c=0; c<colors->size(); ++c) (*colors)[c] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif osgGeom->setColorArray( colors ); _geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( osgGeom, input ); // build secondary geometry, if necessary (polygon outlines) if ( renderType == Geometry::TYPE_POLYGON && lineSymbol ) { // polygon offset on the poly so the outline doesn't z-fight osgGeom->getOrCreateStateSet()->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 ); osg::Geometry* outline = new osg::Geometry(); outline->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); buildPolygon(part, featureSRS, mapSRS, makeECEF, false, outline); if ( outline->getVertexArray()->getVertexBufferObject() ) outline->getVertexArray()->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); osg::Vec4f outlineColor = lineSymbol->stroke()->color(); osg::Vec4Array* outlineColors = new osg::Vec4Array(); #if USE_SINGLE_COLOR outlineColors->reserve(1); outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_OVERALL ); #else unsigned pcount = part->getTotalPointCount(); outlineColors->reserve( pcount ); for( unsigned c=0; c < pcount; ++c ) outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif outline->setColorArray(outlineColors); // check for explicit tessellation disable: bool disableTess = lineSymbol && lineSymbol->tessellation().isSetTo(0); // subdivide if necessary. if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider for outlines with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); if ( input->geoInterp().isSet() ) ms.run( *outline, threshold, *input->geoInterp() ); else ms.run( *outline, threshold, *_geoInterp ); } applyLineAndPointSymbology( outline->getOrCreateStateSet(), lineSymbol, 0L ); // make normals before adding an outline osgUtil::SmoothingVisitor sv; _geode->accept( sv ); _geode->addDrawable( outline ); //_featureNode->addDrawable( outline, input->getFID() ); // Mark each primitive set with its feature ID. if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( outline, input ); } } } return true; }
/** * Creates a complete set of positioned label nodes from a feature list. */ osg::Node* createNode( const FeatureList& input, const Style& style, const FilterContext& context ) { const TextSymbol* text = style.get<TextSymbol>(); if ( !text ) return 0L; osg::Group* group = new osg::Group(); Decluttering::setEnabled( group->getOrCreateStateSet(), true ); if ( text->priority().isSet() ) { DeclutteringOptions dco = Decluttering::getOptions(); dco.sortByPriority() = text->priority().isSet(); Decluttering::setOptions( dco ); } StringExpression contentExpr ( *text->content() ); NumericExpression priorityExpr( *text->priority() ); if ( text->removeDuplicateLabels() == true ) { // in remove-duplicates mode, make a list of unique features, selecting // the one with the largest area as the one we'll use for labeling. typedef std::pair<double, osg::ref_ptr<const Feature> > Entry; typedef std::map<std::string, Entry> EntryMap; EntryMap used; for( FeatureList::const_iterator i = input.begin(); i != input.end(); ++i ) { Feature* feature = i->get(); if ( feature && feature->getGeometry() ) { const std::string& value = feature->eval( contentExpr ); if ( !value.empty() ) { double area = feature->getGeometry()->getBounds().area2d(); if ( used.find(value) == used.end() ) { used[value] = Entry(area, feature); } else { Entry& biggest = used[value]; if ( area > biggest.first ) { biggest.first = area; biggest.second = feature; } } } } } for( EntryMap::iterator i = used.begin(); i != used.end(); ++i ) { const std::string& value = i->first; const Feature* feature = i->second.second.get(); group->addChild( makeLabelNode(context, feature, value, text, priorityExpr) ); } } else { for( FeatureList::const_iterator i = input.begin(); i != input.end(); ++i ) { const Feature* feature = i->get(); if ( !feature ) continue; const Geometry* geom = feature->getGeometry(); if ( !geom ) continue; const std::string& value = feature->eval( contentExpr, &context ); if ( value.empty() ) continue; group->addChild( makeLabelNode(context, feature, value, text, priorityExpr) ); } } #if 0 // good idea but needs work. DepthOffsetGroup* dog = new DepthOffsetGroup(); dog->setMinimumOffset( 500.0 ); dog->addChild( group ); return dog; #endif return group; }
osg::Geode* BuildGeometryFilter::processPolygonizedLines(FeatureList& features, bool twosided, FilterContext& context) { osg::Geode* geode = new osg::Geode(); // establish some referencing bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } // iterate over all features. for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* input = i->get(); // extract the required line symbol; bail out if not found. const LineSymbol* line = input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() : _style.get<LineSymbol>(); if ( !line ) continue; // run a symbol script if present. if ( line->script().isSet() ) { StringExpression temp( line->script().get() ); input->eval( temp, &context ); } // The operator we'll use to make lines into polygons. PolygonizeLinesOperator polygonizer( *line->stroke() ); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // if the underlying geometry is a ring (or a polygon), close it so the // polygonizer will generate a closed loop. Ring* ring = dynamic_cast<Ring*>(part); if ( ring ) ring->close(); // skip invalid geometry if ( part->size() < 2 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array(); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts.get(), normals.get(), mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonizer( verts.get(), normals.get(), twosided ); if ( geom ) { geode->addDrawable( geom ); } // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( geom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( geom, input->getDouble("__oe_verticalOffset", 0.0) ); } } polygonizer.installShaders( geode ); } return geode; }
osg::Geode* BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up the reference system info: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // access the polygon symbol, and bail out if there isn't one const PolygonSymbol* poly = input->style().isSet() && input->style()->has<PolygonSymbol>() ? input->style()->get<PolygonSymbol>() : _style.get<PolygonSymbol>(); if ( !poly ) continue; // run a symbol script if present. if ( poly->script().isSet() ) { StringExpression temp( poly->script().get() ); input->eval( temp, &context ); } GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); part->removeDuplicates(); // skip geometry that is invalid for a polygon if ( part->size() < 3 ) continue; // resolve the color: osg::Vec4f primaryColor = poly->fill()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); //osgGeom->setUseVertexBufferObjects( true ); //osgGeom->setUseDisplayList( false ); // are we embedding a feature name? if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // compute localizing matrices or use globals osg::Matrixd w2l, l2w; if (makeECEF) { osgEarth::GeoExtent featureExtent(featureSRS); featureExtent.expandToInclude(part->getBounds()); computeLocalizers(context, featureExtent, w2l, l2w); } else { w2l = _world2local; l2w = _local2world; } // build the geometry: tileAndBuildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom, w2l); //buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom, w2l); osg::Vec3Array* allPoints = static_cast<osg::Vec3Array*>(osgGeom->getVertexArray()); if (allPoints && allPoints->size() > 0) { // subdivide the mesh if necessary to conform to an ECEF globe: if ( makeECEF ) { //convert back to world coords for( osg::Vec3Array::iterator i = allPoints->begin(); i != allPoints->end(); ++i ) { osg::Vec3d v(*i); v = v * l2w; v = v * _world2local; (*i)._v[0] = v[0]; (*i)._v[1] = v[1]; (*i)._v[2] = v[2]; } double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } // assign the primary color array. PER_VERTEX required in order to support // vertex optimization later unsigned count = osgGeom->getVertexArray()->getNumElements(); osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( count, primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( osgGeom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) ); } } } } return geode; }
osg::Node* BuildTextOperator::operator()(const FeatureList& features, const TextSymbol* symbol, const FilterContext& context) { if (!symbol) return 0; std::set< std::string > labelNames; bool removeDuplicateLabels = symbol->removeDuplicateLabels().isSet() ? symbol->removeDuplicateLabels().get() : false; StringExpression contentExpr = *symbol->content(); osg::Geode* result = new osg::Geode; for (FeatureList::const_iterator itr = features.begin(); itr != features.end(); ++itr) { Feature* feature = itr->get(); if (!feature->getGeometry()) continue; std::string text; //If the feature is a TextAnnotation, just get the value from it TextAnnotation* annotation = dynamic_cast<TextAnnotation*>(feature); if (annotation) { text = annotation->text(); } else if (symbol->content().isSet()) { //Get the text from the specified content and referenced attributes text = feature->eval( contentExpr ); //std::string content = symbol->content().value(); //text = parseAttributes(feature, content, symbol->contentAttributeDelimiter().value()); } //else if (symbol->attribute().isSet()) //{ // //Get the text from the specified attribute // std::string attr = symbol->attribute().value(); // text = feature->getAttr(attr); //} if (text.empty()) continue; //See if there is a duplicate name if (removeDuplicateLabels && labelNames.find(text) != labelNames.end()) continue; bool rotateToScreen = symbol->rotateToScreen().isSet() ? symbol->rotateToScreen().value() : false; // find the centroid osg::Vec3d position; osg::Quat orientation; GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); TextSymbol::LinePlacement linePlacement = symbol->linePlacement().isSet() ? symbol->linePlacement().get() : TextSymbol::LINEPLACEMENT_ALONG_LINE; if (geom->getType() == Symbology::Geometry::TYPE_LINESTRING && linePlacement == TextSymbol::LINEPLACEMENT_ALONG_LINE) { //Compute the "middle" of the line string LineString* lineString = static_cast<LineString*>(geom); double length = lineString->getLength(); double center = length / 2.0; osg::Vec3d start, end; if (lineString->getSegment(center, start, end)) { TextSymbol::LineOrientation lineOrientation = symbol->lineOrientation().isSet() ? symbol->lineOrientation().get() : TextSymbol::LINEORIENTATION_HORIZONTAL; position = (end + start) / 2.0; //We don't want to orient the text at all if we are rotating to the screen if (!rotateToScreen && lineOrientation != TextSymbol::LINEORIENTATION_HORIZONTAL) { osg::Vec3d dir = (end-start); dir.normalize(); if (lineOrientation == TextSymbol::LINEORIENTATION_PERPENDICULAR) { osg::Vec3d up(0,0,1); const SpatialReference* srs = context.profile()->getSRS(); if (srs && context.isGeocentric() && srs->getEllipsoid()) { osg::Vec3d w = context.toWorld( position ); up = srs->getEllipsoid()->computeLocalUpVector(w.x(), w.y(), w.z()); } dir = up ^ dir; } orientation.makeRotate(osg::Vec3d(1,0,0), dir); } } else { //Fall back on using the center position = lineString->getBounds().center(); } } else { position = geom->getBounds().center(); } } osgText::Text* t = new osgText::Text(); t->setText( text ); std::string font = "fonts/arial.ttf"; if (symbol->font().isSet() && !symbol->font().get().empty()) { font = symbol->font().value(); } t->setFont( font ); t->setAutoRotateToScreen( rotateToScreen ); TextSymbol::SizeMode sizeMode = symbol->sizeMode().isSet() ? symbol->sizeMode().get() : TextSymbol::SIZEMODE_SCREEN; if (sizeMode == TextSymbol::SIZEMODE_SCREEN) { t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS ); } else if (sizeMode == TextSymbol::SIZEMODE_OBJECT) { t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS ); } float size = symbol->size().isSet() ? symbol->size().get() : 32.0f; t->setCharacterSize( size ); /* //TODO: We need to do something to account for autotransformed text that is under a LOD. Setting the initial bound works sometimes but not all the time. if (rotateToScreen) { //Set the initial bound so that OSG will traverse the text even if it's under an LOD. osg::BoundingBox bb; bb.expandBy( osg::BoundingSphere(position, size)); t->setInitialBound( bb); }*/ //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT ); //t->setCharacterSize( 300000.0f ); t->setPosition( position ); t->setRotation( orientation); t->setAlignment( osgText::TextBase::CENTER_CENTER ); t->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS), osg::StateAttribute::ON ); t->getOrCreateStateSet()->setRenderBinDetails( 99999, "RenderBin", osg::StateSet::OVERRIDE_RENDERBIN_DETAILS ); // apply styling as appropriate: osg::Vec4f textColor = symbol->fill()->color(); osg::Vec4f haloColor = symbol->halo()->color(); t->setColor( textColor ); t->setBackdropColor( haloColor ); t->setBackdropType( osgText::Text::OUTLINE ); if ( context.isGeocentric() ) { // install a cluster culler t->setCullCallback( new CullPlaneCallback( position * context.inverseReferenceFrame() ) ); } if (_hideClutter) { osg::BoundingBox tBound = t->getBound(); osg::ref_ptr<osgUtil::PolytopeIntersector> intersector = new osgUtil::PolytopeIntersector(osgUtil::Intersector::MODEL, tBound.xMin(), tBound.yMin(), tBound.xMax(), tBound.yMax()); osgUtil::IntersectionVisitor intersectVisitor(intersector.get()); result->accept(intersectVisitor); if (!intersector->containsIntersections()) { result->addDrawable( t ); if (removeDuplicateLabels) labelNames.insert(text); } } else { result->addDrawable( t ); if (removeDuplicateLabels) labelNames.insert(text); } } return result; }
osg::Geode* BuildGeometryFilter::processPolygons(FeatureList& features, const FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up the reference system info: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip geometry that is invalid for a polygon if ( part->size() < 3 ) continue; // access the polygon symbol, and bail out if there isn't one const PolygonSymbol* poly = input->style().isSet() && input->style()->has<PolygonSymbol>() ? input->style()->get<PolygonSymbol>() : _style.get<PolygonSymbol>(); if ( !poly ) continue; // resolve the color: osg::Vec4f primaryColor = poly->fill()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); osgGeom->setUseVertexBufferObjects( true ); osgGeom->setUseDisplayList( false ); // are we embedding a feature name? if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom); osg::Vec3Array* allPoints = static_cast<osg::Vec3Array*>(osgGeom->getVertexArray()); // subdivide the mesh if necessary to conform to an ECEF globe: if ( makeECEF ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } // assign the primary color array. PER_VERTEX required in order to support // vertex optimization later osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( osgGeom, input ); } } return geode; }
bool SubstituteModelFilter::process(const FeatureList& features, const MarkerSymbol* symbol, Session* session, osg::Group* attachPoint, FilterContext& context ) { bool makeECEF = context.getSession()->getMapInfo().isGeocentric(); for( FeatureList::const_iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); 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 it the tile is big enough the up vectors won't be quite right. osg::Matrixd rotation; ECEF::transformAndGetRotationMatrix( context.profile()->getSRS(), point, point, rotation ); mat = rotation * _modelMatrix * osg::Matrixd::translate( point ) * _world2local; } else { mat = _modelMatrix * osg::Matrixd::translate( point ) * _world2local; } osg::MatrixTransform* xform = new osg::MatrixTransform(); xform->setMatrix( mat ); xform->setDataVariance( osg::Object::STATIC ); MarkerFactory factory( session); osg::ref_ptr< osg::Node > model = factory.getOrCreateNode( input, symbol ); if (model.get()) { xform->addChild( model.get() ); } attachPoint->addChild( xform ); // name the feature if necessary if ( !_featureNameExpr.empty() ) { const std::string& name = input->eval( _featureNameExpr ); if ( !name.empty() ) xform->setName( name ); } } } } return true; }
bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the colors: osg::Vec4f wallColor(1,1,1,0), wallBaseColor(1,1,1,0), roofColor(1,1,1,0), outlineColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } } if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } // Create the extruded geometry! if (extrudeGeometry( part, height, offset, *_extrusionSymbol->flatten(), walls.get(), rooflines.get(), baselines.get(), outlines.get(), wallColor, wallBaseColor, roofColor, outlineColor, wallSkin, roofSkin, context ) ) { if ( wallSkin ) { context.resourceCache()->getStateSet( wallSkin, wallStateSet ); } // generate per-vertex normals, altering the geometry as necessary to avoid // smoothing around sharp corners osgUtil::SmoothingVisitor::smooth( *walls.get(), osg::DegreesToRadians(_wallAngleThresh_deg) ); // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(rooflines.get()) ); // generate default normals (no crease angle necessary; they are all pointing up) // TODO do this manually; probably faster if ( !_makeStencilVolume ) osgUtil::SmoothingVisitor::smooth( *rooflines.get() ); if ( roofSkin ) { context.resourceCache()->getStateSet( roofSkin, roofStateSet ); } } if ( baselines.valid() ) { osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); addDrawable( walls.get(), wallStateSet.get(), name, input, index ); if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } } return true; }
osg::Geode* BuildGeometryFilter::processLines(FeatureList& features, FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up referencing information: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); // extract the required line symbol; bail out if not found. const LineSymbol* line = input->style().isSet() && input->style()->has<LineSymbol>() ? input->style()->get<LineSymbol>() : _style.get<LineSymbol>(); if ( !line ) continue; // run a symbol script if present. if ( line->script().isSet() ) { StringExpression temp( line->script().get() ); input->eval( temp, &context ); } GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip invalid geometry for lines. if ( part->size() < 2 ) continue; // if the underlying geometry is a ring (or a polygon), use a line loop; otherwise // use a line strip. GLenum primMode = dynamic_cast<Ring*>(part) ? GL_LINE_LOOP : GL_LINE_STRIP; // resolve the color: osg::Vec4f primaryColor = line->stroke()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); //osgGeom->setUseVertexBufferObjects( true ); //osgGeom->setUseDisplayList( false ); // embed the feature name if requested. Warning: blocks geometry merge optimization! if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays(primMode, 0, allPoints->getNumElements()) ); osgGeom->setVertexArray( allPoints ); if ( input->style().isSet() ) { //TODO: re-evaluate this. does it hinder geometry merging? applyLineSymbology( osgGeom->getOrCreateStateSet(), line ); } // subdivide the mesh if necessary to conform to an ECEF globe; // but if the tessellation is set to zero, or if the style specifies a // tessellation size, skip this step. if ( makeECEF && !line->tessellation().isSetTo(0) && !line->tessellationSize().isSet() ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } // assign the primary color (PER_VERTEX required for later optimization) osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( osgGeom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) ); } } } return geode; }
void AltitudeFilter::pushAndClamp( FeatureList& features, FilterContext& cx ) { const Session* session = cx.getSession(); // the map against which we'll be doing elevation clamping //MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); MapFrame mapf = session->createMapFrame( (Map::ModelParts)(Map::TERRAIN_LAYERS | Map::MODEL_LAYERS) ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); osg::ref_ptr<const SpatialReference> featureSRS = cx.profile()->getSRS(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); // want a result even if it's low res eq.setFallBackOnNoData( true ); NumericExpression scaleExpr; if ( _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); // whether to record the min/max height-above-terrain values. bool collectHATs = _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN || _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE; // whether to clamp every vertex (or just the centroid) bool perVertex = _altitude->binding() == AltitudeSymbol::BINDING_VERTEX; // whether the SRS's have a compatible vertical datum. bool vertEquiv = featureSRS->isVertEquivalentTo( mapSRS ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double maxTerrainZ = -DBL_MAX; double minTerrainZ = DBL_MAX; double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); double offsetZ = 0.0; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); // Absolute heights in Z. Only need to collect the HATs; the geometry // remains unchanged. if ( _altitude->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE ) { if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - elevations[i]; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double z = p.z(); if ( !vertEquiv ) { osg::Vec3d tempgeo; if ( !featureSRS->transform(p, mapSRS->getGeographicSRS(), tempgeo) ) z = tempgeo.z(); } double hat = z - centroidElevation; if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Heights-above-ground in Z. Need to resolve this to an absolute number // and record HATs along the way. else if ( _altitude->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = !vertEquiv ? SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()) : 0L; if ( perVertex ) { std::vector<double> elevations; elevations.reserve( geom->size() ); if ( eq.getElevations( geom->asVector(), featureSRS, elevations, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = elevations[i] + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; if ( elevations[i] > maxTerrainZ ) maxTerrainZ = elevations[i]; if ( elevations[i] < minTerrainZ ) minTerrainZ = elevations[i]; } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() *= scaleZ; p.z() += offsetZ; double hat = p.z(); p.z() = centroidElevation + p.z(); // if necessary, convert the Z value (which is now in the map's SRS) back to // the feature's SRS. if ( !vertEquiv ) { featureSRSwithMapVertDatum->transform(p, featureSRS, p); } if ( hat > maxHAT ) maxHAT = hat; if ( hat < minHAT ) minHAT = hat; } if ( centroidElevation > maxTerrainZ ) maxTerrainZ = centroidElevation; if ( centroidElevation < minTerrainZ ) minTerrainZ = centroidElevation; } } } // Clamp - replace the geometry's Z with the terrain height. else // CLAMP_TO_TERRAIN { if ( perVertex ) { eq.getElevations( geom->asVector(), featureSRS, true, _maxRes ); // if necessary, transform the Z values (which are now in the map SRS) back // into the feature's SRS. if ( !vertEquiv ) { osg::ref_ptr<const SpatialReference> featureSRSwithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); osg::Vec3d tempgeo; for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; featureSRSwithMapVertDatum->transform(p, featureSRS, p); } } } else // per-centroid { osgEarth::Bounds bounds = geom->getBounds(); const osg::Vec2d& center = bounds.center2d(); GeoPoint centroid(featureSRS, center.x(), center.y()); double centroidElevation; osg::ref_ptr<const SpatialReference> featureSRSWithMapVertDatum; if ( !vertEquiv ) featureSRSWithMapVertDatum = SpatialReference::create(featureSRS->getHorizInitString(), mapSRS->getVertInitString()); if ( eq.getElevation( centroid, centroidElevation, _maxRes ) ) { for( unsigned i=0; i<geom->size(); ++i ) { osg::Vec3d& p = (*geom)[i]; p.z() = centroidElevation; if ( !vertEquiv ) { featureSRSWithMapVertDatum->transform(p, featureSRS, p); } } } } } if ( !collectHATs ) { for( Geometry::iterator i = geom->begin(); i != geom->end(); ++i ) { i->z() *= scaleZ; i->z() += offsetZ; } } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } if ( minTerrainZ != DBL_MAX ) { feature->set( "__min_terrain_z", minTerrainZ ); feature->set( "__max_terrain_z", maxTerrainZ ); } } }
osg::Geode* BuildGeometryFilter::processPoints(FeatureList& features, FilterContext& context) { osg::Geode* geode = new osg::Geode(); bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; // set up referencing information: if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), true ); while( parts.hasMore() ) { Geometry* part = parts.next(); // extract the required point symbol; bail out if not found. const PointSymbol* point = input->style().isSet() && input->style()->has<PointSymbol>() ? input->style()->get<PointSymbol>() : _style.get<PointSymbol>(); if ( !point ) continue; // resolve the color: osg::Vec4f primaryColor = point->fill()->color(); osg::ref_ptr<osg::Geometry> osgGeom = new osg::Geometry(); //osgGeom->setUseVertexBufferObjects( true ); //osgGeom->setUseDisplayList( false ); // embed the feature name if requested. Warning: blocks geometry merge optimization! if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays(GL_POINTS, 0, allPoints->getNumElements()) ); osgGeom->setVertexArray( allPoints ); if ( input->style().isSet() ) { //TODO: re-evaluate this. does it hinder geometry merging? applyPointSymbology( osgGeom->getOrCreateStateSet(), point ); } // assign the primary color (PER_VERTEX required for later optimization) osg::Vec4Array* colors = new osg::Vec4Array; colors->assign( osgGeom->getVertexArray()->getNumElements(), primaryColor ); osgGeom->setColorArray( colors ); osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagDrawable( osgGeom, input ); // install clamping attributes if necessary if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::applyDefaultClampingAttrs( osgGeom, input->getDouble("__oe_verticalOffset", 0.0) ); } } } return geode; }
/** * Creates a complete set of positioned label nodes from a feature list. */ osg::Node* createNode( const FeatureList& input, const Style& style, FilterContext& context ) { if ( style.get<TextSymbol>() == 0L && style.get<IconSymbol>() == 0L ) return 0L; // copy the style so we can (potentially) modify the text symbol. Style styleCopy = style; TextSymbol* text = styleCopy.get<TextSymbol>(); IconSymbol* icon = styleCopy.get<IconSymbol>(); osg::Group* group = new osg::Group(); StringExpression textContentExpr ( text ? *text->content() : StringExpression() ); NumericExpression textPriorityExpr( text ? *text->priority() : NumericExpression() ); NumericExpression textSizeExpr ( text ? *text->size() : NumericExpression() ); StringExpression iconUrlExpr ( icon ? *icon->url() : StringExpression() ); NumericExpression iconScaleExpr ( icon ? *icon->scale() : NumericExpression() ); NumericExpression iconHeadingExpr ( icon ? *icon->heading() : NumericExpression() ); for( FeatureList::const_iterator i = input.begin(); i != input.end(); ++i ) { Feature* feature = i->get(); if ( !feature ) continue; // run a symbol script if present. if ( text && text->script().isSet() ) { StringExpression temp( text->script().get() ); feature->eval( temp, &context ); } // run a symbol script if present. if ( icon && icon->script().isSet() ) { StringExpression temp( icon->script().get() ); feature->eval( temp, &context ); } const Geometry* geom = feature->getGeometry(); if ( !geom ) continue; Style tempStyle = styleCopy; // evaluate expressions into literals. // TODO: Later we could replace this with a generate "expression evaluator" type // that we could pass to PlaceNode in the DB options. -gw if ( text ) { if ( text->content().isSet() ) tempStyle.get<TextSymbol>()->content()->setLiteral( feature->eval( textContentExpr, &context ) ); if ( text->size().isSet() ) tempStyle.get<TextSymbol>()->size()->setLiteral( feature->eval(textSizeExpr, &context) ); } if ( icon ) { if ( icon->url().isSet() ) tempStyle.get<IconSymbol>()->url()->setLiteral( feature->eval(iconUrlExpr, &context) ); if ( icon->scale().isSet() ) tempStyle.get<IconSymbol>()->scale()->setLiteral( feature->eval(iconScaleExpr, &context) ); if ( icon->heading().isSet() ) tempStyle.get<IconSymbol>()->heading()->setLiteral( feature->eval(iconHeadingExpr, &context) ); } osg::Node* node = makePlaceNode( context, feature, tempStyle, textPriorityExpr); if ( node ) { if ( context.featureIndex() ) { context.featureIndex()->tagNode(node, feature); } group->addChild( node ); } } // Note to self: need to change this to support picking later. -gw //VirtualProgram* vp = VirtualProgram::getOrCreate(group->getOrCreateStateSet()); //vp->setInheritShaders( false ); return group; }
void AltitudeFilter::pushAndDontClamp( FeatureList& features, FilterContext& cx ) { NumericExpression scaleExpr; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleExpr = *_altitude->verticalScale(); NumericExpression offsetExpr; if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetExpr = *_altitude->verticalOffset(); bool gpuClamping = _altitude.valid() && _altitude->technique() == _altitude->TECHNIQUE_GPU; bool ignoreZ = gpuClamping && _altitude->clamping() == _altitude->CLAMP_TO_TERRAIN; for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); // run a symbol script if present. if ( _altitude.valid() && _altitude->script().isSet() ) { StringExpression temp( _altitude->script().get() ); feature->eval( temp, &cx ); } double minHAT = DBL_MAX; double maxHAT = -DBL_MAX; double scaleZ = 1.0; if ( _altitude.valid() && _altitude->verticalScale().isSet() ) scaleZ = feature->eval( scaleExpr, &cx ); optional<double> offsetZ( 0.0 ); if ( _altitude.valid() && _altitude->verticalOffset().isSet() ) offsetZ = feature->eval( offsetExpr, &cx ); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); for( Geometry::iterator g = geom->begin(); g != geom->end(); ++g ) { if ( ignoreZ ) { g->z() = 0.0; } if ( !gpuClamping ) { g->z() *= scaleZ; g->z() += offsetZ.get(); } if ( g->z() < minHAT ) minHAT = g->z(); if ( g->z() > maxHAT ) maxHAT = g->z(); } } if ( minHAT != DBL_MAX ) { feature->set( "__min_hat", minHAT ); feature->set( "__max_hat", maxHAT ); } // encode the Z offset if if ( gpuClamping ) { feature->set("__oe_verticalScale", scaleZ); feature->set("__oe_verticalOffset", offsetZ.get()); } } }
bool ExtrudeGeometryFilter::process( FeatureList& features, FilterContext& context ) { // seed our random number generators Random wallSkinPRNG( _wallSkinSymbol.valid()? *_wallSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); Random roofSkinPRNG( _roofSkinSymbol.valid()? *_roofSkinSymbol->randomSeed() : 0, Random::METHOD_FAST ); for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); walls->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Geometry> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); rooflines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // fire up the outline geometry if we have a line symbol. if ( _outlineSymbol != 0L ) { outlines = new osg::Geometry(); outlines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); baselines->setUseVertexBufferObjects( _useVertexBufferObjects.get() ); } // calculate the extrusion height: float height; if ( _heightCallback.valid() ) { height = _heightCallback->operator()(input, context); } else if ( _heightExpr.isSet() ) { height = input->eval( _heightExpr.mutable_value(), &context ); } else { height = *_extrusionSymbol->height(); } // calculate the height offset from the base: float offset = 0.0; if ( _heightOffsetExpr.isSet() ) { offset = input->eval( _heightOffsetExpr.mutable_value(), &context ); } osg::ref_ptr<osg::StateSet> wallStateSet; osg::ref_ptr<osg::StateSet> roofStateSet; // calculate the wall texturing: SkinResource* wallSkin = 0L; if ( _wallSkinSymbol.valid() ) { if ( _wallResLib.valid() ) { SkinSymbol querySymbol( *_wallSkinSymbol.get() ); querySymbol.objectHeight() = fabs(height) - offset; wallSkin = _wallResLib->getSkin( &querySymbol, wallSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // calculate the rooftop texture: SkinResource* roofSkin = 0L; if ( _roofSkinSymbol.valid() ) { if ( _roofResLib.valid() ) { SkinSymbol querySymbol( *_roofSkinSymbol.get() ); roofSkin = _roofResLib->getSkin( &querySymbol, roofSkinPRNG, context.getDBOptions() ); } else { //TODO: simple single texture? } } // Build the data model for the structure. Structure structure; buildStructure( part, height, offset, _extrusionSymbol->flatten().get(), wallSkin, roofSkin, structure, context); // Create the walls. if ( walls.valid() ) { osg::Vec4f wallColor(1,1,1,1), wallBaseColor(1,1,1,1); if ( _wallPolygonSymbol.valid() ) { wallColor = _wallPolygonSymbol->fill()->color(); } if ( _extrusionSymbol->wallGradientPercentage().isSet() ) { wallBaseColor = Color(wallColor).brightness( 1.0 - *_extrusionSymbol->wallGradientPercentage() ); } else { wallBaseColor = wallColor; } buildWallGeometry(structure, walls.get(), wallColor, wallBaseColor, wallSkin); if ( wallSkin ) { // Get a stateset for the individual wall stateset context.resourceCache()->getOrCreateStateSet( wallSkin, wallStateSet ); } } // tessellate and add the roofs if necessary: if ( rooflines.valid() ) { osg::Vec4f roofColor(1,1,1,1); if ( _roofPolygonSymbol.valid() ) { roofColor = _roofPolygonSymbol->fill()->color(); } buildRoofGeometry(structure, rooflines.get(), roofColor, roofSkin); if ( roofSkin ) { // Get a stateset for the individual roof skin context.resourceCache()->getOrCreateStateSet( roofSkin, roofStateSet ); } } if ( outlines.valid() ) { osg::Vec4f outlineColor(1,1,1,1); if ( _outlineSymbol.valid() ) { outlineColor = _outlineSymbol->stroke()->color(); } float minCreaseAngle = _outlineSymbol->creaseAngle().value(); buildOutlineGeometry(structure, outlines.get(), outlineColor, minCreaseAngle); } if ( baselines.valid() ) { //TODO. osgUtil::Tessellator tess; tess.setTessellationType( osgUtil::Tessellator::TESS_TYPE_GEOMETRY ); tess.setWindingType( osgUtil::Tessellator::TESS_WINDING_ODD ); tess.retessellatePolygons( *(baselines.get()) ); } // Set up for feature naming and feature indexing: std::string name; if ( !_featureNameExpr.empty() ) name = input->eval( _featureNameExpr, &context ); FeatureSourceIndex* index = context.featureIndex(); if ( walls.valid() ) { addDrawable( walls.get(), wallStateSet.get(), name, input, index ); } if ( rooflines.valid() ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } return true; }
//override bool renderFeaturesForStyle( Session* session, const Style& style, const FeatureList& features, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image ) { OE_DEBUG << LC << "Rendering " << features.size() << " features for " << imageExtent.toString() << "\n"; // A processing context to use with the filters: FilterContext context( session ); context.setProfile( getFeatureSource()->getFeatureProfile() ); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); const CoverageSymbol* masterCov = style.getSymbol<CoverageSymbol>(); // sort into bins, making a copy for lines that require buffering. FeatureList polygons; FeatureList lines; for(FeatureList::const_iterator f = features.begin(); f != features.end(); ++f) { if ( f->get()->getGeometry() ) { bool hasPoly = false; bool hasLine = false; if ( masterPoly || f->get()->style()->has<PolygonSymbol>() ) { polygons.push_back( f->get() ); hasPoly = true; } if ( masterLine || f->get()->style()->has<LineSymbol>() ) { Feature* newFeature = new Feature( *f->get() ); if ( !newFeature->getGeometry()->isLinear() ) { newFeature->setGeometry( newFeature->getGeometry()->cloneAs(Geometry::TYPE_RING) ); } lines.push_back( newFeature ); hasLine = true; } // if there are no geometry symbols but there is a coverage symbol, default to polygons. if ( !hasLine && !hasPoly ) { if ( masterCov || f->get()->style()->has<CoverageSymbol>() ) { polygons.push_back( f->get() ); } } } } // initialize: RenderFrame frame; frame.xmin = imageExtent.xMin(); frame.ymin = imageExtent.yMin(); frame.xf = (double)image->s() / imageExtent.width(); frame.yf = (double)image->t() / imageExtent.height(); if ( lines.size() > 0 ) { // We are buffering in the features native extent, so we need to use the // transformed extent to get the proper "resolution" for the image const SpatialReference* featureSRS = context.profile()->getSRS(); GeoExtent transformedExtent = imageExtent.transform(featureSRS); double trans_xf = (double)image->s() / transformedExtent.width(); double trans_yf = (double)image->t() / transformedExtent.height(); // resolution of the image (pixel extents): double xres = 1.0/trans_xf; double yres = 1.0/trans_yf; // downsample the line data so that it is no higher resolution than to image to which // we intend to rasterize it. If you don't do this, you run the risk of the buffer // operation taking forever on very high-res input data. if ( _options.optimizeLineSampling() == true ) { ResampleFilter resample; resample.minLength() = osg::minimum( xres, yres ); context = resample.push( lines, context ); } // now run the buffer operation on all lines: BufferFilter buffer; double lineWidth = 1.0; if ( masterLine ) { buffer.capStyle() = masterLine->stroke()->lineCap().value(); if ( masterLine->stroke()->width().isSet() ) { lineWidth = masterLine->stroke()->width().value(); GeoExtent imageExtentInFeatureSRS = imageExtent.transform(featureSRS); double pixelWidth = imageExtentInFeatureSRS.width() / (double)image->s(); // if the width units are specified, process them: if (masterLine->stroke()->widthUnits().isSet() && masterLine->stroke()->widthUnits().get() != Units::PIXELS) { const Units& featureUnits = featureSRS->getUnits(); const Units& strokeUnits = masterLine->stroke()->widthUnits().value(); // if the units are different than those of the feature data, we need to // do a units conversion. if ( featureUnits != strokeUnits ) { if ( Units::canConvert(strokeUnits, featureUnits) ) { // linear to linear, no problem lineWidth = strokeUnits.convertTo( featureUnits, lineWidth ); } else if ( strokeUnits.isLinear() && featureUnits.isAngular() ) { // linear to angular? approximate degrees per meter at the // latitude of the tile's centroid. double lineWidthM = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth); double mPerDegAtEquatorInv = 360.0/(featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI); double lon, lat; imageExtent.getCentroid(lon, lat); lineWidth = lineWidthM * mPerDegAtEquatorInv * cos(osg::DegreesToRadians(lat)); } } // enfore a minimum width of one pixel. float minPixels = masterLine->stroke()->minPixels().getOrUse( 1.0f ); lineWidth = osg::clampAbove(lineWidth, pixelWidth*minPixels); } else // pixels { lineWidth *= pixelWidth; } } } buffer.distance() = lineWidth * 0.5; // since the distance is for one side buffer.push( lines, context ); } // Transform the features into the map's SRS: TransformFilter xform( imageExtent.getSRS() ); xform.setLocalizeCoordinates( false ); FilterContext polysContext = xform.push( polygons, context ); FilterContext linesContext = xform.push( lines, context ); // set up the AGG renderer: agg::rendering_buffer rbuf( image->data(), image->s(), image->t(), image->s()*4 ); // Create the renderer and the rasterizer agg::rasterizer ras; // Setup the rasterizer if ( _options.coverage() == true ) ras.gamma(1.0); else ras.gamma(_options.gamma().get()); ras.filling_rule(agg::fill_even_odd); // construct an extent for cropping the geometry to our tile. // extend just outside the actual extents so we don't get edge artifacts: GeoExtent cropExtent = GeoExtent(imageExtent); cropExtent.scale(1.1, 1.1); osg::ref_ptr<Symbology::Polygon> cropPoly = new Symbology::Polygon( 4 ); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMin(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMax(), cropExtent.yMax(), 0 )); cropPoly->push_back( osg::Vec3d( cropExtent.xMin(), cropExtent.yMax(), 0 )); // If there's a coverage symbol, make a copy of the expressions so we can evaluate them optional<NumericExpression> covValue; const CoverageSymbol* covsym = style.get<CoverageSymbol>(); if (covsym && covsym->valueExpression().isSet()) covValue = covsym->valueExpression().get(); // render the polygons for(FeatureList::iterator i = polygons.begin(); i != polygons.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const PolygonSymbol* poly = feature->style().isSet() && feature->style()->has<PolygonSymbol>() ? feature->style()->get<PolygonSymbol>() : masterPoly; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = poly->fill()->color(); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } // render the lines for(FeatureList::iterator i = lines.begin(); i != lines.end(); i++) { Feature* feature = i->get(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr<Geometry> croppedGeometry; if ( geometry->crop( cropPoly.get(), croppedGeometry ) ) { const LineSymbol* line = feature->style().isSet() && feature->style()->has<LineSymbol>() ? feature->style()->get<LineSymbol>() : masterLine; if ( _options.coverage() == true && covValue.isSet() ) { float value = (float)feature->eval(covValue.mutable_value(), &context); rasterizeCoverage(croppedGeometry.get(), value, frame, ras, rbuf); } else { osg::Vec4f color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1); rasterize(croppedGeometry.get(), color, frame, ras, rbuf); } } } return true; }