ReaderWriter::FeatureList ReaderWriter::featureAsString(ReaderWriter::Features feature) { typedef struct { ReaderWriter::Features feature; const char *s; } FeatureStringList; FeatureStringList list[] = { { FEATURE_READ_OBJECT, "readObject" }, { FEATURE_READ_IMAGE, "readImage" }, { FEATURE_READ_HEIGHT_FIELD, "readHeightField" }, { FEATURE_READ_NODE, "readNode" }, { FEATURE_READ_SHADER, "readShader" }, { FEATURE_WRITE_OBJECT, "writeObject" }, { FEATURE_WRITE_IMAGE, "writeImage" }, { FEATURE_WRITE_HEIGHT_FIELD, "writeHeightField" }, { FEATURE_WRITE_NODE, "writeNode" }, { FEATURE_WRITE_SHADER, "writeShader" }, { FEATURE_NONE,0 } }; FeatureList result; for(FeatureStringList *p=list; p->feature != 0; p++) { if ((feature & p->feature) != 0) result.push_back(p->s); } 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() ); } } }
FeatureList BufferFilter::process( Feature* input, FilterEnv* env ) { FeatureList output; GeoShapeList& shapes = input->getShapes(); GeoShapeList new_shapes; double b = getDistance(); if ( env->getInputSRS()->isGeographic() ) { // for geo, convert from meters to degrees //TODO: we SHOULD do this for each and every feature buffer segment, but // for how this is a shortcut approximation. double bc = b/1.4142; osg::Vec2d vec( bc, bc ); //vec.normalize(); GeoPoint c = input->getExtent().getCentroid(); osg::Vec2d p0( c.x(), c.y() ); osg::Vec2d p1; Units::convertLinearToAngularVector( vec, Units::METERS, Units::DEGREES, p0, p1 ); b = (p1-p0).length(); } for( GeoShapeList::iterator i = shapes.begin(); i != shapes.end(); i++ ) { GeoPartList new_parts; GeoShape& shape = *i; if ( shape.getShapeType() == GeoShape::TYPE_POLYGON ) { GeoShape new_shape( GeoShape::TYPE_POLYGON, shape.getSRS() ); bufferPolygons( shape, b, new_shape.getParts() ); new_shapes.push_back( new_shape ); } else if ( shape.getShapeType() == GeoShape::TYPE_LINE ) { if ( getConvertToPolygon() ) { GeoShape new_shape( GeoShape::TYPE_POLYGON, shape.getSRS() ); bufferLinesToPolygons( shape, b, new_shape ); new_shapes.push_back( new_shape ); } else { GeoShape new_shape( GeoShape::TYPE_LINE, shape.getSRS() ); bufferLinesToLines( shape, b, new_shape ); new_shapes.push_back( new_shape ); } } } if ( new_shapes.size() > 0 ) input->getShapes().swap( new_shapes ); output.push_back( input ); return output; }
void FeatureCursor::fill( FeatureList& list ) { while( hasMore() ) { list.push_back( nextFeature() ); } }
void ImageOverlay::init() { OpenThreads::ScopedLock< OpenThreads::Mutex > lock(_mutex); if (_root->getNumChildren() > 0) { _root->removeChildren(0, _root->getNumChildren()); } if ( !_clampCallback.valid() ) { _clampCallback = new TerrainCallbackAdapter<ImageOverlay>(this); } if ( getMapNode() ) { const SpatialReference* mapSRS = getMapNode()->getMapSRS(); // calculate a bounding polytope in world space (for mesh clamping): osg::ref_ptr<Feature> f = new Feature( new Polygon(), mapSRS->getGeodeticSRS() ); Geometry* g = f->getGeometry(); g->push_back( osg::Vec3d(_lowerLeft.x(), _lowerLeft.y(), 0) ); g->push_back( osg::Vec3d(_lowerRight.x(), _lowerRight.y(), 0) ); g->push_back( osg::Vec3d(_upperRight.x(), _upperRight.y(), 0) ); g->push_back( osg::Vec3d(_upperLeft.x(), _upperLeft.y(), 0) ); f->getWorldBoundingPolytope( getMapNode()->getMapSRS(), _boundingPolytope ); FeatureList features; if (!mapSRS->isGeographic()) { f->splitAcrossDateLine(features); } else { features.push_back( f ); } for (FeatureList::iterator itr = features.begin(); itr != features.end(); ++itr) { _root->addChild(createNode(itr->get(), features.size() > 1)); } _dirty = false; // Set the annotation up for auto-clamping. We always need to auto-clamp a draped image // so that the mesh roughly conforms with the surface, otherwise the draping routine // might clip it. Style style; style.getOrCreate<AltitudeSymbol>()->clamping() = AltitudeSymbol::CLAMP_RELATIVE_TO_TERRAIN; applyStyle( style ); setLightingIfNotSet( false ); getMapNode()->getTerrain()->addTerrainCallback( _clampCallback.get() ); clamp( getMapNode()->getTerrain()->getGraph(), getMapNode()->getTerrain() ); } }
osg::Node* GeometryCompiler::compile(Feature* feature, const Style& style, const FilterContext& context) { FeatureList workingSet; workingSet.push_back(feature); return compile(workingSet, style, context); }
virtual void traverse( FeatureTile* tile) { if (tile->getFeatures().size() > 0) { //Actually load up the features FeatureList features; for (FeatureIDList::const_iterator i = tile->getFeatures().begin(); i != tile->getFeatures().end(); i++) { Feature* f = _features->getFeature( *i ); if (f) { //Reproject the feature to the dest SRS if it's not already if (!f->getSRS()->isEquivalentTo( _srs ) ) { f->transform( _srs ); } features.push_back( f ); } else { OE_NOTICE << "couldn't get feature " << *i << std::endl; } } //Need to do the cropping again since these are brand new features coming from the feature source. CropFilter cropFilter(_cropMethod); FilterContext context(0); context.extent() = tile->getExtent(); cropFilter.push( features, context ); std::string contents = Feature::featuresToGeoJSON( features ); std::stringstream buf; int x = tile->getKey().getTileX(); unsigned int numRows, numCols; tile->getKey().getProfile()->getNumTiles(tile->getKey().getLevelOfDetail(), numCols, numRows); int y = numRows - tile->getKey().getTileY() - 1; buf << _dest << "/" << tile->getKey().getLevelOfDetail() << "/" << x << "/" << y << ".json"; std::string filename = buf.str(); //OE_NOTICE << "Writing " << features.size() << " features to " << filename << std::endl; if ( !osgDB::fileExists( osgDB::getFilePath(filename) ) ) osgEarth::makeDirectoryForFile( filename ); std::fstream output( filename.c_str(), std::ios_base::out ); if ( output.is_open() ) { output << contents; output.flush(); output.close(); } } tile->traverse( this ); }
bool getFeatures( const std::string& buffer, const std::string& mimeType, FeatureList& features ) { // find the right driver for the given mime type OGR_SCOPED_LOCK; // find the right driver for the given mime type OGRSFDriverH ogrDriver = isJSON(mimeType) ? OGRGetDriverByName( "GeoJSON" ) : isGML(mimeType) ? OGRGetDriverByName( "GML" ) : 0L; // fail if we can't find an appropriate OGR driver: if ( !ogrDriver ) { OE_WARN << LC << "Error reading TFS response; cannot grok content-type \"" << mimeType << "\"" << std::endl; return false; } OGRDataSourceH ds = OGROpen( buffer.c_str(), FALSE, &ogrDriver ); if ( !ds ) { OE_WARN << LC << "Error reading TFS response" << std::endl; return false; } // read the feature data. OGRLayerH layer = OGR_DS_GetLayer(ds, 0); if ( layer ) { const SpatialReference* srs = _layer.getSRS(); OGR_L_ResetReading(layer); OGRFeatureH feat_handle; while ((feat_handle = OGR_L_GetNextFeature( layer )) != NULL) { if ( feat_handle ) { osg::ref_ptr<Feature> f = OgrUtils::createFeature( feat_handle, srs ); if ( f.valid() && !isBlacklisted(f->getFID()) ) { features.push_back( f.release() ); } OGR_F_Destroy( feat_handle ); } } } // Destroy the datasource OGR_DS_Destroy( ds ); return true; }
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); } } }
virtual void traverse( FeatureTile* tile) { if (_added && _cropMethod != CropFilter::METHOD_CROPPING) return; bool traverse = true; GeoExtent featureExtent(_feature->getSRS(), _feature->getGeometry()->getBounds()); if (featureExtent.intersects( tile->getExtent())) { //If the node contains the feature, and it doesn't contain the max number of features add it. If it's already full then //split it. if (tile->getKey().getLevelOfDetail() >= (unsigned int)_firstLevel && (tile->getFeatures().size() < (unsigned int)_maxFeatures || tile->getKey().getLevelOfDetail() == _maxLevel || tile->getKey().getLevelOfDetail() == _levelAdded)) { if (_levelAdded < 0 || _levelAdded == tile->getKey().getLevelOfDetail()) { osg::ref_ptr< Feature > clone = new Feature( *_feature, osg::CopyOp::DEEP_COPY_ALL ); FeatureList features; features.push_back( clone ); CropFilter cropFilter(_cropMethod); FilterContext context(0); context.extent() = tile->getExtent(); cropFilter.push( features, context ); if (!features.empty() && clone->getGeometry() && clone->getGeometry()->isValid()) { //tile->getFeatures().push_back( clone ); tile->getFeatures().push_back( clone->getFID() ); _added = true; _levelAdded = tile->getKey().getLevelOfDetail(); _numAdded++; traverse = false; } } if (traverse || _cropMethod == CropFilter::METHOD_CROPPING) { tile->traverse( this ); } } else { tile->split(); tile->traverse( this ); } } }
FeatureCursor* FeatureListSource::createFeatureCursor( const Symbology::Query& query ) { //Create a copy of all of the features before returning the cursor. //The processing filters in osgEarth can modify the features as they are operating and we don't want our original data destroyed. FeatureList cursorFeatures; for (FeatureList::iterator itr = _features.begin(); itr != _features.end(); ++itr) { Feature* feature = new osgEarth::Features::Feature(*(itr->get()), osg::CopyOp::DEEP_COPY_ALL); cursorFeatures.push_back( feature ); } return new FeatureListCursor( cursorFeatures ); }
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; }
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 getFeatures(HTTPResponse &response, FeatureList& features) { //OE_NOTICE << "mimetype=" << response.getMimeType() << std::endl; //TODO: Handle more than just geojson... std::string ext = getExtensionForMimeType(response.getMimeType()); //Save the response to a temp file std::string tmpPath = getTempPath(); std::string name = getTempName(tmpPath, ext); saveResponse(response, name ); //OE_NOTICE << "Saving to " << name << std::endl; //OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, &driver); OGRDataSourceH ds = OGROpen(name.c_str(), FALSE, NULL); if (!ds) { OE_NOTICE << "Error opening data with contents " << std::endl << response.getPartAsString(0) << std::endl; } OGRLayerH layer = OGR_DS_GetLayer(ds, 0); //Read all the features if (layer) { OGR_L_ResetReading(layer); OGRFeatureH feat_handle; while ((feat_handle = OGR_L_GetNextFeature( layer )) != NULL) { if ( feat_handle ) { Feature* f = OgrUtils::createFeature( feat_handle ); if ( f ) { features.push_back( f ); } OGR_F_Destroy( feat_handle ); } } } //Destroy the datasource OGR_DS_Destroy( ds ); //Remove the temporary file remove( name.c_str() ); }
osg::Node* GeometryCompiler::compile(Feature* feature, const Style& style, const FilterContext& context) { if ( !context.profile() ) { OE_WARN << LC << "Valid feature profile required" << std::endl; return 0L; } if ( style.empty() ) { OE_WARN << LC << "Non-empty style required" << std::endl; return 0L; } FeatureList workingSet; workingSet.push_back(feature); return compile(workingSet, style, context); }
FeatureList DecimateFilter::process( Feature* input, FilterEnv* env ) { FeatureList output; GeoShapeList new_shapes; for( GeoShapeList::iterator i = input->getShapes().begin(); i != input->getShapes().end(); i++ ) { int min_points = i->getShapeType() == GeoShape::TYPE_POLYGON? 3 : i->getShapeType() == GeoShape::TYPE_LINE? 2 : 1; GeoPartList new_parts; for( GeoPartList::iterator j = i->getParts().begin(); j != i->getParts().end(); j++ ) { decimatePart( *j, distance_threshold, min_points, new_parts ); } i->getParts().swap( new_parts ); if ( new_parts.size() > 0 ) { new_shapes.push_back( *i ); } } input->getShapes().swap( new_shapes ); if ( input->getShapes().size() > 0 ) { output.push_back( input ); } return output; }
bool getFeatures( const std::string& buffer, const std::string& mimeType, FeatureList& features ) { OGR_SCOPED_LOCK; bool json = isJSON( mimeType ); bool gml = isGML( mimeType ); // find the right driver for the given mime type OGRSFDriverH ogrDriver = json ? OGRGetDriverByName( "GeoJSON" ) : gml ? OGRGetDriverByName( "GML" ) : 0L; // fail if we can't find an appropriate OGR driver: if ( !ogrDriver ) { OE_WARN << LC << "Error reading WFS response; cannot grok content-type \"" << mimeType << "\"" << std::endl; return false; } std::string tmpName; OGRDataSourceH ds = 0; //GML needs to be saved to a temp file to load from disk. GeoJSON can be loaded directly from memory if (gml) { std::string ext = getExtensionForMimeType( mimeType ); //Save the response to a temp file std::string tmpPath = getTempPath(); tmpName = getTempName(tmpPath, ext); saveResponse(buffer, tmpName ); ds = OGROpen( tmpName.c_str(), FALSE, &ogrDriver ); } else if (json) { //Open GeoJSON directly from memory ds = OGROpen( buffer.c_str(), FALSE, &ogrDriver ); } if ( !ds ) { OE_WARN << LC << "Error reading WFS response" << std::endl; return false; } // read the feature data. OGRLayerH layer = OGR_DS_GetLayer(ds, 0); if ( layer ) { FeatureProfile* fp = getFeatureProfile(); const SpatialReference* srs = fp ? fp->getSRS() : 0L; OGR_L_ResetReading(layer); OGRFeatureH feat_handle; while ((feat_handle = OGR_L_GetNextFeature( layer )) != NULL) { if ( feat_handle ) { osg::ref_ptr<Feature> f = OgrUtils::createFeature( feat_handle, srs ); if ( f.valid() && !isBlacklisted(f->getFID()) ) { features.push_back( f.release() ); } OGR_F_Destroy( feat_handle ); } } } // Destroy the datasource OGR_DS_Destroy( ds ); //Delete the temp file if one was created if (!tmpName.empty()) { remove( tmpName.c_str() ); } return true; }
// 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; FeatureList preProcessList; OGR_SCOPED_LOCK; if ( _nextHandleToQueue ) { Feature* f = createFeature( _nextHandleToQueue ); if ( f ) { _queue.push( f ); if ( _filters.size() > 0 ) preProcessList.push_back( f ); } OGR_F_Destroy( _nextHandleToQueue ); _nextHandleToQueue = 0L; } int handlesToQueue = _chunkSize - _queue.size(); for( int i=0; i<handlesToQueue; i++ ) { OGRFeatureH handle = OGR_L_GetNextFeature( _resultSetHandle ); if ( handle ) { Feature* f = createFeature( handle ); if ( f ) { _queue.push( f ); if ( _filters.size() > 0 ) preProcessList.push_back( f ); } OGR_F_Destroy( handle ); } else break; } // preprocess the features using the filter list: if ( preProcessList.size() > 0 ) { FilterContext cx; cx.profile() = _profile.get(); for( FeatureFilterList::const_iterator i = _filters.begin(); i != _filters.end(); ++i ) { FeatureFilter* filter = i->get(); cx = filter->push( preProcessList, cx ); } } // read one more for "more" detection: _nextHandleToQueue = OGR_L_GetNextFeature( _resultSetHandle ); //OE_NOTICE << "read " << _queue.size() << " features ... " << std::endl; }
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; }
// reads a chunk of features into a memory cache; do this for performance // and to avoid needing the OGR Mutex every time void FeatureCursorCDBV::readChunk() { if ( !_resultSetHandle ) return; FeatureList preProcessList; OGR_SCOPED_LOCK; if ( _nextHandleToQueue ) { osg::ref_ptr<Feature> f = OgrUtils::createFeature( _nextHandleToQueue, _profile.get() ); if ( f.valid() && !_source->isBlacklisted(f->getFID()) ) { f->setFID(_s_CDBV_FeatureID); ++_s_CDBV_FeatureID; 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( _nextHandleToQueue ); _nextHandleToQueue = 0L; } unsigned handlesToQueue = _chunkSize - _queue.size(); bool resultSetEndReached = false; for( unsigned i=0; i<handlesToQueue; i++ ) { OGRFeatureH handle = OGR_L_GetNextFeature( _resultSetHandle ); if ( handle ) { osg::ref_ptr<Feature> f = OgrUtils::createFeature( handle, _profile.get() ); if ( f.valid() && !_source->isBlacklisted(f->getFID()) ) { f->setFID(_s_CDBV_FeatureID); ++_s_CDBV_FeatureID; 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( handle ); } else { resultSetEndReached = true; break; } } // 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 ); } } // read one more for "more" detection: if (!resultSetEndReached) _nextHandleToQueue = OGR_L_GetNextFeature( _resultSetHandle ); else _nextHandleToQueue = 0L; //OE_NOTICE << "read " << _queue.size() << " features ... " << std::endl; }
osg::Group* UTMData::buildGZDTile(const std::string& name, const GeoExtent& extent, const Style& style, const FeatureProfile* featureProfile, const Map* map) { osg::Group* group = new osg::Group(); Style lineStyle; lineStyle.add( const_cast<LineSymbol*>(style.get<LineSymbol>()) ); lineStyle.add( const_cast<AltitudeSymbol*>(style.get<AltitudeSymbol>()) ); bool hasText = style.get<TextSymbol>() != 0L; GeometryCompiler compiler; osg::ref_ptr<Session> session = new Session(map); FilterContext context( session.get(), featureProfile, extent ); // make sure we get sufficient tessellation: compiler.options().maxGranularity() = 1.0; FeatureList features; // longitudinal line: LineString* lon = new LineString(2); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lon->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); Feature* lonFeature = new Feature(lon, extent.getSRS()); lonFeature->geoInterp() = GEOINTERP_GREAT_CIRCLE; features.push_back( lonFeature ); // latitudinal line: LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMin(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMin(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); // top lat line at 84N if ( extent.yMax() == 84.0 ) { LineString* lat = new LineString(2); lat->push_back( osg::Vec3d(extent.xMin(), extent.yMax(), 0) ); lat->push_back( osg::Vec3d(extent.xMax(), extent.yMax(), 0) ); Feature* latFeature = new Feature(lat, extent.getSRS()); latFeature->geoInterp() = GEOINTERP_RHUMB_LINE; features.push_back( latFeature ); } osg::Node* geomNode = compiler.compile(features, lineStyle, context); if ( geomNode ) group->addChild( geomNode ); // get the geocentric tile center: osg::Vec3d tileCenter; extent.getCentroid( tileCenter.x(), tileCenter.y() ); const SpatialReference* ecefSRS = extent.getSRS()->getGeocentricSRS(); osg::Vec3d centerECEF; extent.getSRS()->transform( tileCenter, ecefSRS, centerECEF ); if ( hasText ) { osg::Vec3d west, east; extent.getSRS()->transform( osg::Vec3d(extent.xMin(),tileCenter.y(),0), ecefSRS, west ); extent.getSRS()->transform( osg::Vec3d(extent.xMax(),tileCenter.y(),0), ecefSRS, east ); const TextSymbol* textSym_in = style.get<TextSymbol>(); osg::ref_ptr<TextSymbol> textSym = textSym_in ? new TextSymbol(*textSym_in) : new TextSymbol(); textSym->size() = (west-east).length() / 3.0; TextSymbolizer ts(textSym.get()); osg::Geode* textGeode = new osg::Geode(); osg::Drawable* d = ts.create(name); d->getOrCreateStateSet()->setRenderBinToInherit(); textGeode->addDrawable(d); Registry::shaderGenerator().run(textGeode, Registry::stateSetCache()); osg::Matrixd centerL2W; ecefSRS->createLocalToWorld( centerECEF, centerL2W ); osg::MatrixTransform* mt = new osg::MatrixTransform(centerL2W); mt->addChild(textGeode); group->addChild(mt); } //group = buildGZDChildren( group, name ); group = ClusterCullingFactory::createAndInstall( group, centerECEF )->asGroup(); return group; }
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; } }
FeatureCursor* createFeatureCursor( const Symbology::Query& query ) { TileKey key = *query.tileKey(); #if 0 // Debug Polygon* poly = new Polygon(); poly->push_back(key.getExtent().xMin(), key.getExtent().yMin()); poly->push_back(key.getExtent().xMax(), key.getExtent().yMin()); poly->push_back(key.getExtent().xMax(), key.getExtent().yMax()); poly->push_back(key.getExtent().xMin(), key.getExtent().yMax()); FeatureList features; Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); features.push_back( feature ); return new FeatureListCursor( features ); #else osg::ref_ptr< osgEarth::ImageLayer > layer = query.getMap()->getImageLayerByName(*_options.layer()); if (layer.valid()) { GeoImage image = layer->createImage( key ); FeatureList features; if (image.valid()) { double pixWidth = key.getExtent().width() / (double)image.getImage()->s(); double pixHeight = key.getExtent().height() / (double)image.getImage()->t(); ImageUtils::PixelReader reader(image.getImage()); for (unsigned int r = 0; r < image.getImage()->t(); r++) { double y = key.getExtent().yMin() + (double)r * pixHeight; double minX = 0; double maxX = 0; float value = 0.0; for (unsigned int c = 0; c < image.getImage()->s(); c++) { double x = key.getExtent().xMin() + (double)c * pixWidth; osg::Vec4f color = reader(c, r); // Starting a new row. Initialize the values. if (c == 0) { minX = x; maxX = x + pixWidth; value = color.r(); } // Ending a row, finish the polygon. else if (c == image.getImage()->s() -1) { // Increment the maxX to finish the row. maxX = x + pixWidth; Polygon* poly = new Polygon(); poly->push_back(minX, y); poly->push_back(maxX, y); poly->push_back(maxX, y+pixHeight); poly->push_back(minX, y+pixHeight); Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); feature->set(*_options.attribute(), value); features.push_back( feature ); minX = x; maxX = x + pixWidth; value = color.r(); } // The value is different, so complete the polygon and start a new one. else if (color.r() != value) { Polygon* poly = new Polygon(); poly->push_back(minX, y); poly->push_back(maxX, y); poly->push_back(maxX, y+pixHeight); poly->push_back(minX, y+pixHeight); Feature* feature = new Feature(poly, SpatialReference::create("wgs84")); feature->set(*_options.attribute(), value); features.push_back( feature ); minX = x; maxX = x + pixWidth; value = color.r(); } // The value is the same as the previous value, continue the polygon by increasing the maxX. else if (color.r() == value) { maxX = x + pixWidth; } } } if (!features.empty()) { //OE_NOTICE << LC << "Returning " << features.size() << " features" << std::endl; return new FeatureListCursor( features ); } } } else { OE_NOTICE << LC << "Couldn't get layer " << *_options.layer() << std::endl; } return 0; #endif }
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(); }
FeatureCursor* createFeatureCursor( const Symbology::Query& query ) { TileKey key = *query.tileKey(); int z = key.getLevelOfDetail(); int tileX = key.getTileX(); int tileY = key.getTileY(); unsigned int numRows, numCols; key.getProfile()->getNumTiles(key.getLevelOfDetail(), numCols, numRows); tileY = numRows - tileY - 1; //Get the image sqlite3_stmt* select = NULL; std::string queryStr = "SELECT tile_data from tiles where zoom_level = ? AND tile_column = ? AND tile_row = ?"; int rc = sqlite3_prepare_v2( _database, queryStr.c_str(), -1, &select, 0L ); if ( rc != SQLITE_OK ) { OE_WARN << LC << "Failed to prepare SQL: " << queryStr << "; " << sqlite3_errmsg(_database) << std::endl; return NULL; } bool valid = true; sqlite3_bind_int( select, 1, z ); sqlite3_bind_int( select, 2, tileX ); sqlite3_bind_int( select, 3, tileY ); rc = sqlite3_step( select ); FeatureList features; if ( rc == SQLITE_ROW) { // the pointer returned from _blob gets freed internally by sqlite, supposedly const char* data = (const char*)sqlite3_column_blob( select, 0 ); int dataLen = sqlite3_column_bytes( select, 0 ); std::string dataBuffer( data, dataLen ); // decompress if necessary: if ( _compressor.valid() ) { std::istringstream inputStream(dataBuffer); std::string value; if ( !_compressor->decompress(inputStream, value) ) { OE_WARN << LC << "Decompression failed" << std::endl; valid = false; } else { dataBuffer = value; } } mapnik::vector::tile tile; if (tile.ParseFromString(dataBuffer)) { // Get the layer in question for (unsigned int i = 0; i < tile.layers().size(); i++) { const mapnik::vector::tile_layer &layer = tile.layers().Get(i); //OE_NOTICE << layer.name() << std::endl; //if (layer.name() != "road") continue; //if (layer.name() != "building") continue; for (unsigned int j = 0; j < layer.features().size(); j++) { const mapnik::vector::tile_feature &feature = layer.features().Get(j); osg::ref_ptr< osgEarth::Symbology::Geometry > geometry; eGeomType geomType = static_cast<eGeomType>(feature.type()); if (geomType == ::Polygon) { //OE_NOTICE << "Polygon " << std::endl; geometry = new osgEarth::Symbology::Polygon(); } else if (geomType == ::LineString) { //OE_NOTICE << "LineString" << std::endl; geometry = new osgEarth::Symbology::LineString(); } else if (geomType == ::Point) { //OE_NOTICE << "Point" << std::endl; geometry = new osgEarth::Symbology::PointSet(); } else { //OE_NOTICE << "uknown" << std::endl; geometry = new osgEarth::Symbology::LineString(); } osg::ref_ptr< Feature > oeFeature = new Feature(geometry, key.getProfile()->getSRS()); features.push_back(oeFeature.get()); // Read attributes for (unsigned int k = 0; k < feature.tags().size(); k+=2) { std::string key = layer.keys().Get(feature.tags().Get(k)); mapnik::vector::tile_value value = layer.values().Get(feature.tags().Get(k+1)); if (value.has_bool_value()) { oeFeature->set(key, value.bool_value()); } else if (value.has_double_value()) { oeFeature->set(key, value.double_value()); } else if (value.has_float_value()) { oeFeature->set(key, value.float_value()); } else if (value.has_int_value()) { oeFeature->set(key, (int)value.int_value()); } else if (value.has_sint_value()) { oeFeature->set(key, (int)value.sint_value()); } else if (value.has_string_value()) { oeFeature->set(key, value.string_value()); } else if (value.has_uint_value()) { oeFeature->set(key, (int)value.uint_value()); } // Special path for getting heights from our test dataset. if (key == "other_tags") { std::string other_tags = value.string_value(); StringTokenizer tok("=>"); StringVector tized; tok.tokenize(other_tags, tized); if (tized.size() == 3) { if (tized[0] == "height") { std::string value = tized[2]; // Remove quotes from the height float height = as<float>(value, FLT_MAX); if (height != FLT_MAX) { oeFeature->set("height", height); } } } } } unsigned int length = 0; int cmd = -1; const int cmd_bits = 3; unsigned int tileres = layer.extent(); int x = 0; int y = 0; for (int k = 0; k < feature.geometry_size();) { if (!length) { unsigned int cmd_length = feature.geometry(k++); cmd = cmd_length & ((1 << cmd_bits) - 1); length = cmd_length >> cmd_bits; } if (length > 0) { length--; if (cmd == SEG_MOVETO || cmd == SEG_LINETO) { int px = feature.geometry(k++); int py = feature.geometry(k++); px = zig_zag_decode(px); py = zig_zag_decode(py); x += px; y += py; double width = key.getExtent().width(); double height = key.getExtent().height(); double geoX = key.getExtent().xMin() + (width/(double)tileres) * (double)x; double geoY = key.getExtent().yMax() - (height/(double)tileres) * (double)y; geometry->push_back(geoX, geoY, 0); } else if (cmd == (SEG_CLOSE & ((1 << cmd_bits) - 1))) { geometry->push_back(geometry->front()); } } } if (geometry->getType() == Geometry::TYPE_POLYGON) { geometry->rewind(osgEarth::Symbology::Geometry::ORIENTATION_CCW); } } } } else {
osg::HeightField* createHeightField( const TileKey& key, ProgressCallback* progress) { if (key.getLevelOfDetail() > _maxDataLevel) { //OE_NOTICE << "Reached maximum data resolution key=" << key.getLevelOfDetail() << " max=" << _maxDataLevel << std::endl; return NULL; } int tileSize = _options.tileSize().value(); //Allocate the heightfield osg::ref_ptr<osg::HeightField> hf = new osg::HeightField; hf->allocate(tileSize, tileSize); for (unsigned int i = 0; i < hf->getHeightList().size(); ++i) hf->getHeightList()[i] = NO_DATA_VALUE; if (intersects(key)) { //Get the extents of the tile double xmin, ymin, xmax, ymax; key.getExtent().getBounds(xmin, ymin, xmax, ymax); const SpatialReference* featureSRS = _features->getFeatureProfile()->getSRS(); GeoExtent extentInFeatureSRS = key.getExtent().transform( featureSRS ); const SpatialReference* keySRS = key.getProfile()->getSRS(); // populate feature list // assemble a spatial query. It helps if your features have a spatial index. Query query; query.bounds() = extentInFeatureSRS.bounds(); FeatureList featureList; osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor(query); while ( cursor.valid() && cursor->hasMore() ) { Feature* f = cursor->nextFeature(); if ( f && f->getGeometry() ) featureList.push_back(f); } // We now have a feature list in feature SRS. bool transformRequired = !keySRS->isHorizEquivalentTo(featureSRS); if (!featureList.empty()) { // Iterate over the output heightfield and sample the data that was read into it. double dx = (xmax - xmin) / (tileSize-1); double dy = (ymax - ymin) / (tileSize-1); for (int c = 0; c < tileSize; ++c) { double geoX = xmin + (dx * (double)c); for (int r = 0; r < tileSize; ++r) { double geoY = ymin + (dy * (double)r); float h = NO_DATA_VALUE; for (FeatureList::iterator f = featureList.begin(); f != featureList.end(); ++f) { osgEarth::Symbology::Polygon* boundary = dynamic_cast<osgEarth::Symbology::Polygon*>((*f)->getGeometry()); if (!boundary) { OE_WARN << LC << "NOT A POLYGON" << std::endl; } else { GeoPoint geo(keySRS, geoX, geoY, 0.0, ALTMODE_ABSOLUTE); if ( transformRequired ) geo = geo.transform(featureSRS); if ( boundary->contains2D(geo.x(), geo.y()) ) { h = (*f)->getDouble(_options.attr().value()); if ( keySRS->isGeographic() ) { // for a round earth, must adjust the final elevation accounting for the // curvature of the earth; so we have to adjust it in the feature boundary's // local tangent plane. Bounds bounds = boundary->getBounds(); GeoPoint anchor( featureSRS, bounds.center().x(), bounds.center().y(), h, ALTMODE_ABSOLUTE ); if ( transformRequired ) anchor = anchor.transform(keySRS); // For transforming between ECEF and local tangent plane: osg::Matrix localToWorld, worldToLocal; anchor.createLocalToWorld(localToWorld); worldToLocal.invert( localToWorld ); // Get the ECEF location of the anchor point: osg::Vec3d ecef; geo.toWorld( ecef ); // Move it into Local Tangent Plane coordinates: osg::Vec3d local = ecef * worldToLocal; // Reset the Z to zero, since the LTP is centered on the "h" elevation: local.z() = 0.0; // Back into ECEF: ecef = local * localToWorld; // And back into lat/long/alt: geo.fromWorld( geo.getSRS(), ecef); h = geo.z(); } break; } } } hf->setHeight(c, r, h-0.1); } } } } return hf.release(); }
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() ); } } } }
bool BillboardExtension::connect(MapNode* mapNode) { if ( !mapNode ) { OE_WARN << LC << "Illegal: MapNode cannot be null." << std::endl; return false; } OE_INFO << LC << "Connecting to MapNode.\n"; if ( !_options.imageURI().isSet() ) { OE_WARN << LC << "Illegal: image URI is required" << std::endl; return false; } if ( !_options.featureOptions().isSet() ) { OE_WARN << LC << "Illegal: feature source is required" << std::endl; return false; } _features = FeatureSourceFactory::create( _options.featureOptions().value() ); if ( !_features.valid() ) { OE_WARN << LC << "Illegal: no valid feature source provided" << std::endl; return false; } //if ( _features->getGeometryType() != osgEarth::Symbology::Geometry::TYPE_POINTSET ) //{ // OE_WARN << LC << "Illegal: only points currently supported" << std::endl; // return false; //} _features->initialize( _dbOptions ); osg::Vec3dArray* verts; if ( _features->getFeatureProfile() ) { verts = new osg::Vec3dArray(); OE_NOTICE << "Reading features...\n"; osg::ref_ptr<FeatureCursor> cursor = _features->createFeatureCursor(); while ( cursor.valid() && cursor->hasMore() ) { Feature* f = cursor->nextFeature(); if ( f && f->getGeometry() ) { if ( f->getGeometry()->getComponentType() == Geometry::TYPE_POLYGON ) { FilterContext cx; cx.setProfile( new FeatureProfile(_features->getFeatureProfile()->getExtent()) ); ScatterFilter scatter; scatter.setDensity( _options.density().get() ); scatter.setRandom( true ); FeatureList featureList; featureList.push_back(f); scatter.push( featureList, cx ); } // Init a filter to tranform feature in desired SRS if (!mapNode->getMapSRS()->isEquivalentTo(_features->getFeatureProfile()->getSRS())) { FilterContext cx; cx.setProfile( new FeatureProfile(_features->getFeatureProfile()->getExtent()) ); TransformFilter xform( mapNode->getMapSRS() ); FeatureList featureList; featureList.push_back(f); cx = xform.push(featureList, cx); } GeometryIterator iter(f->getGeometry()); while(iter.hasMore()) { const Geometry* geom = iter.next(); osg::ref_ptr<osg::Vec3dArray> fVerts = geom->createVec3dArray(); verts->insert(verts->end(), fVerts->begin(), fVerts->end()); } } } } else { OE_WARN << LC << "Illegal: feature source has no SRS" << std::endl; return false; } if ( verts && verts->size() > 0 ) { OE_NOTICE << LC << "Read " << verts->size() << " points.\n"; //localize all the verts GeoPoint centroid; _features->getFeatureProfile()->getExtent().getCentroid(centroid); centroid = centroid.transform(mapNode->getMapSRS()); OE_NOTICE << "Centroid = " << centroid.x() << ", " << centroid.y() << "\n"; osg::Matrixd l2w, w2l; centroid.createLocalToWorld(l2w); w2l.invert(l2w); osg::MatrixTransform* mt = new osg::MatrixTransform; mt->setMatrix(l2w); OE_NOTICE << "Clamping elevations...\n"; osgEarth::ElevationQuery eq(mapNode->getMap()); eq.setFallBackOnNoData( true ); eq.getElevations(verts->asVector(), mapNode->getMapSRS(), true, 0.005); OE_NOTICE << "Building geometry...\n"; osg::Vec3Array* normals = new osg::Vec3Array(verts->size()); osg::Vec4Array* colors = new osg::Vec4Array(verts->size()); Random rng; for (int i=0; i < verts->size(); i++) { GeoPoint vert(mapNode->getMapSRS(), (*verts)[i], osgEarth::ALTMODE_ABSOLUTE); osg::Vec3d world; vert.toWorld(world); (*verts)[i] = world * w2l; osg::Vec3 normal = world; normal.normalize(); (*normals)[i] = osg::Matrix::transform3x3(normal, w2l); double n = rng.next(); (*colors)[i].set( n, n, n, 1 ); } //create geom and primitive sets osg::Geometry* geometry = new osg::Geometry(); geometry->setVertexArray( verts ); geometry->setNormalArray( normals ); geometry->setNormalBinding( osg::Geometry::BIND_PER_VERTEX ); geometry->setColorArray(colors); geometry->setColorBinding( osg::Geometry::BIND_PER_VERTEX ); geometry->addPrimitiveSet( new osg::DrawArrays( GL_POINTS, 0, verts->size() ) ); //create image and texture to render to osg::Texture2D* tex = new osg::Texture2D(_options.imageURI()->getImage(_dbOptions)); tex->setResizeNonPowerOfTwoHint(false); tex->setFilter( osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_LINEAR ); tex->setFilter( osg::Texture::MAG_FILTER, osg::Texture::LINEAR ); tex->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE); tex->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE); geometry->setName("BillboardPoints"); osg::Geode* geode = new osg::Geode; geode->addDrawable(geometry); //osg::ref_ptr<StateSetCache> cache = new StateSetCache(); //Registry::shaderGenerator().run(geode, cache.get()); //set the texture related uniforms osg::StateSet* geode_ss = geode->getOrCreateStateSet(); geode_ss->setTextureAttributeAndModes( 2, tex, 1 ); geode_ss->getOrCreateUniform("billboard_tex", osg::Uniform::SAMPLER_2D)->set( 2 ); float bbWidth = (float)tex->getImage()->s() / 2.0f; float bbHeight = (float)tex->getImage()->t(); float aspect = (float)tex->getImage()->s() / (float)tex->getImage()->t(); if (_options.height().isSet()) { bbHeight = _options.height().get(); if (!_options.width().isSet()) { bbWidth = bbHeight * aspect / 2.0f; } } if (_options.width().isSet()) { bbWidth = _options.width().get() / 2.0f; if (!_options.height().isSet()) { bbHeight = _options.width().get() / aspect; } } geode_ss->getOrCreateUniform("billboard_width", osg::Uniform::FLOAT)->set( bbWidth ); geode_ss->getOrCreateUniform("billboard_height", osg::Uniform::FLOAT)->set( bbHeight ); geode_ss->setMode(GL_BLEND, osg::StateAttribute::ON); //for now just using an osg::Program //TODO: need to add GeometryShader support to the shader comp setup VirtualProgram* vp = VirtualProgram::getOrCreate(geode_ss); vp->setName( "osgEarth Billboard Extension" ); ShaderPackage shaders; shaders.add( "Billboard geometry shader", billboardGeomShader ); shaders.add( "Billboard fragment shader", billboardFragShader ); shaders.loadAll( vp ); geode_ss->setMode( GL_CULL_FACE, osg::StateAttribute::OFF ); geode->setCullingActive(false); mt->addChild(geode); mapNode->getModelLayerGroup()->addChild(mt); return true; } return false; }
osg::Group* FeatureModelGraph::build(const Style& defaultStyle, const Query& baseQuery, const GeoExtent& workingExtent, FeatureSourceIndex* index) { osg::ref_ptr<osg::Group> group = new osg::Group(); FeatureSource* source = _session->getFeatureSource(); // case: each feature has an embedded style. if ( source->hasEmbeddedStyles() ) { const FeatureProfile* featureProfile = source->getFeatureProfile(); // each feature has its own style, so use that and ignore the style catalog. osg::ref_ptr<FeatureCursor> cursor = source->createFeatureCursor( baseQuery ); while( cursor.valid() && cursor->hasMore() ) { Feature* feature = cursor->nextFeature(); if ( feature ) { FeatureList list; list.push_back( feature ); osg::ref_ptr<FeatureCursor> cursor = new FeatureListCursor(list); FilterContext context( _session.get(), featureProfile, workingExtent, index ); // note: gridding is not supported for embedded styles. osg::ref_ptr<osg::Node> node; // Get the Group that parents all features of this particular style. Note, this // might be NULL if the factory does not support style groups. osg::Group* styleGroup = _factory->getOrCreateStyleGroup(*feature->style(), _session.get()); if ( styleGroup ) { if ( !group->containsNode( styleGroup ) ) group->addChild( styleGroup ); } if ( _factory->createOrUpdateNode( cursor.get(), *feature->style(), context, node ) ) { if ( node.valid() ) { if ( styleGroup ) styleGroup->addChild( node.get() ); else group->addChild( node.get() ); } } } } } // case: features are externally styled. else { const StyleSheet* styles = _session->styles(); // if the stylesheet has selectors, use them to sort the features into style groups. Then create // a create a node for each style group. if ( styles->selectors().size() > 0 ) { for( StyleSelectorList::const_iterator i = styles->selectors().begin(); i != styles->selectors().end(); ++i ) { // pull the selected style... const StyleSelector& sel = *i; // if the selector uses an expression to select the style name, then we must perform the // query and then SORT the features into style groups. if ( sel.styleExpression().isSet() ) { // merge the selector's query into the existing query Query combinedQuery = baseQuery.combineWith( *sel.query() ); // query, sort, and add each style group to th parent: queryAndSortIntoStyleGroups( combinedQuery, *sel.styleExpression(), index, group ); } // otherwise, all feature returned by this query will have the same style: else { // combine the selection style with the incoming base style: Style selectedStyle = *styles->getStyle( sel.getSelectedStyleName() ); Style combinedStyle = defaultStyle.combineWith( selectedStyle ); // .. and merge it's query into the existing query Query combinedQuery = baseQuery.combineWith( *sel.query() ); // then create the node. osg::Group* styleGroup = createStyleGroup( combinedStyle, combinedQuery, index ); if ( styleGroup && !group->containsNode(styleGroup) ) group->addChild( styleGroup ); } } } // if no selectors are present, render all the features with a single style. else { Style combinedStyle = defaultStyle; // if there's no base style defined, choose a "default" style from the stylesheet. if ( defaultStyle.empty() ) combinedStyle = *styles->getDefaultStyle(); osg::Group* styleGroup = createStyleGroup( combinedStyle, baseQuery, index ); if ( styleGroup && !group->containsNode(styleGroup) ) group->addChild( styleGroup ); } } return group->getNumChildren() > 0 ? group.release() : 0L; }
osg::Node* MGRSGraticule::buildSQIDTiles( const std::string& gzd ) { const GeoExtent& extent = _gzd[gzd]; // parse the GZD into its components: unsigned zone; char letter; sscanf( gzd.c_str(), "%u%c", &zone, &letter ); TextSymbol* textSym = _options->secondaryStyle()->get<TextSymbol>(); if ( !textSym ) textSym = _options->primaryStyle()->getOrCreate<TextSymbol>(); AltitudeSymbol* alt = _options->secondaryStyle()->get<AltitudeSymbol>(); double h = 0.0; TextSymbolizer ts( textSym ); MGRSFormatter mgrs(MGRSFormatter::PRECISION_100000M); osg::Geode* textGeode = new osg::Geode(); textGeode->getOrCreateStateSet()->setRenderBinDetails( 9999, "DepthSortedBin" ); textGeode->getOrCreateStateSet()->setAttributeAndModes( _depthAttribute, 1 ); const SpatialReference* ecefSRS = extent.getSRS()->getECEF(); osg::Vec3d centerMap, centerECEF; extent.getCentroid(centerMap.x(), centerMap.y()); extent.getSRS()->transform(centerMap, ecefSRS, centerECEF); //extent.getSRS()->transformToECEF(centerMap, centerECEF); osg::Matrix local2world; ecefSRS->createLocalToWorld( centerECEF, local2world ); //= ECEF::createLocalToWorld(centerECEF); osg::Matrix world2local; world2local.invert(local2world); FeatureList features; std::vector<GeoExtent> sqidExtents; // UTM: if ( letter > 'B' && letter < 'Y' ) { // grab the SRS for the current UTM zone: // TODO: AL/AA designation?? const SpatialReference* utm = SpatialReference::create( Stringify() << "+proj=utm +zone=" << zone << " +north +units=m" ); // transform the four corners of the tile to UTM. osg::Vec3d gzdUtmSW, gzdUtmSE, gzdUtmNW, gzdUtmNE; extent.getSRS()->transform( osg::Vec3d(extent.xMin(),extent.yMin(),h), utm, gzdUtmSW ); extent.getSRS()->transform( osg::Vec3d(extent.xMin(),extent.yMax(),h), utm, gzdUtmNW ); extent.getSRS()->transform( osg::Vec3d(extent.xMax(),extent.yMin(),h), utm, gzdUtmSE ); extent.getSRS()->transform( osg::Vec3d(extent.xMax(),extent.yMax(),h), utm, gzdUtmNE ); // find the southern boundary of the first full SQID tile in the GZD tile. double southSQIDBoundary = gzdUtmSW.y(); //extentUTM.yMin(); double remainder = fmod( southSQIDBoundary, 100000.0 ); if ( remainder > 0.0 ) southSQIDBoundary += (100000.0 - remainder); // find the min/max X for this cell in UTM: double xmin = extent.yMin() >= 0.0 ? gzdUtmSW.x() : gzdUtmNW.x(); double xmax = extent.yMin() >= 0.0 ? gzdUtmSE.x() : gzdUtmNE.x(); // Record the UTM extent of each SQID cell in this tile. // Go from the south boundary northwards: for( double y = southSQIDBoundary; y < gzdUtmNW.y(); y += 100000.0 ) { // start at the central meridian (500K) and go west: for( double x = 500000.0; x > xmin; x -= 100000.0 ) { sqidExtents.push_back( GeoExtent(utm, x-100000.0, y, x, y+100000.0) ); } // then start at the central meridian and go east: for( double x = 500000.0; x < xmax; x += 100000.0 ) { sqidExtents.push_back( GeoExtent(utm, x, y, x+100000.0, y+100000.0) ); } } for( std::vector<GeoExtent>::iterator i = sqidExtents.begin(); i != sqidExtents.end(); ++i ) { GeoExtent utmEx = *i; // now, clamp each of the points in the UTM SQID extent to the map-space // boundaries of the GZD tile. (We only need to clamp in the X dimension, // Y geometry is allowed to overflow.) Also, skip NE, we don't need it. double r, xlimit; osg::Vec3d sw(utmEx.xMin(), utmEx.yMin(), 0); r = (sw.y()-gzdUtmSW.y())/(gzdUtmNW.y()-gzdUtmSW.y()); xlimit = gzdUtmSW.x() + r * (gzdUtmNW.x() - gzdUtmSW.x()); if ( sw.x() < xlimit ) sw.x() = xlimit; osg::Vec3d nw(utmEx.xMin(), utmEx.yMax(), 0); r = (nw.y()-gzdUtmSW.y())/(gzdUtmNW.y()-gzdUtmSW.y()); xlimit = gzdUtmSW.x() + r * (gzdUtmNW.x() - gzdUtmSW.x()); if ( nw.x() < xlimit ) nw.x() = xlimit; osg::Vec3d se(utmEx.xMax(), utmEx.yMin(), 0); r = (se.y()-gzdUtmSE.y())/(gzdUtmNE.y()-gzdUtmSE.y()); xlimit = gzdUtmSE.x() + r * (gzdUtmNE.x() - gzdUtmSE.x()); if ( se.x() > xlimit ) se.x() = xlimit; // at the northernmost GZD (lateral band X), clamp the northernmost SQIDs to the upper latitude. if ( letter == 'X' && nw.y() > gzdUtmNW.y() ) nw.y() = gzdUtmNW.y(); // need this in order to calculate the font size: double utmWidth = se.x() - sw.x(); // now transform the corner points back into the map SRS: utm->transform( sw, extent.getSRS(), sw ); utm->transform( nw, extent.getSRS(), nw ); utm->transform( se, extent.getSRS(), se ); // and draw valid sqid geometry. if ( sw.x() < se.x() ) { Feature* lat = new Feature(new LineString(2), extent.getSRS()); lat->geoInterp() = GEOINTERP_RHUMB_LINE; lat->getGeometry()->push_back( sw ); lat->getGeometry()->push_back( se ); features.push_back(lat); Feature* lon = new Feature(new LineString(2), extent.getSRS()); lon->geoInterp() = GEOINTERP_GREAT_CIRCLE; lon->getGeometry()->push_back( sw ); lon->getGeometry()->push_back( nw ); features.push_back(lon); // and the text label: osg::Vec3d sqidTextMap = (nw + se) * 0.5; sqidTextMap.z() += 1000.0; osg::Vec3d sqidTextECEF; extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF); //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF); osg::Vec3d sqidLocal; sqidLocal = sqidTextECEF * world2local; MGRSCoord mgrsCoord; if ( mgrs.transform( GeoPoint(extent.getSRS(),sqidTextMap,ALTMODE_ABSOLUTE), mgrsCoord) ) { textSym->size() = utmWidth/3.0; osgText::Text* d = ts.create( mgrsCoord.sqid ); osg::Matrixd textLocal2World; ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World ); d->setPosition( sqidLocal ); textGeode->addDrawable( d ); } } } } else if ( letter == 'A' || letter == 'B' ) { // SRS for south polar region UPS projection. This projection has (0,0) at the // south pole, with +X extending towards 90 degrees E longitude and +Y towards // 0 degrees longitude. const SpatialReference* ups = SpatialReference::create( "+proj=stere +lat_ts=-90 +lat_0=-90 +lon_0=0 +k_0=1 +x_0=0 +y_0=0"); osg::Vec3d gtemp; double r = GeoMath::distance(-osg::PI_2, 0.0, -1.3962634, 0.0); // -90 => -80 latitude double r2 = r*r; if ( letter == 'A' ) { for( double x = 0.0; x < 1200000.0; x += 100000.0 ) { double yminmax = sqrt( r2 - x*x ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(-x, -yminmax, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d(-x, yminmax, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double y = -1100000.0; y < 1200000.0; y += 100000.0 ) { double xmax = sqrt( r2 - y*y ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(-xmax, y, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d( 0, y, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double x = -1200000.0; x < 0.0; x += 100000.0 ) { for( double y = -1200000.0; y < 1200000.0; y += 100000.0 ) { osg::Vec3d sqidTextMap; ups->transform( osg::Vec3d(x+50000.0, y+50000.0, 0), extent.getSRS(), sqidTextMap); if ( sqidTextMap.y() < -80.0 ) { sqidTextMap.z() += 1000.0; osg::Vec3d sqidTextECEF; extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF); //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF); osg::Vec3d sqidLocal = sqidTextECEF * world2local; MGRSCoord mgrsCoord; if ( mgrs.transform( GeoPoint(extent.getSRS(),sqidTextMap,ALTMODE_ABSOLUTE), mgrsCoord) ) { textSym->size() = 33000.0; osgText::Text* d = ts.create( mgrsCoord.sqid ); osg::Matrixd textLocal2World; ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World ); d->setPosition( sqidLocal ); textGeode->addDrawable( d ); } } } } } else if ( letter == 'B' ) { for( double x = 100000.0; x < 1200000.0; x += 100000.0 ) { double yminmax = sqrt( r2 - x*x ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(x, -yminmax, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d(x, yminmax, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double y = -1100000.0; y < 1200000.0; y += 100000.0 ) { double xmax = sqrt( r2 - y*y ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d( 0, y, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d( xmax, y, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double x = 0.0; x < 1200000.0; x += 100000.0 ) { for( double y = -1200000.0; y < 1200000.0; y += 100000.0 ) { osg::Vec3d sqidTextMap; ups->transform( osg::Vec3d(x+50000.0, y+50000.0, 0), extent.getSRS(), sqidTextMap); if ( sqidTextMap.y() < -80.0 ) { sqidTextMap.z() += 1000.0; osg::Vec3d sqidTextECEF; extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF); //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF); osg::Vec3d sqidLocal = sqidTextECEF * world2local; MGRSCoord mgrsCoord; if ( mgrs.transform( GeoPoint(extent.getSRS(),sqidTextMap,ALTMODE_ABSOLUTE), mgrsCoord) ) { textSym->size() = 33000.0; osgText::Text* d = ts.create( mgrsCoord.sqid ); osg::Matrixd textLocal2World; ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World ); d->setPosition( sqidLocal ); textGeode->addDrawable( d ); } } } } } } else if ( letter == 'Y' || letter == 'Z' ) { // SRS for north polar region UPS projection. This projection has (0,0) at the // south pole, with +X extending towards 90 degrees E longitude and +Y towards // 180 degrees longitude. const SpatialReference* ups = SpatialReference::create( "+proj=stere +lat_ts=90 +lat_0=90 +lon_0=0 +k_0=1 +x_0=0 +y_0=0"); osg::Vec3d gtemp; double r = GeoMath::distance(osg::PI_2, 0.0, 1.46607657, 0.0); // 90 -> 84 latitude double r2 = r*r; if ( letter == 'Y' ) { for( double x = 0.0; x < 700000.0; x += 100000.0 ) { double yminmax = sqrt( r2 - x*x ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(-x, -yminmax, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d(-x, yminmax, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double y = -600000.0; y < 700000.0; y += 100000.0 ) { double xmax = sqrt( r2 - y*y ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(-xmax, y, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d( 0, y, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double x = -700000.0; x < 0.0; x += 100000.0 ) { for( double y = -700000.0; y < 700000.0; y += 100000.0 ) { osg::Vec3d sqidTextMap; ups->transform( osg::Vec3d(x+50000.0, y+50000.0, 0), extent.getSRS(), sqidTextMap); if ( sqidTextMap.y() > 84.0 ) { sqidTextMap.z() += 1000.0; osg::Vec3d sqidTextECEF; extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF); //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF); osg::Vec3d sqidLocal = sqidTextECEF * world2local; MGRSCoord mgrsCoord; if ( mgrs.transform( GeoPoint(extent.getSRS(),sqidTextMap,ALTMODE_ABSOLUTE), mgrsCoord) ) { textSym->size() = 33000.0; osgText::Text* d = ts.create( mgrsCoord.sqid ); osg::Matrixd textLocal2World; ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World ); d->setPosition( sqidLocal ); textGeode->addDrawable( d ); } } } } } else if ( letter == 'Z' ) { for( double x = 100000.0; x < 700000.0; x += 100000.0 ) { double yminmax = sqrt( r2 - x*x ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d(x, -yminmax, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d(x, yminmax, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double y = -600000.0; y < 700000.0; y += 100000.0 ) { double xmax = sqrt( r2 - y*y ); Feature* f = new Feature( new LineString(2), extent.getSRS() ); f->geoInterp() = GEOINTERP_GREAT_CIRCLE; osg::Vec3d p0, p1; ups->transform( osg::Vec3d( 0, y, 0), extent.getSRS(), p0 ); ups->transform( osg::Vec3d( xmax, y, 0), extent.getSRS(), p1 ); f->getGeometry()->push_back( p0 ); f->getGeometry()->push_back( p1 ); features.push_back( f ); } for( double x = 0.0; x < 700000.0; x += 100000.0 ) { for( double y = -700000.0; y < 700000.0; y += 100000.0 ) { osg::Vec3d sqidTextMap; ups->transform( osg::Vec3d(x+50000.0, y+50000.0, 0), extent.getSRS(), sqidTextMap); if ( sqidTextMap.y() > 84.0 ) { sqidTextMap.z() += 1000.0; osg::Vec3d sqidTextECEF; extent.getSRS()->transform(sqidTextMap, ecefSRS, sqidTextECEF); //extent.getSRS()->transformToECEF(sqidTextMap, sqidTextECEF); osg::Vec3d sqidLocal = sqidTextECEF * world2local; MGRSCoord mgrsCoord; if ( mgrs.transform( GeoPoint(extent.getSRS(),sqidTextMap,ALTMODE_ABSOLUTE), mgrsCoord) ) { textSym->size() = 33000.0; osgText::Text* d = ts.create( mgrsCoord.sqid ); osg::Matrixd textLocal2World; ecefSRS->createLocalToWorld( sqidTextECEF, textLocal2World ); d->setPosition( sqidLocal ); textGeode->addDrawable( d ); } } } } } } osg::Group* group = new osg::Group(); Style lineStyle; lineStyle.add( _options->secondaryStyle()->get<LineSymbol>() ); lineStyle.add( _options->secondaryStyle()->get<AltitudeSymbol>() ); GeometryCompiler compiler; osg::ref_ptr<Session> session = new Session( getMapNode()->getMap() ); FilterContext context( session.get(), _featureProfile.get(), extent ); // make sure we get sufficient tessellation: compiler.options().maxGranularity() = 0.25; osg::Node* geomNode = compiler.compile(features, lineStyle, context); if ( geomNode ) group->addChild( geomNode ); osg::MatrixTransform* mt = new osg::MatrixTransform(local2world); mt->addChild(textGeode); group->addChild( mt ); // prep for depth offset: DepthOffsetUtils::prepareGraph( group ); group->getOrCreateStateSet()->addUniform( _minDepthOffset.get() ); return group; }