예제 #1
0
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;
}
예제 #2
0
// 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() );
        }
    }
}
예제 #3
0
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;
}
예제 #4
0
void
FeatureCursor::fill( FeatureList& list )
{
    while( hasMore() )
    {
        list.push_back( nextFeature() );
    }
}
예제 #5
0
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);
}
예제 #7
0
      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 );        
      }
예제 #8
0
    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);
		}
	}

}
예제 #10
0
      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 );
}
예제 #12
0
    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;
    }
예제 #13
0
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();
}
예제 #14
0
    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() );            
    }
예제 #15
0
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);
}
예제 #16
0
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;
}
예제 #17
0
    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;
    }
예제 #18
0
// 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;
}
예제 #19
0
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;
}
예제 #21
0
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;
    }
}
예제 #23
0
    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
    }
예제 #24
0
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();
}
예제 #25
0
    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();
    }
예제 #27
0
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() );
            }
        }
    }
}
예제 #28
0
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;
}
예제 #29
0
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;
}
예제 #30
0
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;
}