osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthUtil/SpatialData.cpp

Go to the documentation of this file.
00001 /* -*-c++-*- */
00002 /* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
00003  * Copyright 2008-2010 Pelican Mapping
00004  * http://osgearth.org
00005  *
00006  * osgEarth is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU Lesser General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU Lesser General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU Lesser General Public License
00017  * along with this program.  If not, see <http://www.gnu.org/licenses/>
00018  */
00019 #include <osgEarthUtil/SpatialData>
00020 #include <osgUtil/CullVisitor>
00021 #include <osg/PolygonOffset>
00022 #include <osg/Polytope>
00023 #include <osg/Geometry>
00024 #include <osg/Depth>
00025 #include <osgText/Text>
00026 #include <sstream>
00027 
00028 #define LC "[GeoGraph] "
00029 
00030 using namespace osgEarth;
00031 using namespace osgEarth::Util;
00032 
00033 //------------------------------------------------------------------------
00034 
00035 namespace
00036 {
00037     unsigned getIndex( const GeoExtent& cellExtent, const osg::Vec3d& point, unsigned xdim, unsigned ydim )
00038     {
00039         double cw = cellExtent.width() / (double)xdim;
00040         double ch = cellExtent.height() / (double)ydim;
00041 
00042         unsigned col = osg::clampBelow( (unsigned)((point.x()-cellExtent.xMin())/cw), xdim-1 );
00043         unsigned row = osg::clampBelow( (unsigned)((point.y()-cellExtent.yMin())/ch), ydim-1 );
00044 
00045         return row*xdim + col;
00046     }
00047 
00048     static osgText::Font* s_font = 0L;
00049 
00050     osg::Geode* makeClusterGeode( const GeoExtent& cellExtent, unsigned num )
00051     {
00052         osgText::Text* t = new osgText::Text();
00053 
00054         double clat, clon;
00055         cellExtent.getCentroid( clon, clat );
00056         osg::Vec3d xyz;        
00057         cellExtent.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ(
00058             osg::DegreesToRadians( clat ), osg::DegreesToRadians( clon ), 0, xyz.x(), xyz.y(), xyz.z() );
00059         t->setPosition( xyz );
00060         
00061         std::stringstream buf;
00062         buf << num;
00063         t->setText( buf.str() );
00064         t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS );
00065         t->setCharacterSize( 22.0f );
00066         t->setAutoRotateToScreen( true );
00067 
00068         if ( !s_font )
00069             s_font = osgText::readFontFile( "arialbd.ttf" );
00070         t->setFont( s_font );
00071 
00072         t->setBackdropType( osgText::Text::OUTLINE );
00073         t->setColor( osg::Vec4(1,1,1,1) );
00074         t->setBackdropColor( osg::Vec4(0,0,0,1) );
00075 
00076         osg::Geode* geode = new osg::Geode();
00077         geode->addDrawable( t );
00078 
00079         osg::StateSet* s = geode->getOrCreateStateSet();
00080         s->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS) );
00081 
00082         t->setDataVariance( osg::Object::DYNAMIC );
00083 
00084         return geode;
00085     }
00086 
00087     GeoObjectCollection::iterator
00088     findObject( GeoObjectCollection& objects, GeoObject* object )
00089     {
00090         float key = object->getPriority();
00091         GeoObjectCollection::iterator first = objects.find(key);
00092         if ( first == objects.end() )
00093             return objects.end();
00094 
00095         GeoObjectCollection::iterator last = objects.upper_bound(key);
00096         for( ; first != last; ++first )
00097             if ( first->second.get() == object )
00098                 return first;
00099 
00100         return objects.end();
00101     }
00102 }
00103 
00104 //------------------------------------------------------------------------
00105 
00106 GeoObject::GeoObject()
00107 {
00108     //NOP
00109 }
00110 
00111 //------------------------------------------------------------------------
00112 
00113 GeoGraph::GeoGraph(const GeoExtent& extent, float maxRange, unsigned maxObjects,
00114                    unsigned splitDim, float splitRangeFactor,
00115                    unsigned rootWidth, unsigned rootHeight ) :
00116 GeoCell( extent, maxRange, maxObjects, splitDim, splitRangeFactor, 0 )
00117 {
00118     _rootWidth = osg::maximum( rootWidth, (unsigned)2 );
00119     _rootHeight = osg::maximum( rootHeight, (unsigned)2 );
00120 
00121     if ( _depth == 0 )
00122     {
00123         double xinterval = extent.width() / (double)_rootWidth;
00124         double yinterval = extent.height() / (double)_rootHeight;
00125 
00126         for( unsigned y=0; y<_rootHeight; ++y )
00127         {
00128             for( unsigned x=0; x<_rootWidth; ++x )
00129             {
00130                 GeoExtent cellExtent(
00131                     _extent.getSRS(),
00132                     _extent.xMin() + xinterval*(double)x,
00133                     _extent.yMin() + yinterval*(double)y,
00134                     _extent.xMin() + xinterval*(double)(x+1),
00135                     _extent.yMin() + yinterval*(double)(y+1) );
00136 
00137                 GeoCell * child = new GeoCell(
00138                     cellExtent,
00139                     _maxRange,
00140                     _maxObjects,
00141                     _splitDim,
00142                     _splitRangeFactor,
00143                     1 );
00144 
00145                 this->addChild( child, 0, maxRange ); //FLT_MAX );
00146             }
00147         }                    
00148     }
00149 }
00150 
00151 bool
00152 GeoGraph::insertObject( GeoObject* object )
00153 {
00154     osg::Vec3d loc;
00155     if ( object->getLocation(loc) )
00156     {
00157         unsigned index = getIndex( _extent, loc, _rootWidth, _rootHeight );
00158         return static_cast<GeoCell*>(getChild(index))->insertObject( object );
00159     }
00160     else
00161     {
00162         return false;
00163     }
00164 }
00165 
00166 //------------------------------------------------------------------------
00167 
00168 GeoCell::GeoCell(const GeoExtent& extent, float maxRange, unsigned maxObjects,
00169                  unsigned splitDim, float splitRangeFactor, unsigned depth ) :
00170 _extent( extent ),
00171 _maxRange( maxRange ),
00172 _maxObjects( maxObjects ),
00173 _splitDim( splitDim ),
00174 _splitRangeFactor( splitRangeFactor ),
00175 _depth( depth ),
00176 _minObjects( (maxObjects/10)*8 ), // 80%
00177 _count( 0 ),
00178 _boundaryPoints( 10 ),
00179 _frameStamp( 0 )
00180 {
00181     generateBoundaries();
00182     //generateBoundaryGeometry();
00183 
00184     // Disable OSG's culling so we can do our own custom culling.
00185     this->setCullingActive( false );
00186 }
00187 
00188 void
00189 GeoCell::generateBoundaries()
00190 {
00191     const osg::EllipsoidModel* em = _extent.getSRS()->getEllipsoid();
00192     static const double hae =  1e6;
00193     static const double hbe = -1e5;
00194 
00195     // find the geodetic center:
00196     osg::Vec3d gcenter;
00197     _extent.getCentroid( gcenter.x(), gcenter.y() );
00198 
00199     // convert to a geocentric vector:
00200     osg::Vec3d center;
00201     em->convertLatLongHeightToXYZ(
00202         osg::DegreesToRadians(gcenter.y()), osg::DegreesToRadians(gcenter.x()), 0.0, center.x(), center.y(), center.z());
00203 
00204     osg::Vec3d centerVec = center;
00205     centerVec.normalize();
00206 
00207     // find the 4 geodetic corners:
00208     osg::Vec3d gcorner[4];
00209     gcorner[0].set( _extent.xMin(), _extent.yMin(), 0.0 );
00210     gcorner[1].set( _extent.xMin(), _extent.yMax(), 0.0 );
00211     gcorner[2].set( _extent.xMax(), _extent.yMax(), 0.0 );
00212     gcorner[3].set( _extent.xMax(), _extent.yMin(), 0.0 );
00213 
00214     // and convert those to geocentric vectors:
00215     osg::Vec3d corner[4];
00216     osg::Vec3d cornerVec[4];
00217     for(unsigned i=0; i<4; ++i )
00218     {
00219         em->convertLatLongHeightToXYZ(
00220             osg::DegreesToRadians(gcorner[i].y()), osg::DegreesToRadians(gcorner[i].x()), 0.0,
00221             corner[i].x(), corner[i].y(), corner[i].z() );
00222         cornerVec[i] = corner[i];
00223         cornerVec[i].normalize();
00224     }   
00225     
00226     // now extrude the center and corners up and down to get the boundary points:
00227     unsigned p = 0;
00228     _boundaryPoints[p++] = center + centerVec*hae;
00229     _boundaryPoints[p++] = center +centerVec*hbe;
00230     for( unsigned i=0; i<4; ++i )
00231     {
00232         _boundaryPoints[p++] = corner[i] + cornerVec[i]*hae;
00233         _boundaryPoints[p++] = corner[i] + cornerVec[i]*hbe;
00234     }
00235 }
00236 
00237 osg::BoundingSphere
00238 GeoCell::computeBound() const
00239 {
00240     osg::BoundingSphere bs;
00241     for( unsigned i=0; i<10; ++i )
00242         bs.expandBy( _boundaryPoints[i] );
00243     return bs;
00244 }
00245 
00246 void
00247 GeoCell::generateBoundaryGeometry()
00248 {
00249     osg::Geometry* g = new osg::Geometry();
00250 
00251     osg::Vec3Array* v = new osg::Vec3Array(10);
00252     for( unsigned i=0; i<10; ++i )
00253         (*v)[i] = _boundaryPoints[i];
00254     g->setVertexArray( v );
00255 
00256     osg::DrawElementsUByte* el = new osg::DrawElementsUByte( GL_QUADS );
00257     el->push_back( 7 ); el->push_back( 5 ); el->push_back( 4 ); el->push_back( 6 );
00258     el->push_back( 9 ); el->push_back( 7 ); el->push_back( 6 ); el->push_back( 8 );
00259     el->push_back( 3 ); el->push_back( 9 ); el->push_back( 8 ); el->push_back( 2 );
00260     el->push_back( 5 ); el->push_back( 3 ); el->push_back( 2 ); el->push_back( 4 );
00261     //el->push_back( 2 ); el->push_back( 8 ); el->push_back( 6 ); el->push_back( 4 ); //top
00262     //el->push_back( 9 ); el->push_back( 3 ); el->push_back( 5 ); el->push_back( 7 ); // bottom
00263     g->addPrimitiveSet( el );
00264 
00265     osg::Vec4Array* c = new osg::Vec4Array(1);
00266     (*c)[0].set( 1, 1, 1, 0.25 );
00267     g->setColorArray( c );
00268     g->setColorBinding( osg::Geometry::BIND_OVERALL );
00269 
00270     _boundaryColor = c;
00271 
00272     g->setDataVariance( osg::Object::DYNAMIC );
00273     g->setUseDisplayList( false );
00274     g->setUseVertexBufferObjects( true );
00275 
00276     osg::StateSet* set = g->getOrCreateStateSet();
00277     set->setMode( GL_BLEND, 1 );
00278     set->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
00279     set->setAttribute( new osg::PolygonOffset(1,1), 1 );
00280 
00281     _boundaryGeode = new osg::Geode();
00282     _boundaryGeode->addDrawable( g );
00283 }
00284 
00285 bool
00286 GeoCell::intersects( const osg::Polytope& tope ) const
00287 {
00288     const osg::Polytope::PlaneList& planes = tope.getPlaneList();
00289     for( osg::Polytope::PlaneList::const_iterator i = planes.begin(); i != planes.end(); ++i )
00290     {
00291         if ( i->intersect( _boundaryPoints ) < 0 )
00292             return false;
00293     }
00294     return true;
00295 }
00296 
00297 void
00298 GeoCell::traverse( osg::NodeVisitor& nv )
00299 {
00300     bool isCull = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
00301 
00302     if ( _depth > 0 )
00303     {
00304         if ( isCull )
00305         {
00306             // process boundary geometry, if present.
00307             if ( _boundaryGeode.valid() ) 
00308             {
00309                 if ( _count > 0 )
00310                     (*_boundaryColor)[0].set(1,0,0,0.35);
00311                 else
00312                     (*_boundaryColor)[0].set(1,1,1,0.25);
00313                 _boundaryColor->dirty();
00314 
00315                 _boundaryGeode->accept( nv );
00316             }
00317 
00318             // custom BSP culling function. this checks that the set of boundary points
00319             // for this cell intersects the viewing frustum.
00320             osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
00321             if ( cv && !intersects( cv->getCurrentCullingSet().getFrustum() ) )
00322             {
00323                 return;
00324             }
00325 
00326             // passed cull, so record the framestamp.
00327             _frameStamp = cv->getFrameStamp()->getFrameNumber();
00328         }
00329 
00330         if ( _objects.size() > 0 )
00331         {
00332             for( GeoObjectCollection::iterator i = _objects.begin(); i != _objects.end(); ++i )
00333             {
00334                 osg::Node* node = i->second->getNode();
00335                 if ( node )
00336                     node->accept( nv );
00337             }
00338         }
00339 
00340         if ( _clusterGeode.valid() )
00341             _clusterGeode->accept( nv );
00342     }
00343 
00344     else
00345     {
00346         if ( isCull )
00347             _frameStamp = nv.getFrameStamp()->getFrameNumber();
00348     }
00349 
00350     osg::LOD::traverse( nv );
00351 }
00352 
00353 void
00354 GeoCell::adjustCount( int delta )
00355 {
00356     _count += delta;
00357 
00358     if ( _depth > 0 && getNumParents() > 0 )
00359     {
00360         static_cast<GeoCell*>(getParent(0))->adjustCount( delta );        
00361 
00362 #if 0
00363         if ( !_clusterGeode.valid() )
00364         {
00365             _clusterGeode = makeClusterGeode( _extent, _count );
00366         }
00367         else
00368         {
00369             osgText::Text* t = static_cast<osgText::Text*>( _clusterGeode->getDrawable(0) );
00370             std::stringstream buf;
00371             buf << _count;
00372             t->setText( buf.str() );
00373         }
00374 #endif
00375     }
00376 }
00377 
00378 bool
00379 GeoCell::insertObject( GeoObject* object )
00380 {
00381     osg::Vec3d location;
00382     if ( object->getLocation(location) && _extent.contains(location.x(), location.y()) )
00383     {
00384         object->_cell = this;
00385         _objects.insert( std::make_pair(object->getPriority(), object) );
00386 
00387         if ( _objects.size() > _maxObjects )
00388         {
00389             GeoObjectCollection::iterator low = _objects.begin();
00390             GeoObject* lowPriObject = low->second.get();
00391             
00392             if ( getNumChildren() == 0 )
00393                 split();
00394 
00395             lowPriObject->getLocation(location);
00396             unsigned index = getIndex(_extent, location, _splitDim, _splitDim);
00397             bool insertedOK = static_cast<GeoCell*>(getChild(index))->insertObject( lowPriObject );
00398             if ( insertedOK )
00399             {
00400                 // remove it from this cell.
00401                 _objects.erase( low );
00402             }
00403             else
00404             {
00405                 // should never ever happen..
00406                 OE_WARN << LC << "Object insertion failed" << std::endl;
00407                 return false;
00408             }
00409         }
00410         return true;
00411     }
00412     else
00413     {
00414         return false;
00415     }
00416 }
00417 
00418 void
00419 GeoCell::split()
00420 {
00421     // the max LOD range for children of this cell:
00422     float newRange = _maxRange * _splitRangeFactor;
00423 
00424     // first create all the child cells:
00425     double xInterval = _extent.width() / (double)_splitDim;
00426     double yInterval = _extent.height() / (double)_splitDim;
00427 
00428     for( unsigned row=0; row<_splitDim; ++row )
00429     {
00430         for( unsigned col=0; col<_splitDim; ++col )
00431         {
00432             GeoExtent cellExtent(
00433                 _extent.getSRS(),
00434                 _extent.xMin() + xInterval*(double)col,
00435                 _extent.yMin() + yInterval*(double)row,
00436                 _extent.xMin() + xInterval*(double)(col+1),
00437                 _extent.yMin() + yInterval*(double)(row+1) );
00438 
00439             this->addChild(
00440                 new GeoCell(cellExtent, newRange, _maxObjects, _splitDim, _splitRangeFactor, _depth+1),
00441                 0.0f,
00442                 newRange );
00443         }
00444     }
00445 }
00446 
00447 bool
00448 GeoCell::removeObject( GeoObject* object )
00449 {
00450     if ( object->_cell.get() == this )
00451     {
00452         object->_cell = 0L;
00453         _objects.erase( findObject(_objects, object) );
00454         adjustCount( -1 );
00455 
00456         // if we just fell beneath the threshold, pull one up from below.
00457         if ( _objects.size() == _minObjects-1 )
00458         {
00459             //TODO.
00460         }
00461 
00462         // TODO: rebalance, merge the tree, etc.
00463         return true;
00464     }
00465     else
00466     {
00467         for( unsigned i=0; i<getNumChildren(); ++i )
00468         {
00469             if ( static_cast<GeoCell*>(getChild(i))->removeObject( object ) )
00470                 return true;
00471         }
00472     }
00473     return false;
00474 }
00475 
00476 void
00477 GeoCell::merge()
00478 {
00479     //NYI //TODO
00480 }
00481 
00482 bool
00483 GeoCell::reindexObject( GeoObject* object )
00484 {
00485     GeoCell* owner = object->getGeoCell();
00486     if ( owner )
00487     {
00488         osg::Vec3d location;
00489         if ( object->getLocation(location) && !owner->_extent.contains(location.x(), location.y()) )
00490         {
00491             // first remove from its current cell
00492             owner->removeObject( object );
00493 
00494             GeoCell* cell = dynamic_cast<GeoCell*>( owner->getParent(0) );
00495             while( cell )
00496             {
00497                 if ( cell->getExtent().contains(location.x(), location.y()) )
00498                 {
00499                     if ( cell->insertObject( object ) )
00500                         return true;
00501                 }
00502                 cell = dynamic_cast<GeoCell*>( cell->getParent(0) );
00503             }
00504         }
00505 
00506         // no change
00507         return true;
00508     }
00509     else
00510     {
00511         return insertObject( object );
00512     }
00513 }
00514 
00515 #if 0
00516 bool
00517 GeoCell::reindex( GeoObject* object )
00518 {
00519     osg::Vec3d location;
00520     if ( object->getLocation(location) && !_extent.contains(location.x(), location.y()) )
00521     {
00522         // first remove from its current cell
00523         osg::ref_ptr<GeoCell> safeCell = object->_cell.get();
00524         if ( safeCell.valid() )
00525         {
00526             object->_cell = 0L;
00527             safeCell->_objects.erase( findObject(safeCell->_objects, object) );
00528             //safeCell->_objects.erase( std::find( _objects.begin(), _objects.end(), std::make_pair(object->getPriority(),object) ) );
00529             //safeCell->_objects.erase( std::find( safeCell->_objects.begin(), safeCell->_objects.end(), object ) );
00530             safeCell->adjustCount( -1 );
00531             //safeCell->removeObject( object );
00532         }
00533 
00534         GeoCell* cell = dynamic_cast<GeoCell*>( this->getParent(0) );
00535         while( cell )
00536         {
00537             if ( cell->getExtent().contains(location.x(), location.y()) )
00538             {
00539                 if ( cell->insertObject( object ) )
00540                     return true;
00541             }
00542             cell = dynamic_cast<GeoCell*>( cell->getParent(0) );
00543         }
00544     }
00545 
00546     return true;
00547 }
00548 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines