v8::Handle<v8::Value> JSFilterContext::ToWorldCallback(const v8::Arguments& args) { FilterContext* context = V8Util::UnwrapObject<FilterContext>(args.Holder()); if (context && args.Length() == 1 && args[0]->IsObject()) { v8::Local<v8::Object> obj( v8::Object::Cast(*args[0]) ); /*if (V8Util::CheckObjectType(obj, JSGeometry::GetObjectType())) // Geometry { osgEarth::Symbology::Geometry* geometry = V8Util::UnwrapObject<osgEarth::Symbology::Geometry>(obj); return }*/ if (V8Util::CheckObjectType(obj, JSVec3d::GetObjectType())) // Vec3d { osg::Vec3d* vec = V8Util::UnwrapObject<osg::Vec3d>(obj); osg::Vec3d* world = new osg::Vec3d(context->toWorld(*vec)); return JSVec3d::WrapVec3d(world, true); } } return v8::Undefined(); }
FeatureCursor* createFeatureCursor( const Symbology::Query& query ) { FeatureCursor* result = 0L; std::string url = createURL( query ); // check the blacklist: if ( Registry::instance()->isBlacklisted(url) ) return 0L; OE_DEBUG << LC << url << std::endl; URI uri(url); // read the data: ReadResult r = uri.readString( _dbOptions.get() ); 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 const std::string& mimeType = r.metadata().value( IOMetadata::CONTENT_TYPE ); dataOK = getFeatures( buffer, 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 (!_options.filters().empty()) { // preprocess the features using the filter list: if ( features.size() > 0 ) { FilterContext cx; cx.setProfile( getFeatureProfile() ); for( FeatureFilterList::const_iterator i = _options.filters().begin(); i != _options.filters().end(); ++i ) { FeatureFilter* filter = i->get(); cx = filter->push( features, cx ); } } } //result = new FeatureListCursor(features); result = dataOK ? new FeatureListCursor( features ) : 0L; if ( !result ) Registry::instance()->blacklist( url ); return result; }
// reads a chunk of features into a memory cache; do this for performance // and to avoid needing the OGR Mutex every time void FeatureCursorOGR::readChunk() { if ( !_resultSetHandle ) return; OGR_SCOPED_LOCK; while( _queue.size() < _chunkSize && !_resultSetEndReached ) { FeatureList filterList; while( filterList.size() < _chunkSize && !_resultSetEndReached ) { OGRFeatureH handle = OGR_L_GetNextFeature( _resultSetHandle ); if ( handle ) { osg::ref_ptr<Feature> feature = OgrUtils::createFeature( handle, _profile.get() ); if (feature.valid() && !_source->isBlacklisted( feature->getFID() ) && validateGeometry( feature->getGeometry() )) { filterList.push_back( feature.release() ); } OGR_F_Destroy( handle ); } else { _resultSetEndReached = true; } } // preprocess the features using the filter list: if ( !_filters.empty() ) { FilterContext cx; cx.setProfile( _profile.get() ); if (_query.bounds().isSet()) { cx.extent() = GeoExtent(_profile->getSRS(), _query.bounds().get()); } else { cx.extent() = _profile->getExtent(); } for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i ) { FeatureFilter* filter = i->get(); cx = filter->push( filterList, cx ); } } for(FeatureList::const_iterator i = filterList.begin(); i != filterList.end(); ++i) { _queue.push( i->get() ); } } }
void FeatureCursorCDBV::Read_Data_Directly(void) { FeatureList preProcessList; OGR_SCOPED_LOCK; OGR_L_ResetReading(_layerHandle); OGRFeatureH feat_handle; OGRLayer * thislayer = (OGRLayer *)_layerHandle; int totalCount = thislayer->GetFeatureCount(); int fcount = -1; while ((feat_handle = OGR_L_GetNextFeature(_layerHandle)) != NULL) { ++fcount; if (feat_handle) { osg::ref_ptr<Feature> f = OgrUtils::createFeature(feat_handle, _profile.get()); if (f.valid() && !_source->isBlacklisted(f->getFID())) { if (isGeometryValid(f->getGeometry())) { _queue.push(f); if (_filters.size() > 0) { preProcessList.push_back(f.release()); } } else { OE_DEBUG << LC << "Skipping feature with invalid geometry: " << f->getGeoJSON() << std::endl; } } OGR_F_Destroy(feat_handle); } } // preprocess the features using the filter list: if (preProcessList.size() > 0) { FilterContext cx; cx.setProfile(_profile.get()); for (FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { FeatureFilter* filter = i->get(); cx = filter->push(preProcessList, cx); } } }
FilterContext ClampFilter::push( FeatureList& features, const FilterContext& cx ) { const Session* session = cx.getSession(); if ( !session ) { OE_WARN << LC << "No session - session is required for elevation clamping" << std::endl; return cx; } // the map against which we'll be doing elevation clamping MapFrame mapf = session->createMapFrame( Map::ELEVATION_LAYERS ); const SpatialReference* mapSRS = mapf.getProfile()->getSRS(); const SpatialReference* featureSRS = cx.profile()->getSRS(); bool isGeocentric = session->getMapInfo().isGeocentric(); // establish an elevation query interface based on the features' SRS. ElevationQuery eq( mapf ); for( FeatureList::iterator i = features.begin(); i != features.end(); ++i ) { Feature* feature = i->get(); GeometryIterator gi( feature->getGeometry() ); while( gi.hasMore() ) { Geometry* geom = gi.next(); if ( isGeocentric ) { // convert to map coords: cx.toWorld( geom ); mapSRS->transformFromECEF( geom ); // populate the elevations: eq.getElevations( geom, mapSRS ); // convert back to geocentric: mapSRS->transformToECEF( geom ); cx.toLocal( geom ); } else { // clamps the entire array to the highest available resolution. eq.getElevations( geom, featureSRS ); } } } return cx; }
bool BuildGeometryFilter::pushTextAnnotation( TextAnnotation* anno, const FilterContext& context ) { // find the centroid osg::Vec3d centroid = anno->getGeometry()->getBounds().center(); osgText::Text* t = new osgText::Text(); t->setText( anno->text() ); t->setFont( "fonts/arial.ttf" ); t->setAutoRotateToScreen( true ); t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS ); t->setCharacterSize( 32.0f ); //t->setCharacterSizeMode( osgText::TextBase::OBJECT_COORDS_WITH_MAXIMUM_SCREEN_SIZE_CAPPED_BY_FONT_HEIGHT ); //t->setCharacterSize( 300000.0f ); t->setPosition( centroid ); t->setAlignment( osgText::TextBase::CENTER_CENTER ); t->getOrCreateStateSet()->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS), osg::StateAttribute::ON ); t->getOrCreateStateSet()->setRenderBinDetails( 99999, "RenderBin" ); // apply styling as appropriate: osg::Vec4f textColor(1,1,1,1); osg::Vec4f haloColor(0,0,0,1); const TextSymbol* textSymbolizer = getStyle().getSymbol<TextSymbol>(); if ( textSymbolizer ) { textColor = textSymbolizer->fill()->color(); if ( textSymbolizer->halo().isSet() ) { haloColor = textSymbolizer->halo()->color(); } } t->setColor( textColor ); t->setBackdropColor( haloColor ); t->setBackdropType( osgText::Text::OUTLINE ); if ( context.isGeocentric() ) { // install a cluster culler: note that the CCC control point and normal must be // in world coordinates const osg::EllipsoidModel* ellip = context.profile()->getSRS()->getEllipsoid(); osg::Vec3d cp = centroid * context.inverseReferenceFrame(); osg::Vec3d normal = ellip->computeLocalUpVector( cp.x(), cp.y(), cp.z() ); osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback( cp, normal, 0.0f ); t->setCullCallback( ccc ); } _geode->addDrawable( t ); return true; }
void TessellateOperator::operator()( Feature* feature, FilterContext& context ) const { if (_numPartitions <= 1 || !feature || !feature->getGeometry() || feature->getGeometry()->getComponentType() == Geometry::TYPE_POINTSET ) { return; } bool isGeo = context.profile() ? context.profile()->getSRS()->isGeographic() : true; GeoInterpolation interp = feature->geoInterp().isSet() ? *feature->geoInterp() : _defaultInterp; GeometryIterator i( feature->getGeometry(), true ); while( i.hasMore() ) { Geometry* g = i.next(); bool isRing = dynamic_cast<Ring*>( g ) != 0L; Vec3dVector newVerts; newVerts.reserve( g->size() * _numPartitions ); for( Geometry::const_iterator v = g->begin(); v != g->end(); ++v ) { const osg::Vec3d& p0 = *v; if ( v != g->end()-1 ) // not last vert { if ( isGeo ) tessellateGeo( *v, *(v+1), _numPartitions, interp, newVerts ); else tessellateLinear( *v, *(v+1), _numPartitions, newVerts ); } else if ( isRing ) { if ( isGeo ) tessellateGeo( *v, *g->begin(), _numPartitions, interp, newVerts ); else tessellateLinear( *v, *g->begin(), _numPartitions, newVerts ); } else { // get the final vert. newVerts.push_back( *v ); } } g->swap( newVerts ); } }
FilterContext TransformFilter::push( FeatureList& input, FilterContext& incx ) { _bbox = osg::BoundingBoxd(); // first transform all the points into the output SRS, collecting a bounding box as we go: bool ok = true; for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) if ( !push( i->get(), incx ) ) ok = false; FilterContext outcx( incx ); outcx.isGeocentric() = _makeGeocentric; if ( _outputSRS.valid() ) { if ( incx.extent()->isValid() ) outcx.profile() = new FeatureProfile( incx.extent()->transform( _outputSRS.get() ) ); else outcx.profile() = new FeatureProfile( incx.profile()->getExtent().transform( _outputSRS.get() ) ); } // set the reference frame to shift data to the centroid. This will // prevent floating point precision errors in the openGL pipeline for // properly gridded data. if ( _bbox.valid() && _localize ) { // create a suitable reference frame: osg::Matrixd localizer; if ( _makeGeocentric ) { localizer = createGeocentricInvRefFrame( _bbox.center(), _outputSRS.get() ); localizer.invert( localizer ); } else { localizer = osg::Matrixd::translate( -_bbox.center() ); } // localize the geometry relative to the reference frame. for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) { localizeGeometry( i->get(), localizer ); } outcx.setReferenceFrame( localizer ); } return outcx; }
void FeatureSource::applyFilters(FeatureList& features, const GeoExtent& extent) const { // apply filters before returning. if ( !getFilters().empty() ) { FilterContext cx; cx.setProfile( getFeatureProfile() ); cx.extent() = extent; for(FeatureFilterList::const_iterator filter = getFilters().begin(); filter != getFilters().end(); ++filter) { cx = filter->get()->push( features, cx ); } } }
void FeatureSource::applyFilters(FeatureList& features, const GeoExtent& extent) const { // apply filters before returning. if (_filters.valid() && _filters->empty() == false) { FilterContext cx; cx.setProfile( getFeatureProfile() ); cx.extent() = extent; for(FeatureFilterChain::const_iterator filter = _filters->begin(); filter != _filters->end(); ++filter) { cx = filter->get()->push( features, cx ); } } }
BufferSrcFilterContext::BufferSrcFilterContext(const FilterContext &baseContext) { if (baseContext && isFilterValid(baseContext.getFilter())) { makeRef(baseContext); } }
FilterContext AltitudeFilter::push( FeatureList& features, FilterContext& cx ) { bool clamp = _altitude.valid() && _altitude->clamping() != AltitudeSymbol::CLAMP_NONE && cx.getSession() != 0L && cx.profile() != 0L; if ( clamp ) pushAndClamp( features, cx ); else pushAndDontClamp( features, cx ); return cx; }
osg::Vec3dArray* createBoundary( const SpatialReference* srs, ProgressCallback* progress ) { if ( _failed ) return 0L; if ( _features.valid() ) { if ( _features->getFeatureProfile() ) { osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor(); if ( cursor ) { if ( cursor->hasMore() ) { Feature* f = cursor->nextFeature(); if ( f && f->getGeometry() ) { // Init a filter to tranform feature in desired SRS if (!srs->isEquivalentTo(_features->getFeatureProfile()->getSRS())) { FilterContext cx; cx.profile() = new FeatureProfile(_features->getFeatureProfile()->getExtent()); //cx.isGeocentric() = _features->getFeatureProfile()->getSRS()->isGeographic(); TransformFilter xform( srs ); FeatureList featureList; featureList.push_back(f); cx = xform.push(featureList, cx); } return f->getGeometry()->toVec3dArray(); } } } } else { OE_WARN << LC << "Failed to create boundary; feature source has no SRS" << std::endl; _failed = true; } } else { OE_WARN << LC << "Unable to create boundary; invalid feature source" << std::endl; _failed = true; } return 0L; }
bool TransformFilter::push( Feature* input, FilterContext& context ) { if ( !input || !input->getGeometry() ) return true; bool needsSRSXform = _outputSRS.valid() && ( ! context.profile()->getSRS()->isEquivalentTo( _outputSRS.get() ) ); bool needsMatrixXform = !_mat.isIdentity(); // optimize: do nothing if nothing needs doing if ( !needsSRSXform && !_makeGeocentric && !_localize && !needsMatrixXform ) return true; // iterate over the feature geometry. GeometryIterator iter( input->getGeometry() ); while( iter.hasMore() ) { Geometry* geom = iter.next(); // pre-transform the point before doing an SRS transformation. if ( needsMatrixXform ) { for( unsigned i=0; i < geom->size(); ++i ) (*geom)[i] = (*geom)[i] * _mat; } // first transform the geometry to the output SRS: if ( needsSRSXform ) context.profile()->getSRS()->transformPoints( _outputSRS.get(), geom->asVector(), false ); // convert to ECEF if required: if ( _makeGeocentric ) _outputSRS->transformToECEF( geom->asVector(), false ); // update the bounding box. if ( _localize ) { for( unsigned i=0; i<geom->size(); ++i ) _bbox.expandBy( (*geom)[i] ); } } return true; }
Feature* GeometryFeatureCursor::nextFeature() { if ( hasMore() ) { _lastFeature = new Feature( _geom.get(), _featureProfile.valid() ? _featureProfile->getSRS() : 0L ); FilterContext cx; cx.profile() = _featureProfile.get(); FeatureList list; list.push_back( _lastFeature.get() ); for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i ) { cx = i->get()->push( list, cx ); } _geom = 0L; } return _lastFeature.get(); }
void BufferSinkFilterContext::assign(const FilterContext &ctx, OptionalErrorCode ec) { clear_if(ec); m_type = checkFilter(ctx.filter()); if (m_type == FilterMediaType::Unknown) { throws_if(ec, Errors::IncorrectBufferSinkFilter); return; } m_sink = ctx; }
v8::Handle<v8::Value> JSFilterContext::FromMapCallback(const v8::Arguments& args) { FilterContext* context = V8Util::UnwrapObject<FilterContext>(args.Holder()); if (context && args.Length() == 1 && args[0]->IsObject()) { v8::Local<v8::Object> obj( v8::Object::Cast(*args[0]) ); if (V8Util::CheckObjectType(obj, JSVec3d::GetObjectType())) // Vec3d { osg::Vec3d* map = V8Util::UnwrapObject<osg::Vec3d>(obj); osg::Vec3d* local = new osg::Vec3d(context->fromMap(*map)); return JSVec3d::WrapVec3d(local, true); } } return v8::Undefined(); }
v8::Handle<v8::Value> JSFilterContext::PropertyCallback(v8::Local<v8::String> name, const v8::AccessorInfo& info) { FilterContext* context = V8Util::UnwrapObject<FilterContext>(info.Holder()); v8::String::Utf8Value utf8_value(name); std::string prop(*utf8_value); if (!context || prop.empty()) return v8::Handle<v8::Value>(); if (prop == "session") return JSSession::WrapSession(const_cast<Session*>(context->getSession())); if (prop == "profile") return JSFeatureProfile::WrapFeatureProfile(const_cast<FeatureProfile*>(context->profile().get())); if (prop == "extent" && context->extent().isSet()) return JSGeoExtent::WrapGeoExtent(const_cast<osgEarth::GeoExtent*>(&context->extent().get())); //if (prop == "geocentric") // return v8::Boolean::New(context->isGeocentric()); return v8::Handle<v8::Value>(); }
void FilterContext::link(unsigned srcPad, FilterContext &dstFilter, unsigned dstPad, error_code &ec) { clear_if(ec); if (!m_raw || !dstFilter) { throws_if(ec, Errors::Unallocated); return; } int sts = avfilter_link(m_raw, srcPad, dstFilter.raw(), dstPad); if (sts < 0) { throws_if(ec, sts, ffmpeg_category()); return; } }
void FeaturesToNodeFilter::computeLocalizers( const FilterContext& context, const osgEarth::GeoExtent &extent, osg::Matrixd &out_w2l, osg::Matrixd &out_l2w ) { if ( context.isGeoreferenced() ) { if ( context.getSession()->getMapInfo().isGeocentric() ) { const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS(); GeoExtent geodExtent = extent.transform( geogSRS ); if ( geodExtent.width() < 180.0 ) { osg::Vec3d centroid, centroidECEF; geodExtent.getCentroid( centroid.x(), centroid.y() ); geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF ); geogSRS->getECEF()->createLocalToWorld( centroidECEF, out_l2w ); out_w2l.invert( out_l2w ); } } else // projected { if ( extent.isValid() ) { osg::Vec3d centroid; extent.getCentroid(centroid.x(), centroid.y()); extent.getSRS()->transform( centroid, context.getSession()->getMapInfo().getProfile()->getSRS(), centroid ); out_w2l.makeTranslate( -centroid ); out_l2w.invert( out_w2l ); } } } }
FilterContext ScatterFilter::push(FeatureList& features, 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 _prng = Random( _randomSeed, Random::METHOD_FAST ); 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(); osg::ref_ptr< PointSet > points = new PointSet(); if ( geom->getComponentType() == Geometry::TYPE_POLYGON ) { polyScatter( geom, geomSRS, context, points.get() ); } else if ( geom->getComponentType() == Geometry::TYPE_LINESTRING || geom->getComponentType() == Geometry::TYPE_RING ) { lineScatter( geom, geomSRS, context, points.get() ); } else { points = static_cast< PointSet*>(geom->cloneAs(Geometry::TYPE_POINTSET)); } // replace the source geometry with the scattered points. f->setGeometry( points.get() ); } return context; }
bool SubstituteModelFilter::findResource(const URI& uri, const InstanceSymbol* symbol, FilterContext& context, std::set<URI>& missing, osg::ref_ptr<InstanceResource>& output ) { // be careful about refptrs here since _instanceCache is an LRU. InstanceCache::Record rec; if ( _instanceCache.get(uri, rec) ) { // found it in the cache: output = rec.value().get(); } else if ( _resourceLib.valid() ) { // look it up in the resource library: output = _resourceLib->getInstance( uri.base(), context.getDBOptions() ); } else { // create it on the fly: output = symbol->createResource(); output->uri() = uri; _instanceCache.insert( uri, output.get() ); } // failed to find the instance. if ( !output.valid() ) { if ( missing.find(uri) == missing.end() ) { missing.insert(uri); OE_WARN << LC << "Failed to locate resource: " << uri.full() << std::endl; } } return output.valid(); }
void FeaturesToNodeFilter::computeLocalizers( const FilterContext& context ) { if ( context.isGeoreferenced() ) { if ( context.getSession()->getMapInfo().isGeocentric() ) { const SpatialReference* geogSRS = context.profile()->getSRS()->getGeographicSRS(); GeoExtent geodExtent = context.extent()->transform( geogSRS ); if ( geodExtent.width() < 180.0 ) { osg::Vec3d centroid, centroidECEF; geodExtent.getCentroid( centroid.x(), centroid.y() ); geogSRS->transform( centroid, geogSRS->getECEF(), centroidECEF ); geogSRS->getECEF()->createLocalToWorld( centroidECEF, _local2world ); _world2local.invert( _local2world ); } } else // projected { if ( context.extent().isSet() ) { osg::Vec3d centroid; context.extent()->getCentroid(centroid.x(), centroid.y()); context.extent()->getSRS()->transform( centroid, context.getSession()->getMapInfo().getProfile()->getSRS(), centroid ); _world2local.makeTranslate( -centroid ); _local2world.invert( _world2local ); } } } }
void ExtrudeGeometryFilter::reset( const FilterContext& context ) { _cosWallAngleThresh = cos( _wallAngleThresh_deg ); _geodes.clear(); if ( _styleDirty ) { const StyleSheet* sheet = context.getSession() ? context.getSession()->styles() : 0L; _wallSkinSymbol = 0L; _wallPolygonSymbol = 0L; _roofSkinSymbol = 0L; _roofPolygonSymbol = 0L; _extrusionSymbol = 0L; _outlineSymbol = 0L; _extrusionSymbol = _style.get<ExtrusionSymbol>(); if ( _extrusionSymbol.valid() ) { // make a copy of the height expression so we can use it: if ( _extrusionSymbol->heightExpression().isSet() ) { _heightExpr = *_extrusionSymbol->heightExpression(); } // If there is no height expression, and we have either absolute or terrain-relative // clamping, THAT means that we want to extrude DOWN from the geometry to the ground // (instead of from the geometry.) AltitudeSymbol* alt = _style.get<AltitudeSymbol>(); if ( alt && !_extrusionSymbol->heightExpression().isSet() && !_extrusionSymbol->height().isSet() ) { if (alt->clamping() == AltitudeSymbol::CLAMP_ABSOLUTE || alt->clamping() == AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN ) { _heightExpr = NumericExpression( "0-[__max_hat]" ); } } // attempt to extract the wall symbols: if ( _extrusionSymbol->wallStyleName().isSet() && sheet != 0L ) { const Style* wallStyle = sheet->getStyle( *_extrusionSymbol->wallStyleName(), false ); if ( wallStyle ) { _wallSkinSymbol = wallStyle->get<SkinSymbol>(); _wallPolygonSymbol = wallStyle->get<PolygonSymbol>(); } } // attempt to extract the rooftop symbols: if ( _extrusionSymbol->roofStyleName().isSet() && sheet != 0L ) { const Style* roofStyle = sheet->getStyle( *_extrusionSymbol->roofStyleName(), false ); if ( roofStyle ) { _roofSkinSymbol = roofStyle->get<SkinSymbol>(); _roofPolygonSymbol = roofStyle->get<PolygonSymbol>(); } } // if there's a line symbol, use it to outline the extruded data. _outlineSymbol = _style.get<LineSymbol>(); } // backup plan for skin symbols: const SkinSymbol* skin = _style.get<SkinSymbol>(); if ( skin ) { if ( !_wallSkinSymbol.valid() ) _wallSkinSymbol = skin; if ( !_roofSkinSymbol.valid() ) _roofSkinSymbol = skin; } // backup plan for poly symbols: const PolygonSymbol* poly = _style.get<PolygonSymbol>(); if ( poly ) { if ( !_wallPolygonSymbol.valid() ) _wallPolygonSymbol = poly; if ( !_roofPolygonSymbol.valid() ) _roofPolygonSymbol = poly; } _styleDirty = false; } }
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; }
bool ExtrudeGeometryFilter::buildStructure(const Geometry* input, double height, double heightOffset, bool flatten, const SkinResource* wallSkin, const SkinResource* roofSkin, Structure& structure, FilterContext& cx ) { bool makeECEF = false; const SpatialReference* srs = 0L; const SpatialReference* mapSRS = 0L; if ( cx.isGeoreferenced() ) { srs = cx.extent()->getSRS(); makeECEF = cx.getSession()->getMapInfo().isGeocentric(); mapSRS = cx.getSession()->getMapInfo().getProfile()->getSRS(); } // whether this is a closed polygon structure. structure.isPolygon = (input->getComponentType() == Geometry::TYPE_POLYGON); // extrusion working variables double targetLen = -DBL_MAX; osg::Vec3d minLoc(DBL_MAX, DBL_MAX, DBL_MAX); double minLoc_len = DBL_MAX; osg::Vec3d maxLoc(0,0,0); double maxLoc_len = 0; // Initial pass over the geometry does two things: // 1: Calculate the minimum Z across all parts. // 2: Establish a "target length" for extrusion double absHeight = fabs(height); ConstGeometryIterator zfinder( input ); while( zfinder.hasMore() ) { const Geometry* geom = zfinder.next(); for( Geometry::const_iterator m = geom->begin(); m != geom->end(); ++m ) { osg::Vec3d m_point = *m; if ( m_point.z() + absHeight > targetLen ) targetLen = m_point.z() + absHeight; if (m_point.z() < minLoc.z()) minLoc = m_point; if (m_point.z() > maxLoc.z()) maxLoc = m_point; } } // apply the height offsets height -= heightOffset; targetLen -= heightOffset; float roofRotation = 0.0f; Bounds roofBounds; float sinR = 0.0f, cosR = 0.0f; double roofTexSpanX = 0.0, roofTexSpanY = 0.0; osg::ref_ptr<const SpatialReference> roofProjSRS; if ( roofSkin ) { roofBounds = input->getBounds(); // if our data is lat/long, we need to reproject the geometry and the bounds into a projected // coordinate system in order to properly generate tex coords. if ( srs && srs->isGeographic() ) { osg::Vec2d geogCenter = roofBounds.center2d(); roofProjSRS = srs->createUTMFromLonLat( Angle(geogCenter.x()), Angle(geogCenter.y()) ); if ( roofProjSRS.valid() ) { roofBounds.transform( srs, roofProjSRS.get() ); osg::ref_ptr<Geometry> projectedInput = input->clone(); srs->transform( projectedInput->asVector(), roofProjSRS.get() ); roofRotation = getApparentRotation( projectedInput.get() ); } } else { roofRotation = getApparentRotation( input ); } sinR = sin(roofRotation); cosR = cos(roofRotation); if ( !roofSkin->isTiled().value() ) { //note: non-tiled roofs don't really work atm. roofTexSpanX = cosR*roofBounds.width() - sinR*roofBounds.height(); roofTexSpanY = sinR*roofBounds.width() + cosR*roofBounds.height(); } else { roofTexSpanX = roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : 10.0; if ( roofTexSpanX <= 0.0 ) roofTexSpanX = 10.0; roofTexSpanY = roofSkin->imageHeight().isSet() ? *roofSkin->imageHeight() : roofSkin->imageWidth().isSet() ? *roofSkin->imageWidth() : 10.0; if ( roofTexSpanY <= 0.0 ) roofTexSpanY = 10.0; } } // prep for wall texture coordinate generation. double texWidthM = wallSkin ? *wallSkin->imageWidth() : 0.0; double texHeightM = wallSkin ? *wallSkin->imageHeight() : 1.0; ConstGeometryIterator iter( input ); while( iter.hasMore() ) { const Geometry* part = iter.next(); // skip a part that's too small if (part->size() < 2) continue; // add a new wall. structure.elevations.push_back(Elevation()); Elevation& elevation = structure.elevations.back(); double maxHeight = targetLen - minLoc.z(); // Adjust the texture height so it is a multiple of the maximum height double div = osg::round(maxHeight / texHeightM); elevation.texHeightAdjustedM = div > 0.0 ? maxHeight / div : maxHeight; // Step 1 - Create the real corners and transform them into our target SRS. Corners corners; for(Geometry::const_iterator m = part->begin(); m != part->end(); ++m) { Corners::iterator corner = corners.insert(corners.end(), Corner()); // mark as "from source", as opposed to being inserted by the algorithm. corner->isFromSource = true; corner->base = *m; // extrude: if ( height >= 0 ) // extrude up { if ( flatten ) corner->roof.set( corner->base.x(), corner->base.y(), targetLen ); else corner->roof.set( corner->base.x(), corner->base.y(), corner->base.z() + height ); } else // height < 0 .. extrude down { corner->roof = *m; corner->base.z() += height; } // figure out the rooftop texture coords before doing any transformation: if ( roofSkin && srs ) { double xr, yr; if ( srs && srs->isGeographic() && roofProjSRS ) { osg::Vec3d projRoofPt; srs->transform( corner->roof, roofProjSRS.get(), projRoofPt ); xr = (projRoofPt.x() - roofBounds.xMin()); yr = (projRoofPt.y() - roofBounds.yMin()); } else { xr = (corner->roof.x() - roofBounds.xMin()); yr = (corner->roof.y() - roofBounds.yMin()); } corner->roofTexU = (cosR*xr - sinR*yr) / roofTexSpanX; corner->roofTexV = (sinR*xr + cosR*yr) / roofTexSpanY; } // transform into target SRS. transformAndLocalize( corner->base, srs, corner->base, mapSRS, _world2local, makeECEF ); transformAndLocalize( corner->roof, srs, corner->roof, mapSRS, _world2local, makeECEF ); } // Step 2 - Insert intermediate Corners as needed to satify texturing // requirements (if necessary) and record each corner offset (horizontal distance // from the beginning of the part geometry to the corner.) double cornerOffset = 0.0; double nextTexBoundary = texWidthM; for(Corners::iterator c = corners.begin(); c != corners.end(); ++c) { Corners::iterator this_corner = c; Corners::iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); osg::Vec3d base_vec = next_corner->base - this_corner->base; double span = base_vec.length(); this_corner->offsetX = cornerOffset; if (wallSkin) { base_vec /= span; // normalize osg::Vec3d roof_vec = next_corner->roof - this_corner->roof; roof_vec.normalize(); while(nextTexBoundary < cornerOffset+span) { // insert a new fake corner. Corners::iterator new_corner = corners.insert(next_corner, Corner()); new_corner->isFromSource = false; double advance = nextTexBoundary-cornerOffset; new_corner->base = this_corner->base + base_vec*advance; new_corner->roof = this_corner->roof + roof_vec*advance; new_corner->offsetX = cornerOffset + advance; nextTexBoundary += texWidthM; // advance the main iterator c = new_corner; } } cornerOffset += span; } // Step 3 - Calculate the angle of each corner. osg::Vec3d prev_vec; for(Corners::iterator c = corners.begin(); c != corners.end(); ++c) { Corners::const_iterator this_corner = c; Corners::const_iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); if ( this_corner == corners.begin() ) { Corners::const_iterator prev_corner = corners.end(); --prev_corner; prev_vec = this_corner->roof - prev_corner->roof; prev_vec.normalize(); } osg::Vec3d this_vec = next_corner->roof - this_corner->roof; this_vec.normalize(); if ( c != corners.begin() ) { c->cosAngle = prev_vec * this_vec; } } // Step 4 - Create faces connecting each pair of Posts. Faces& faces = elevation.faces; for(Corners::const_iterator c = corners.begin(); c != corners.end(); ++c) { Corners::const_iterator this_corner = c; Corners::const_iterator next_corner = c; if ( ++next_corner == corners.end() ) next_corner = corners.begin(); // only close the shape for polygons. if (next_corner != corners.begin() || structure.isPolygon) { faces.push_back(Face()); Face& face = faces.back(); face.left = *this_corner; face.right = *next_corner; // recalculate the final offset on the last face if ( next_corner == corners.begin() ) { osg::Vec3d vec = next_corner->roof - this_corner->roof; face.right.offsetX = face.left.offsetX + vec.length(); } face.widthM = next_corner->offsetX - this_corner->offsetX; } } } return true; }
bool BuildGeometryFilter::process( FeatureList& features, const FilterContext& context ) { bool makeECEF = false; const SpatialReference* featureSRS = 0L; const SpatialReference* mapSRS = 0L; if ( context.isGeoreferenced() ) { makeECEF = context.getSession()->getMapInfo().isGeocentric(); featureSRS = context.extent()->getSRS(); mapSRS = context.getSession()->getMapInfo().getProfile()->getSRS(); } for( FeatureList::iterator f = features.begin(); f != features.end(); ++f ) { Feature* input = f->get(); GeometryIterator parts( input->getGeometry(), false ); while( parts.hasMore() ) { Geometry* part = parts.next(); // skip empty geometry if ( part->size() == 0 ) continue; const Style& myStyle = input->style().isSet() ? *input->style() : _style; bool setLinePropsHere = input->style().isSet(); // otherwise it will be set globally, we assume float width = 1.0f; bool hasPolyOutline = false; const PointSymbol* pointSymbol = myStyle.get<PointSymbol>(); const LineSymbol* lineSymbol = myStyle.get<LineSymbol>(); const PolygonSymbol* polySymbol = myStyle.get<PolygonSymbol>(); // resolve the geometry type from the component type and the symbology: Geometry::Type renderType = Geometry::TYPE_UNKNOWN; // First priority is a matching part type and symbol: if ( polySymbol != 0L && part->getType() == Geometry::TYPE_POLYGON ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L && part->isLinear() ) { renderType = part->getType(); } else if ( pointSymbol != 0L && part->getType() == Geometry::TYPE_POINTSET ) { renderType = Geometry::TYPE_POINTSET; } // Second priority is the symbol: else if ( polySymbol != 0L ) { renderType = Geometry::TYPE_POLYGON; } else if ( lineSymbol != 0L ) { if ( part->getType() == Geometry::TYPE_POLYGON ) renderType = Geometry::TYPE_RING; else renderType = Geometry::TYPE_LINESTRING; } else if ( pointSymbol != 0L ) { renderType = Geometry::TYPE_POINTSET; } // No symbol? just use the geometry type. else { renderType = part->getType(); } // validate the geometry: if ( renderType == Geometry::TYPE_POLYGON && part->size() < 3 ) continue; else if ( (renderType == Geometry::TYPE_LINESTRING || renderType == Geometry::TYPE_RING) && part->size() < 2 ) continue; // resolve the color: osg::Vec4f primaryColor = polySymbol ? osg::Vec4f(polySymbol->fill()->color()) : lineSymbol ? osg::Vec4f(lineSymbol->stroke()->color()) : pointSymbol ? osg::Vec4f(pointSymbol->fill()->color()) : osg::Vec4f(1,1,1,1); osg::Geometry* osgGeom = new osg::Geometry(); osgGeom->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); if ( _featureNameExpr.isSet() ) { const std::string& name = input->eval( _featureNameExpr.mutable_value(), &context ); osgGeom->setName( name ); } // build the geometry: osg::Vec3Array* allPoints = 0L; if ( renderType == Geometry::TYPE_POLYGON ) { buildPolygon(part, featureSRS, mapSRS, makeECEF, true, osgGeom); allPoints = static_cast<osg::Vec3Array*>( osgGeom->getVertexArray() ); } else { // line or point geometry GLenum primMode = renderType == Geometry::TYPE_LINESTRING ? GL_LINE_STRIP : renderType == Geometry::TYPE_RING ? GL_LINE_LOOP : GL_POINTS; allPoints = new osg::Vec3Array(); transformAndLocalize( part->asVector(), featureSRS, allPoints, mapSRS, _world2local, makeECEF ); osgGeom->addPrimitiveSet( new osg::DrawArrays( primMode, 0, part->size() ) ); osgGeom->setVertexArray( allPoints ); applyLineAndPointSymbology( osgGeom->getOrCreateStateSet(), lineSymbol, pointSymbol ); if ( primMode == GL_POINTS && allPoints->size() == 1 ) { const osg::Vec3d& center = (*allPoints)[0]; osgGeom->setInitialBound( osg::BoundingBox(center-osg::Vec3(.5,.5,.5), center+osg::Vec3(.5,.5,.5)) ); } } if (allPoints->getVertexBufferObject()) allPoints->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); // subdivide the mesh if necessary to conform to an ECEF globe: if ( makeECEF && renderType != Geometry::TYPE_POINTSET ) { // check for explicit tessellation disable: const LineSymbol* line = _style.get<LineSymbol>(); bool disableTess = line && line->tessellation().isSetTo(0); if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); //ms.setMaxElementsPerEBO( INT_MAX ); if ( input->geoInterp().isSet() ) ms.run( *osgGeom, threshold, *input->geoInterp() ); else ms.run( *osgGeom, threshold, *_geoInterp ); } } // assign the primary color: #if USE_SINGLE_COLOR osg::Vec4Array* colors = new osg::Vec4Array( 1 ); (*colors)[0] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_OVERALL ); #else osg::Vec4Array* colors = new osg::Vec4Array( osgGeom->getVertexArray()->getNumElements() ); //allPoints->size() ); for(unsigned c=0; c<colors->size(); ++c) (*colors)[c] = primaryColor; osgGeom->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif osgGeom->setColorArray( colors ); _geode->addDrawable( osgGeom ); // record the geometry's primitive set(s) in the index: if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( osgGeom, input ); // build secondary geometry, if necessary (polygon outlines) if ( renderType == Geometry::TYPE_POLYGON && lineSymbol ) { // polygon offset on the poly so the outline doesn't z-fight osgGeom->getOrCreateStateSet()->setAttributeAndModes( new osg::PolygonOffset(1,1), 1 ); osg::Geometry* outline = new osg::Geometry(); outline->setUseVertexBufferObjects( _useVertexBufferObjects.value() ); buildPolygon(part, featureSRS, mapSRS, makeECEF, false, outline); if ( outline->getVertexArray()->getVertexBufferObject() ) outline->getVertexArray()->getVertexBufferObject()->setUsage(GL_STATIC_DRAW_ARB); osg::Vec4f outlineColor = lineSymbol->stroke()->color(); osg::Vec4Array* outlineColors = new osg::Vec4Array(); #if USE_SINGLE_COLOR outlineColors->reserve(1); outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_OVERALL ); #else unsigned pcount = part->getTotalPointCount(); outlineColors->reserve( pcount ); for( unsigned c=0; c < pcount; ++c ) outlineColors->push_back( outlineColor ); outline->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); #endif outline->setColorArray(outlineColors); // check for explicit tessellation disable: bool disableTess = lineSymbol && lineSymbol->tessellation().isSetTo(0); // subdivide if necessary. if ( makeECEF && !disableTess ) { double threshold = osg::DegreesToRadians( *_maxAngle_deg ); OE_DEBUG << "Running mesh subdivider for outlines with threshold " << *_maxAngle_deg << std::endl; MeshSubdivider ms( _world2local, _local2world ); if ( input->geoInterp().isSet() ) ms.run( *outline, threshold, *input->geoInterp() ); else ms.run( *outline, threshold, *_geoInterp ); } applyLineAndPointSymbology( outline->getOrCreateStateSet(), lineSymbol, 0L ); // make normals before adding an outline osgUtil::SmoothingVisitor sv; _geode->accept( sv ); _geode->addDrawable( outline ); //_featureNode->addDrawable( outline, input->getFID() ); // Mark each primitive set with its feature ID. if ( context.featureIndex() ) context.featureIndex()->tagPrimitiveSets( outline, input ); } } } return true; }
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* Graticule::createGridLevel( unsigned int levelNum ) const { if ( !_map->isGeocentric() ) { OE_WARN << "Graticule: only supports geocentric maps" << std::endl; return 0L; } Graticule::Level level; if ( !getLevel( levelNum, level ) ) return 0L; OE_DEBUG << "Graticule: creating grid level " << levelNum << std::endl; osg::Group* group = new osg::Group(); const Profile* mapProfile = _map->getProfile(); const GeoExtent& pex = mapProfile->getExtent(); double tw = pex.width() / (double)level._cellsX; double th = pex.height() / (double)level._cellsY; for( unsigned int x=0; x<level._cellsX; ++x ) { for( unsigned int y=0; y<level._cellsY; ++y ) { GeoExtent tex( mapProfile->getSRS(), pex.xMin() + tw * (double)x, pex.yMin() + th * (double)y, pex.xMin() + tw * (double)(x+1), pex.yMin() + th * (double)(y+1) ); Geometry* geom = createCellGeometry( tex, level._lineWidth, pex, _map->isGeocentric() ); Feature* feature = new Feature(); feature->setGeometry( geom ); FeatureList features; features.push_back( feature ); FilterContext cx; cx.profile() = new FeatureProfile( tex ); //cx.isGeocentric() = _map->isGeocentric(); if ( _map->isGeocentric() ) { // We need to make sure that on a round globe, the points are sampled such that // long segments follow the curvature of the earth. ResampleFilter resample; resample.maxLength() = tex.width() / 10.0; cx = resample.push( features, cx ); } TransformFilter xform( mapProfile->getSRS() ); //xform.setMakeGeocentric( _map->isGeocentric() ); //xform.setLocalizeCoordinates( true ); cx = xform.push( features, cx ); osg::ref_ptr<osg::Node> output; BuildGeometryFilter bg; bg.setStyle( _lineStyle ); //cx = bg.push( features, cx ); output = bg.push( features, cx ); //.getNode(); if ( _map->isGeocentric() ) { // get the geocentric control point: double cplon, cplat, cpx, cpy, cpz; tex.getCentroid( cplon, cplat ); tex.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ( osg::DegreesToRadians( cplat ), osg::DegreesToRadians( cplon ), 0.0, cpx, cpy, cpz ); osg::Vec3 controlPoint(cpx, cpy, cpz); // get the horizon point: tex.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ( osg::DegreesToRadians( tex.yMin() ), osg::DegreesToRadians( tex.xMin() ), 0.0, cpx, cpy, cpz ); osg::Vec3 horizonPoint(cpx, cpy, cpz); // the deviation is the dot product of the control vector and the vector from the // control point to the horizon point. osg::Vec3 controlPointNorm = controlPoint; controlPointNorm.normalize(); osg::Vec3 horizonVecNorm = horizonPoint - controlPoint; horizonVecNorm.normalize(); float deviation = controlPointNorm * horizonVecNorm; // construct the culling callback using the deviation. osg::ClusterCullingCallback* ccc = new osg::ClusterCullingCallback(); ccc->set( controlPoint, controlPointNorm, deviation, (controlPoint-horizonPoint).length() ); // need a new group, because never put a cluster culler on a matrixtransform (doesn't work) osg::Group* me = new osg::Group(); me->setCullCallback( ccc ); me->addChild( output.get() ); output = me; } group->addChild( output.get() ); } } // organize it for better culling osgUtil::Optimizer opt; opt.optimize( group, osgUtil::Optimizer::SPATIALIZE_GROUPS ); osg::Node* result = group; if ( levelNum < getNumLevels() ) { Graticule::Level nextLevel; if ( getLevel( levelNum+1, nextLevel ) ) { osg::PagedLOD* plod = new osg::PagedLOD(); plod->addChild( group, nextLevel._maxRange, level._maxRange ); std::stringstream buf; buf << levelNum+1 << "_" << getID() << "." << GRID_MARKER << "." << GRATICLE_EXTENSION; std::string bufStr = buf.str(); plod->setFileName( 1, bufStr ); plod->setRange( 1, 0, nextLevel._maxRange ); result = plod; } } return result; }
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; }