osg::Group* createLabels( Map* map ) { osg::ref_ptr<osg::Group> labels = new osg::Group(); // first, open up the source shapefile OGRFeatureOptions fo; fo.url() = g_featureFile; osg::ref_ptr<FeatureSource> features = FeatureSourceFactory::create( fo ); if ( !features.valid() ) { OE_WARN << LC << "Unable to load features!" << std::endl; return 0L; } features->initialize( "" ); const FeatureProfile* featureProfile = features->getFeatureProfile(); if ( !featureProfile || !featureProfile->getSRS() ) { OE_WARN << LC << "Feature data has no spatial reference!" << std::endl; return 0L; } osg::ref_ptr<FeatureCursor> cursor = features->createFeatureCursor(); if ( !cursor.valid() ) { OE_WARN << LC << "Failed to query the feature source!" << std::endl; return 0L; } //SceneControlBin* priorityBin = canvas->getSceneControls(); unsigned count = 0; std::set<std::string> antiDupeSet; while( cursor->hasMore() ) { Feature* feature = cursor->nextFeature(); Geometry* geom = feature->getGeometry(); if ( !geom ) continue; // we will display the country name: std::string text = feature->getString( g_labelAttr ); if ( text.empty() ) continue; // and use the population to prioritize labels: float population = feature->getDouble(g_priorityAttr, 0.0); // remove duplicate labels: if ( g_removeDupes ) { if ( antiDupeSet.find(text) != antiDupeSet.end() ) continue; antiDupeSet.insert(text); } // calculate the world location of the label: osg::Vec3d centerPoint = geom->getBounds().center(); osg::Vec3d mapPoint; if ( !map->toMapPoint( centerPoint, featureProfile->getSRS(), mapPoint ) ) continue; osg::Vec3d worldPoint; if ( !map->mapPointToGeocentricPoint( mapPoint, worldPoint ) ) continue; // create the label and place it: osg::MatrixTransform* xform = new osg::MatrixTransform( osg::Matrix::translate(worldPoint) ); xform->setCullCallback( new CullNodeByNormal(worldPoint) ); xform->addChild( new ControlNode(new LabelControl(text)) ); labels->addChild( xform ); ++count; //OE_NOTICE << LC << "Added: " << text << std::endl; } OE_NOTICE << LC << "Found " << count << " features. " << std::endl; return labels.release(); }
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::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; }
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; }
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(); // run a symbol script if present. if ( _extrusionSymbol->script().isSet() ) { StringExpression temp( _extrusionSymbol->script().get() ); input->eval( temp, &context ); } // iterator over the parts. GeometryIterator iter( input->getGeometry(), false ); while( iter.hasMore() ) { Geometry* part = iter.next(); osg::ref_ptr<osg::Geometry> walls = new osg::Geometry(); osg::ref_ptr<osg::Geometry> rooflines = 0L; osg::ref_ptr<osg::Geometry> baselines = 0L; osg::ref_ptr<osg::Drawable> outlines = 0L; if ( part->getType() == Geometry::TYPE_POLYGON ) { rooflines = new osg::Geometry(); // prep the shapes by making sure all polys are open: static_cast<Polygon*>(part)->open(); } // make a base cap if we're doing stencil volumes. if ( _makeStencilVolume ) { baselines = new osg::Geometry(); } // 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(); } 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); 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? } } float verticalOffset = (float)input->getDouble("__oe_verticalOffset", 0.0); // Build the data model for the structure. Structure structure; buildStructure( part, height, _extrusionSymbol->flatten().get(), verticalOffset, 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, context.getDBOptions()); } } // 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, context.getDBOptions()); } } if (_outlineSymbol.valid()) { outlines = buildOutlineGeometry(structure); } 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 ); FeatureIndexBuilder* index = context.featureIndex(); if ( walls.valid() && walls->getVertexArray() && walls->getVertexArray()->getNumElements() > 0 ) { addDrawable( walls.get(), wallStateSet.get(), name, input, index ); } if ( rooflines.valid() && rooflines->getVertexArray() && rooflines->getVertexArray()->getNumElements() > 0 ) { addDrawable( rooflines.get(), roofStateSet.get(), name, input, index ); } if ( baselines.valid() && baselines->getVertexArray() && baselines->getVertexArray()->getNumElements() > 0 ) { addDrawable( baselines.get(), 0L, name, input, index ); } if ( outlines.valid() ) { addDrawable( outlines.get(), 0L, name, input, index ); } } } return true; }