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; }
osg::Node* PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx) { // compute the coordinate localization matrices. computeLocalizers( cx ); // establish some things bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { makeECEF = cx.getSession()->getMapInfo().isGeocentric(); featureSRS = cx.extent()->getSRS(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // The operator we'll use to make lines into polygons. const LineSymbol* line = _style.get<LineSymbol>(); PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() ); // Geode to hold all the geometries. osg::Geode* geode = new osg::Geode(); // iterate over all features. for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* f = i->get(); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( f->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::Vec3Array* verts = new osg::Vec3Array(); osg::Vec3Array* normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonize( verts, normals ); geode->addDrawable( geom ); // record the geometry's primitive set(s) in the index: if ( cx.featureIndex() ) cx.featureIndex()->tagPrimitiveSets( geom, f ); } } // attempt to combine geometries for better performance MeshConsolidator::run( *geode ); // GPU performance optimization: VertexCacheOptimizer vco; geode->accept( vco ); // If we're auto-scaling, we need a shader float minPixels = line ? line->stroke()->minPixels().getOrUse( 0.0f ) : 0.0f; if ( minPixels > 0.0f ) { osg::StateSet* stateSet = geode->getOrCreateStateSet(); VirtualProgram* vp = VirtualProgram::getOrCreate(stateSet); vp->setName( "osgEarth::PolygonizeLines" ); const char* vs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "attribute vec3 oe_polyline_center; \n" "uniform float oe_polyline_scale; \n" "uniform float oe_polyline_min_pixels; \n" "uniform mat3 oe_WindowScaleMatrix; \n" "void oe_polyline_scalelines(inout vec4 VertexMODEL) \n" "{ \n" " if ( oe_polyline_scale != 1.0 || oe_polyline_min_pixels > 0.0 ) \n" " { \n" " vec4 center_model = vec4(oe_polyline_center*VertexMODEL.w, VertexMODEL.w); \n" " vec4 vector_model = VertexMODEL - center_model; \n" " if ( length(vector_model.xyz) > 0.0 ) \n" " { \n" " float scale = oe_polyline_scale; \n" " vec4 vertex_clip = gl_ModelViewProjectionMatrix * VertexMODEL; \n" " vec4 center_clip = gl_ModelViewProjectionMatrix * center_model; \n" " vec4 vector_clip = vertex_clip - center_clip; \n" " if ( oe_polyline_min_pixels > 0.0 ) \n" " { \n" " vec3 vector_win = oe_WindowScaleMatrix * (vertex_clip.xyz/vertex_clip.w - center_clip.xyz/center_clip.w); \n" " float min_scale = max( (0.5*oe_polyline_min_pixels)/length(vector_win.xy), 1.0 ); \n" " scale = max( scale, min_scale ); \n" " } \n" " VertexMODEL = center_model + vector_model*scale; \n" " } \n" " } \n" "} \n"; vp->setFunction( "oe_polyline_scalelines", vs, ShaderComp::LOCATION_VERTEX_MODEL ); vp->addBindAttribLocation( "oe_polyline_center", osg::Drawable::ATTRIBUTE_6 ); // add the default scaling uniform. // good way to test: // osgearth_viewer earthfile --uniform oe_polyline_scale 1.0 10.0 osg::Uniform* scaleU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_scale"); scaleU->set( 1.0f ); stateSet->addUniform( scaleU, 1 ); // the default "min pixels" uniform. osg::Uniform* minPixelsU = new osg::Uniform(osg::Uniform::FLOAT, "oe_polyline_min_pixels"); minPixelsU->set( minPixels ); stateSet->addUniform( minPixelsU, 1 ); } return delocalize( geode ); }
FilterContext BuildGeometryFilter::push( FeatureList& input, const FilterContext& context ) { reset(); OE_DEBUG << LC << context.toString() << std::endl; bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) if ( !push( i->get(), context ) ) ok = false; // In a feature class with one point-per-feature, you end up with one geometry per point, // which results is (a) very bad performance and (b) geometries with a zero bbox that therefore // don't draw. This is not a total solution (won't work for a single point, isn't friendly for // doing feature-selection, etc.) but is a workable temporary fix. In the future we're going // to replace this filter anyway with something more highly optimized (a la osgGIS). // // however...seems that MERGE_GEOMETRY destroys almost everything except for points!! if ( _mergeGeometry == true ) { osgUtil::Optimizer optimizer; optimizer.optimize( _geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY ); } if ( ok ) { if ( !_style.empty() && _geode.valid() ) { // could optimize this to only happen is lines or points were created .. const LineSymbol* lineSymbol = _style.getSymbol<LineSymbol>(); float size = 1.0; if (lineSymbol) size = lineSymbol->stroke()->width().value(); _geode->getOrCreateStateSet()->setAttribute( new osg::Point(size), osg::StateAttribute::ON ); _geode->getOrCreateStateSet()->setAttribute( new osg::LineWidth(size), osg::StateAttribute::ON ); const PointSymbol* pointSymbol = _style.getSymbol<PointSymbol>(); if ( pointSymbol && pointSymbol->size().isSet() ) _geode->getOrCreateStateSet()->setAttribute( new osg::Point( *pointSymbol->size() ), osg::StateAttribute::ON ); } _result = _geode.release(); if ( context.hasReferenceFrame() ) { osg::MatrixTransform* delocalizer = new osg::MatrixTransform( context.inverseReferenceFrame() ); delocalizer->addChild( _result.get() ); _result = delocalizer; } } else { _result = 0L; } FilterContext outCx( context ); outCx.setReferenceFrame( osg::Matrixd::identity() ); // clear the ref frame. return outCx; }
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::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; 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 ) { if ( !gpuClamping ) { 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 ); } // encode the Z offset if if ( gpuClamping ) { feature->set("__oe_verticalScale", scaleZ); feature->set("__oe_verticalOffset", offsetZ); } } }
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; }
osg::Node* PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx) { // compute the coordinate localization matrices. computeLocalizers( cx ); // establish some things bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { makeECEF = cx.getSession()->getMapInfo().isGeocentric(); featureSRS = cx.extent()->getSRS(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // The operator we'll use to make lines into polygons. const LineSymbol* line = _style.get<LineSymbol>(); PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() ); // Geode to hold all the geometries. osg::Geode* geode = new PixelScalingGeode(); //osg::Geode(); // iterate over all features. for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* f = i->get(); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( f->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::Vec3Array* verts = new osg::Vec3Array(); osg::Vec3Array* normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonize( verts, normals ); // install. geode->addDrawable( geom ); // record the geometry's primitive set(s) in the index: if ( cx.featureIndex() ) cx.featureIndex()->tagDrawable( geom, f ); } } // attempt to combine geometries for better performance MeshConsolidator::run( *geode ); // GPU performance optimization: VertexCacheOptimizer vco; geode->accept( vco ); // If we're auto-scaling, we need a shader polygonize.installShaders( geode ); return delocalize( geode ); }
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; }
//override bool renderFeaturesForStyle( const Style& style, const FeatureList& features, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image ) { // A processing context to use with the filters: FilterContext context; context.setProfile( getFeatureSource()->getFeatureProfile() ); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); // 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() ) { if ( masterPoly || f->get()->style()->has<PolygonSymbol>() ) { polygons.push_back( f->get() ); } 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 ); } } } // 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. lineWidth = masterLine->stroke()->widthUnits()->convertTo(Units::METERS, lineWidth); double circ = featureSRS->getEllipsoid()->getRadiusEquator() * 2.0 * osg::PI; double x, y; context.profile()->getExtent().getCentroid(x, y); double radians = (lineWidth/circ) * cos(osg::DegreesToRadians(y)); lineWidth = osg::RadiansToDegrees(radians); } } // 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::renderer<agg::span_abgr32> ren(rbuf); agg::rasterizer ras; // Setup the rasterizer ras.gamma(1.3); 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 )); // 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; const osg::Vec4 color = poly ? static_cast<osg::Vec4>(poly->fill()->color()) : osg::Vec4(1,1,1,1); rasterize(croppedGeometry.get(), color, frame, ras, ren); } } // 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; const osg::Vec4 color = line ? static_cast<osg::Vec4>(line->stroke()->color()) : osg::Vec4(1,1,1,1); rasterize(croppedGeometry.get(), color, frame, ras, ren); } } return true; }
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; }
FeatureCursor* createFeatureCursor(const Symbology::Query& query, ProgressCallback* progress) { FeatureCursor* result = 0L; std::string url = createURL( query ); // the URL wil lbe empty if it was invalid or outside the level bounds of the layer. if (url.empty()) return 0L; OE_DEBUG << LC << url << std::endl; URI uri(url, _options.url()->context()); // read the data: ReadResult r = uri.readString(_readOptions.get(), progress); const std::string& buffer = r.getString(); const Config& meta = r.metadata(); bool dataOK = false; FeatureList features; if ( !buffer.empty() ) { // Get the mime-type from the metadata record if possible std::string mimeType = r.metadata().value( IOMetadata::CONTENT_TYPE ); //If the mimetype is empty then try to set it from the format specification if (mimeType.empty()) { if (_options.format().value() == "json") mimeType = "json"; else if (_options.format().value().compare("gml") == 0) mimeType = "text/xml"; else if (_options.format().value().compare("pbf") == 0) mimeType = "application/x-protobuf"; } dataOK = getFeatures( buffer, *query.tileKey(), mimeType, features ); } if ( dataOK ) { OE_DEBUG << LC << "Read " << features.size() << " features" << std::endl; } //If we have any filters, process them here before the cursor is created if (getFilters() && !getFilters()->empty() && !features.empty()) { FilterContext cx; cx.setProfile(getFeatureProfile()); cx.extent() = query.tileKey()->getExtent(); for (FeatureFilterChain::const_iterator i = getFilters()->begin(); i != getFilters()->end(); ++i) { FeatureFilter* filter = i->get(); cx = filter->push(features, cx); } } // If we have any features and we have an fid attribute, override the fid of the features if (_options.fidAttribute().isSet()) { for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr) { std::string attr = itr->get()->getString(_options.fidAttribute().get()); FeatureID fid = as<long>(attr, 0); itr->get()->setFID( fid ); } } result = new FeatureListCursor(features); return result; }
FilterContext push(FeatureList& input, FilterContext& context) { if (_featureSource.valid()) { // Get any features that intersect this query. FeatureList boundaries; getFeatures(context.extent().get(), boundaries ); // The list of output features FeatureList output; if (boundaries.empty()) { // No intersecting features. If contains is false, then just the output to the input. if (contains() == false) { output = input; } } else { // Transform the boundaries into the coordinate system of the features for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { itr->get()->transform( context.profile()->getSRS() ); } for(FeatureList::const_iterator f = input.begin(); f != input.end(); ++f) { Feature* feature = f->get(); if ( feature && feature->getGeometry() ) { osg::Vec2d c = feature->getGeometry()->getBounds().center2d(); if ( contains() == true ) { // coarsest: if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y()))) { for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry()); if (ring && ring->contains2D(c.x(), c.y())) { output.push_back( feature ); } } } } else { bool contained = false; // coarsest: if (_featureSource->getFeatureProfile()->getExtent().contains(GeoPoint(feature->getSRS(), c.x(), c.y()))) { for (FeatureList::iterator itr = boundaries.begin(); itr != boundaries.end(); ++itr) { Ring* ring = dynamic_cast< Ring*>(itr->get()->getGeometry()); if (ring && ring->contains2D(c.x(), c.y())) { contained = true; break; } } } if ( !contained ) { output.push_back( feature ); } } } } } OE_INFO << LC << "Allowed " << output.size() << " out of " << input.size() << " features\n"; input = output; } return context; }
//override bool renderFeaturesForStyle( const Style& style, const FeatureList& inFeatures, osg::Referenced* buildData, const GeoExtent& imageExtent, osg::Image* image ) { // local copy of the features that we can process FeatureList features = inFeatures; BuildData* bd = static_cast<BuildData*>( buildData ); // A processing context to use with the filters: FilterContext context; context.profile() = getFeatureSource()->getFeatureProfile(); const LineSymbol* masterLine = style.getSymbol<LineSymbol>(); const PolygonSymbol* masterPoly = style.getSymbol<PolygonSymbol>(); //bool embeddedStyles = getFeatureSource()->hasEmbeddedStyles(); // if only a line symbol exists, and there are polygons in the mix, draw them // as outlines (line rings). //OE_INFO << LC << "Line Symbol = " << (masterLine == 0L ? "null" : masterLine->getConfig().toString()) << std::endl; //OE_INFO << LC << "Poly SYmbol = " << (masterPoly == 0L ? "null" : masterPoly->getConfig().toString()) << std::endl; //bool convertPolysToRings = poly == 0L && line != 0L; //if ( convertPolysToRings ) // OE_INFO << LC << "No PolygonSymbol; will draw polygons to rings" << std::endl; // initialize: double xmin = imageExtent.xMin(); double ymin = imageExtent.yMin(); //double s = (double)image->s(); //double t = (double)image->t(); double xf = (double)image->s() / imageExtent.width(); double yf = (double)image->t() / imageExtent.height(); // strictly speaking we should iterate over the features and buffer each one that's a line, // rather then checking for the existence of a LineSymbol. FeatureList linesToBuffer; for(FeatureList::iterator i = features.begin(); i != features.end(); i++) { Feature* feature = i->get(); Geometry* geom = feature->getGeometry(); if ( geom ) { // check for an embedded style: const LineSymbol* line = feature->style().isSet() ? feature->style()->getSymbol<LineSymbol>() : masterLine; const PolygonSymbol* poly = feature->style().isSet() ? feature->style()->getSymbol<PolygonSymbol>() : masterPoly; // if we have polygons but only a LineSymbol, draw the poly as a line. if ( geom->getComponentType() == Geometry::TYPE_POLYGON ) { if ( !poly && line ) { Feature* outline = new Feature( *feature ); geom = geom->cloneAs( Geometry::TYPE_RING ); outline->setGeometry( geom ); *i = outline; feature = outline; } //TODO: fix to enable outlined polys. doesn't work, not sure why -gw //else if ( poly && line ) //{ // Feature* outline = new Feature(); // geom = geom->cloneAs( Geometry::TYPE_LINESTRING ); // outline->setGeometry( geom ); // features.push_back( outline ); //} } bool needsBuffering = geom->getComponentType() == Geometry::TYPE_LINESTRING || geom->getComponentType() == Geometry::TYPE_RING; if ( needsBuffering ) { linesToBuffer.push_back( feature ); } } } if ( linesToBuffer.size() > 0 ) { //We are buffering in the features native extent, so we need to use the transform extent to get the proper "resolution" for the image GeoExtent transformedExtent = imageExtent.transform(context.profile()->getSRS()); 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( linesToBuffer, context ); } // now run the buffer operation on all lines: BufferFilter buffer; float lineWidth = 0.5; if ( masterLine ) { buffer.capStyle() = masterLine->stroke()->lineCap().value(); if ( masterLine->stroke()->width().isSet() ) lineWidth = masterLine->stroke()->width().value(); } // "relative line size" means that the line width is expressed in (approx) pixels // rather than in map units if ( _options.relativeLineSize() == true ) buffer.distance() = xres * lineWidth; else buffer.distance() = lineWidth; buffer.push( linesToBuffer, context ); } // First, transform the features into the map's SRS: TransformFilter xform( imageExtent.getSRS() ); xform.setLocalizeCoordinates( false ); context = xform.push( features, 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::renderer<agg::span_abgr32> ren(rbuf); agg::rasterizer ras; // Setup the rasterizer ras.gamma(1.3); ras.filling_rule(agg::fill_even_odd); 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 )); double lineWidth = 1.0; if ( masterLine ) lineWidth = (double)masterLine->stroke()->width().value(); osg::Vec4 color = osg::Vec4(1, 1, 1, 1); if ( masterLine ) color = masterLine->stroke()->color(); // render the features for(FeatureList::iterator i = features.begin(); i != features.end(); i++) { Feature* feature = i->get(); //bool first = bd->_pass == 0 && i == features.begin(); Geometry* geometry = feature->getGeometry(); osg::ref_ptr< Geometry > croppedGeometry; if ( ! geometry->crop( cropPoly.get(), croppedGeometry ) ) continue; // set up a default color: osg::Vec4 c = color; unsigned int a = (unsigned int)(127+(c.a()*255)/2); // scale alpha up agg::rgba8 fgColor( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a ); GeometryIterator gi( croppedGeometry.get() ); while( gi.hasMore() ) { c = color; Geometry* g = gi.next(); const LineSymbol* line = feature->style().isSet() ? feature->style()->getSymbol<LineSymbol>() : masterLine; const PolygonSymbol* poly = feature->style().isSet() ? feature->style()->getSymbol<PolygonSymbol>() : masterPoly; if (g->getType() == Geometry::TYPE_RING || g->getType() == Geometry::TYPE_LINESTRING) { if ( line ) c = line->stroke()->color(); else if ( poly ) c = poly->fill()->color(); } else if ( g->getType() == Geometry::TYPE_POLYGON ) { if ( poly ) c = poly->fill()->color(); else if ( line ) c = line->stroke()->color(); } a = (unsigned int)(127+(c.a()*255)/2); // scale alpha up fgColor = agg::rgba8( (unsigned int)(c.r()*255), (unsigned int)(c.g()*255), (unsigned int)(c.b()*255), a ); ras.filling_rule( agg::fill_even_odd ); for( Geometry::iterator p = g->begin(); p != g->end(); p++ ) { const osg::Vec3d& p0 = *p; double x0 = xf*(p0.x()-xmin); double y0 = yf*(p0.y()-ymin); //const osg::Vec3d& p1 = p+1 != g->end()? *(p+1) : g->front(); //double x1 = xf*(p1.x()-xmin); //double y1 = yf*(p1.y()-ymin); if ( p == g->begin() ) ras.move_to_d( x0, y0 ); else ras.line_to_d( x0, y0 ); } } ras.render(ren, fgColor); ras.reset(); } bd->_pass++; return true; }
osg::Node* PolygonizeLinesFilter::push(FeatureList& input, FilterContext& cx) { // compute the coordinate localization matrices. computeLocalizers( cx ); // establish some things bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { makeECEF = cx.getSession()->getMapInfo().isGeocentric(); featureSRS = cx.extent()->getSRS(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // The operator we'll use to make lines into polygons. const LineSymbol* line = _style.get<LineSymbol>(); PolygonizeLinesOperator polygonize( line ? (*line->stroke()) : Stroke() ); // Geode to hold all the geometries. osg::Geode* geode = new osg::Geode(); // iterate over all features. for( FeatureList::iterator i = input.begin(); i != input.end(); ++i ) { Feature* f = i->get(); // iterate over all the feature's geometry parts. We will treat // them as lines strings. GeometryIterator parts( f->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; // transform the geometry into the target SRS and localize it about // a local reference point. osg::Vec3Array* verts = new osg::Vec3Array(); osg::Vec3Array* normals = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, verts, normals, mapSRS, _world2local, makeECEF ); // turn the lines into polygons. osg::Geometry* geom = polygonize( verts, normals ); geode->addDrawable( geom ); } } // attempt to combine geometries for better performance MeshConsolidator::run( *geode ); // GPU performance optimization: #if 0 // issue: ignores vertex attributes osgUtil::Optimizer optimizer; optimizer.optimize( result, osgUtil::Optimizer::VERTEX_PRETRANSFORM | osgUtil::Optimizer::VERTEX_POSTTRANSFORM ); #endif return delocalize( geode ); }
FilterContext CropFilter::push( FeatureList& input, FilterContext& context ) { if ( !context.extent().isSet() ) { OE_WARN << LC << "Extent is not set (and is required)" << std::endl; return context; } const GeoExtent& extent = *context.extent(); GeoExtent newExtent( extent.getSRS() ); if ( _method == METHOD_CENTROID ) { for( FeatureList::iterator i = input.begin(); i != input.end(); ) { bool keepFeature = false; Feature* feature = i->get(); Geometry* featureGeom = feature->getGeometry(); if ( featureGeom && featureGeom->isValid() ) { Bounds bounds = featureGeom->getBounds(); if ( bounds.isValid() ) { osg::Vec3d centroid = bounds.center(); if ( extent.contains( centroid.x(), centroid.y() ) ) { keepFeature = true; newExtent.expandToInclude( bounds.xMin(), bounds.yMin() ); } } } if ( keepFeature ) ++i; else i = input.erase( i ); } } else // METHOD_CROPPING (requires GEOS) { #ifdef OSGEARTH_HAVE_GEOS // create the intersection polygon: osg::ref_ptr<Symbology::Polygon> poly; for( FeatureList::iterator i = input.begin(); i != input.end(); ) { bool keepFeature = false; Feature* feature = i->get(); Symbology::Geometry* featureGeom = feature->getGeometry(); if ( featureGeom && featureGeom->isValid() ) { // test for trivial acceptance: const Bounds bounds = featureGeom->getBounds(); if ( !bounds.isValid() ) { //nop } else if ( extent.contains( bounds ) ) { keepFeature = true; newExtent.expandToInclude( bounds ); } // then move on to the cropping operation: else { if ( !poly.valid() ) { poly = new Symbology::Polygon(); poly->push_back( osg::Vec3d( extent.xMin(), extent.yMin(), 0 )); poly->push_back( osg::Vec3d( extent.xMax(), extent.yMin(), 0 )); poly->push_back( osg::Vec3d( extent.xMax(), extent.yMax(), 0 )); poly->push_back( osg::Vec3d( extent.xMin(), extent.yMax(), 0 )); } osg::ref_ptr<Geometry> croppedGeometry; if ( featureGeom->crop( poly.get(), croppedGeometry ) ) { if ( croppedGeometry->isValid() ) { feature->setGeometry( croppedGeometry.get() ); keepFeature = true; newExtent.expandToInclude( croppedGeometry->getBounds() ); } } } } if ( keepFeature ) ++i; else i = input.erase( i ); } #else // OSGEARTH_HAVE_GEOS OE_WARN << "CropFilter - METHOD_CROPPING not available - please compile osgEarth with GEOS" << std::endl; return context; #endif } FilterContext newContext = context; newContext.extent() = newExtent; return newContext; }
void FeatureNode::build() { // if there's a decoration, clear it out first. this->clearDecoration(); _attachPoint = 0L; // if there is existing geometry, kill it this->removeChildren( 0, this->getNumChildren() ); if ( !getMapNode() ) return; if ( _features.empty() ) return; const Style &style = getStyle(); // compilation options. GeometryCompilerOptions options = _options; // figure out what kind of altitude manipulation we need to perform. AnnotationUtils::AltitudePolicy ap; AnnotationUtils::getAltitudePolicy( style, ap ); // If we're doing auto-clamping on the CPU, shut off compiler map clamping // clamping since it would be redundant. // TODO: I think this is OBE now that we have "scene" clamping technique.. if ( ap.sceneClamping ) { options.ignoreAltitudeSymbol() = true; } osg::Node* node = _compiled.get(); if (_needsRebuild || !_compiled.valid() ) { // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates // of the geometry when performing localization or converting to geocentric. _extent = GeoExtent::INVALID; FeatureList clone; for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL); GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds()); if (_extent.isInvalid()) { _extent = featureExtent; } else { _extent.expandToInclude( featureExtent ); } clone.push_back( feature ); } // prep the compiler: GeometryCompiler compiler( options ); Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() ); FilterContext context( session, new FeatureProfile( _extent ), _extent ); _compiled = compiler.compile( clone, style, context ); node = _compiled.get(); _needsRebuild = false; // Compute the world bounds osg::BoundingSphered bounds; for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { osg::BoundingSphered bs; itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs); bounds.expandBy(bs); } // The polytope will ensure we only clamp to intersecting tiles: Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope); } if ( node ) { if ( AnnotationUtils::styleRequiresAlphaBlending( style ) && getStyle().get<ExtrusionSymbol>() ) { node = AnnotationUtils::installTwoPassAlpha( node ); } //OE_NOTICE << GeometryUtils::geometryToGeoJSON( _feature->getGeometry() ) << std::endl; _attachPoint = new osg::Group(); _attachPoint->addChild( node ); // Draped (projected) geometry if ( ap.draping ) { DrapeableNode* d = new DrapeableNode(); // getMapNode() ); d->addChild( _attachPoint ); this->addChild( d ); } // GPU-clamped geometry else if ( ap.gpuClamping ) { ClampableNode* clampable = new ClampableNode( getMapNode() ); clampable->addChild( _attachPoint ); this->addChild( clampable ); const RenderSymbol* render = style.get<RenderSymbol>(); if ( render && render->depthOffset().isSet() ) { clampable->setDepthOffsetOptions( *render->depthOffset() ); } } else { this->addChild( _attachPoint ); // CPU-clamped geometry? if ( ap.sceneClamping ) { // save for later when we need to reclamp the mesh on the CPU _altitude = style.get<AltitudeSymbol>(); // activate the terrain callback: setCPUAutoClamping( true ); // set default lighting based on whether we are extruding: setLightingIfNotSet( style.has<ExtrusionSymbol>() ); // do an initial clamp to get started. clampMesh( getMapNode()->getTerrain()->getGraph() ); } applyRenderSymbology( style ); } } updateClusterCulling(); }
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; }
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::Node* BuildGeometryFilter::push( FeatureList& input, FilterContext& context ) { osg::ref_ptr<osg::Group> result = new osg::Group(); computeLocalizers( context ); const LineSymbol* line = _style.get<LineSymbol>(); const PolygonSymbol* poly = _style.get<PolygonSymbol>(); const PointSymbol* point = _style.get<PointSymbol>(); // bin the feautres into polygons, lines, polygonized lines, and points. FeatureList polygons; FeatureList lines; FeatureList polygonizedLines; FeatureList points; for(FeatureList::iterator i = input.begin(); i != input.end(); ++i) { Feature* f = i->get(); // first consider the overall style: bool has_polysymbol = poly != 0L; bool has_linesymbol = line != 0L && line->stroke()->widthUnits() == Units::PIXELS; bool has_polylinesymbol = line != 0L && line->stroke()->widthUnits() != Units::PIXELS; bool has_pointsymbol = point != 0L; // if the featue has a style set, that overrides: if ( f->style().isSet() ) { has_polysymbol = has_polysymbol || (f->style()->has<PolygonSymbol>()); has_linesymbol = has_linesymbol || (f->style()->has<LineSymbol>() && f->style()->get<LineSymbol>()->stroke()->widthUnits() == Units::PIXELS); has_polylinesymbol = has_polylinesymbol || (f->style()->has<LineSymbol>() && f->style()->get<LineSymbol>()->stroke()->widthUnits() != Units::PIXELS); has_pointsymbol = has_pointsymbol || (f->style()->has<PointSymbol>()); } // if no style is set, use the geometry type: if ( !has_polysymbol && !has_linesymbol && !has_polylinesymbol && !has_pointsymbol && f->getGeometry() ) { switch( f->getGeometry()->getComponentType() ) { case Geometry::TYPE_LINESTRING: case Geometry::TYPE_RING: f->style()->add( new LineSymbol() ); has_linesymbol = true; break; case Geometry::TYPE_POINTSET: f->style()->add( new PointSymbol() ); has_pointsymbol = true; break; case Geometry::TYPE_POLYGON: f->style()->add( new PolygonSymbol() ); has_polysymbol = true; break; } } if ( has_polysymbol ) polygons.push_back( f ); if ( has_linesymbol ) lines.push_back( f ); if ( has_polylinesymbol ) polygonizedLines.push_back( f ); if ( has_pointsymbol ) points.push_back( f ); } // process them separately. if ( polygons.size() > 0 ) { OE_TEST << LC << "Building " << polygons.size() << " polygons." << std::endl; osg::ref_ptr<osg::Geode> geode = processPolygons(polygons, context); if ( geode->getNumDrawables() > 0 ) { osgUtil::Optimizer o; o.optimize( geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY | osgUtil::Optimizer::INDEX_MESH | osgUtil::Optimizer::VERTEX_PRETRANSFORM | osgUtil::Optimizer::VERTEX_POSTTRANSFORM ); result->addChild( geode.get() ); } } if ( polygonizedLines.size() > 0 ) { OE_TEST << LC << "Building " << polygonizedLines.size() << " polygonized lines." << std::endl; bool twosided = polygons.size() > 0 ? false : true; osg::ref_ptr<osg::Geode> geode = processPolygonizedLines(polygonizedLines, twosided, context); if ( geode->getNumDrawables() > 0 ) { osgUtil::Optimizer o; o.optimize( geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY | osgUtil::Optimizer::INDEX_MESH | osgUtil::Optimizer::VERTEX_PRETRANSFORM | osgUtil::Optimizer::VERTEX_POSTTRANSFORM ); result->addChild( geode.get() ); } } if ( lines.size() > 0 ) { OE_TEST << LC << "Building " << lines.size() << " lines." << std::endl; osg::ref_ptr<osg::Geode> geode = processLines(lines, context); if ( geode->getNumDrawables() > 0 ) { osgUtil::Optimizer o; o.optimize( geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY ); applyLineSymbology( geode->getOrCreateStateSet(), line ); result->addChild( geode.get() ); } } if ( points.size() > 0 ) { OE_TEST << LC << "Building " << points.size() << " points." << std::endl; osg::ref_ptr<osg::Geode> geode = processPoints(points, context); if ( geode->getNumDrawables() > 0 ) { osgUtil::Optimizer o; o.optimize( geode.get(), osgUtil::Optimizer::MERGE_GEOMETRY ); applyPointSymbology( geode->getOrCreateStateSet(), point ); result->addChild( geode.get() ); } } // indicate that geometry contains clamping attributes if (_style.has<AltitudeSymbol>() && _style.get<AltitudeSymbol>()->technique() == AltitudeSymbol::TECHNIQUE_GPU) { Clamping::installHasAttrsUniform( result->getOrCreateStateSet() ); } // Prepare buffer objects. AllocateAndMergeBufferObjectsVisitor allocAndMerge; result->accept( allocAndMerge ); if ( result->getNumChildren() > 0 ) { // apply the delocalization matrix for no-jitter return delocalize( result.release() ); } else { return 0L; } }
void FeatureNode::build() { if ( !_clampCallback.valid() ) _clampCallback = new ClampCallback(this); _attachPoint = 0L; // if there is existing geometry, kill it this->removeChildren( 0, this->getNumChildren() ); if ( !getMapNode() ) return; if ( _features.empty() ) return; const Style &style = getStyle(); // compilation options. GeometryCompilerOptions options = _options; // figure out what kind of altitude manipulation we need to perform. AnnotationUtils::AltitudePolicy ap; AnnotationUtils::getAltitudePolicy( style, ap ); // If we're doing auto-clamping on the CPU, shut off compiler map clamping // clamping since it would be redundant. if ( ap.sceneClamping ) { options.ignoreAltitudeSymbol() = true; } _clamperData.clear(); osg::Node* node = _compiled.get(); if (_needsRebuild || !_compiled.valid() ) { // Clone the Features before rendering as the GeometryCompiler and it's filters can change the coordinates // of the geometry when performing localization or converting to geocentric. _extent = GeoExtent::INVALID; FeatureList clone; for(FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new Feature( *itr->get(), osg::CopyOp::DEEP_COPY_ALL); GeoExtent featureExtent(feature->getSRS(), feature->getGeometry()->getBounds()); if (_extent.isInvalid()) { _extent = featureExtent; } else { _extent.expandToInclude( featureExtent ); } clone.push_back( feature ); } // prep the compiler: GeometryCompiler compiler( options ); Session* session = new Session( getMapNode()->getMap(), _styleSheet.get() ); FilterContext context( session, new FeatureProfile( _extent ), _extent, _index); _compiled = compiler.compile( clone, style, context ); node = _compiled.get(); _needsRebuild = false; // Compute the world bounds osg::BoundingSphered bounds; for( FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { osg::BoundingSphered bs; itr->get()->getWorldBound(getMapNode()->getMapSRS(), bs); bounds.expandBy(bs); } // The polytope will ensure we only clamp to intersecting tiles: Feature::getWorldBoundingPolytope(bounds, getMapNode()->getMapSRS(), _featurePolytope); } if ( node ) { if ( AnnotationUtils::styleRequiresAlphaBlending( style ) && getStyle().get<ExtrusionSymbol>() ) { node = AnnotationUtils::installTwoPassAlpha( node ); } _attachPoint = new osg::Group(); _attachPoint->addChild( node ); // Draped (projected) geometry if ( ap.draping ) { DrapeableNode* d = new DrapeableNode(); d->addChild( _attachPoint ); this->addChild( d ); } // GPU-clamped geometry else if ( ap.gpuClamping ) { ClampableNode* clampable = new ClampableNode(); clampable->addChild( _attachPoint ); this->addChild( clampable ); } else { this->addChild( _attachPoint ); // set default lighting based on whether we are extruding: setDefaultLighting( style.has<ExtrusionSymbol>() ); } applyRenderSymbology(style); if ( getMapNode()->getTerrain() ) { if ( ap.sceneClamping ) { // Need dynamic data variance since scene clamping will change the verts SetDataVarianceVisitor sdv(osg::Object::DYNAMIC); this->accept(sdv); getMapNode()->getTerrain()->addTerrainCallback(_clampCallback.get()); clamp(getMapNode()->getTerrain()->getGraph(), getMapNode()->getTerrain()); } else { getMapNode()->getTerrain()->removeTerrainCallback( _clampCallback.get() ); } } } }
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; }
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; }
FilterContext ScatterFilter::push(FeatureList& features, const FilterContext& context ) { if ( !isSupported() ) { OE_WARN << LC << "support for this filter is not enabled" << std::endl; return context; } // seed the random number generator so the randomness is the same each time // todo: control this seeding based on the feature source name, perhaps? ::srand( _randomSeed ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* f = i->get(); Geometry* geom = f->getGeometry(); if ( !geom ) continue; const SpatialReference* geomSRS = context.profile()->getSRS(); // first, undo the localization frame if there is one. context.toWorld( geom ); // convert to geodetic if necessary, and compute the approximate area in sq km if ( context.isGeocentric() ) { GeometryIterator gi( geom ); while( gi.hasMore() ) geomSRS->getGeographicSRS()->transformFromECEF( gi.next(), true ); geomSRS = geomSRS->getGeographicSRS(); } PointSet* points = new PointSet(); if ( geom->getComponentType() == Geometry::TYPE_POLYGON ) { polyScatter( geom, geomSRS, context, points ); } else if ( geom->getComponentType() == Geometry::TYPE_LINESTRING || geom->getComponentType() == Geometry::TYPE_RING ) { lineScatter( geom, geomSRS, context, points ); } else { OE_WARN << LC << "Sorry, don't know how to scatter a PointSet yet" << std::endl; } // convert back to geocentric if necessary. if ( context.isGeocentric() ) context.profile()->getSRS()->getGeographicSRS()->transformToECEF( points, true ); // re-apply the localization frame. context.toLocal( points ); // replace the source geometry with the scattered points. f->setGeometry( points ); } return context; }