2
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
3
* Copyright 2008-2010 Pelican Mapping
6
* osgEarth is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU Lesser General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU Lesser General Public License for more details.
16
* You should have received a copy of the GNU Lesser General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>
19
#include <osgEarthUtil/SpatialData>
20
#include <osgUtil/CullVisitor>
21
#include <osg/PolygonOffset>
22
#include <osg/Polytope>
23
#include <osg/Geometry>
25
#include <osgText/Text>
27
#define LC "[GeoGraph] "
29
using namespace osgEarth;
30
using namespace osgEarth::Util;
32
//------------------------------------------------------------------------
36
unsigned getIndex( const GeoExtent& cellExtent, const osg::Vec3d& point, unsigned xdim, unsigned ydim )
38
unsigned col = osg::clampBelow( (unsigned)((double)xdim * ((point.x() - cellExtent.xMin()) / cellExtent.width())), xdim-1 );
39
unsigned row = osg::clampBelow( (unsigned)((double)ydim * ((point.y() - cellExtent.yMin()) / cellExtent.height())), ydim-1 );
40
return row*xdim + col;
43
static osgText::Font* s_font = 0L;
45
osg::Geode* makeClusterGeode( const GeoExtent& cellExtent, unsigned num )
47
osgText::Text* t = new osgText::Text();
50
cellExtent.getCentroid( clon, clat );
52
cellExtent.getSRS()->getEllipsoid()->convertLatLongHeightToXYZ(
53
osg::DegreesToRadians( clat ), osg::DegreesToRadians( clon ), 0, xyz.x(), xyz.y(), xyz.z() );
54
t->setPosition( xyz );
56
std::stringstream buf;
58
t->setText( buf.str() );
59
t->setCharacterSizeMode( osgText::TextBase::SCREEN_COORDS );
60
t->setCharacterSize( 22.0f );
61
t->setAutoRotateToScreen( true );
64
s_font = osgText::readFontFile( "arialbd.ttf" );
67
t->setBackdropType( osgText::Text::OUTLINE );
68
t->setColor( osg::Vec4(1,1,1,1) );
69
t->setBackdropColor( osg::Vec4(0,0,0,1) );
71
osg::Geode* geode = new osg::Geode();
72
geode->addDrawable( t );
74
osg::StateSet* s = geode->getOrCreateStateSet();
75
s->setAttributeAndModes( new osg::Depth(osg::Depth::ALWAYS) );
77
t->setDataVariance( osg::Object::DYNAMIC );
82
GeoObjectCollection::iterator
83
findObject( GeoObjectCollection& objects, GeoObject* object )
85
float key = object->getPriority();
86
GeoObjectCollection::iterator first = objects.find(key);
87
if ( first == objects.end() )
90
GeoObjectCollection::iterator last = objects.upper_bound(key);
91
for( ; first != last; ++first )
92
if ( first->second.get() == object )
99
//------------------------------------------------------------------------
101
GeoObject::GeoObject()
106
//------------------------------------------------------------------------
108
GeoGraph::GeoGraph(const GeoExtent& extent, float maxRange, unsigned maxObjects,
109
unsigned splitDim, float splitRangeFactor,
110
unsigned rootWidth, unsigned rootHeight ) :
111
GeoCell( extent, maxRange, maxObjects, splitDim, splitRangeFactor, 0 )
113
_rootWidth = osg::maximum( rootWidth, (unsigned)2 );
114
_rootHeight = osg::maximum( rootHeight, (unsigned)2 );
118
double xinterval = extent.width() / (double)_rootWidth;
119
double yinterval = extent.height() / (double)_rootHeight;
121
for( unsigned y=0; y<_rootHeight; ++y )
123
for( unsigned x=0; x<_rootWidth; ++x )
125
GeoExtent cellExtent(
127
_extent.xMin() + xinterval*(double)x,
128
_extent.yMin() + yinterval*(double)y,
129
_extent.xMin() + xinterval*(double)(x+1),
130
_extent.yMin() + yinterval*(double)(y+1) );
132
GeoCell * child = new GeoCell(
140
this->addChild( child, 0, maxRange ); //FLT_MAX );
147
GeoGraph::insertObject( GeoObject* object )
150
if ( object->getLocation(loc) )
152
unsigned index = getIndex( _extent, loc, _rootWidth, _rootHeight );
153
return static_cast<GeoCell*>(getChild(index))->insertObject( object );
161
//------------------------------------------------------------------------
163
GeoCell::GeoCell(const GeoExtent& extent, float maxRange, unsigned maxObjects,
164
unsigned splitDim, float splitRangeFactor, unsigned depth ) :
166
_maxRange( maxRange ),
167
_maxObjects( maxObjects ),
168
_splitDim( splitDim ),
169
_splitRangeFactor( splitRangeFactor ),
171
_minObjects( (maxObjects/10)*8 ), // 80%
173
_boundaryPoints( 10 ),
176
generateBoundaries();
177
//generateBoundaryGeometry();
179
// Disable OSG's culling so we can do our own custom culling.
180
this->setCullingActive( false );
184
GeoCell::generateBoundaries()
186
const osg::EllipsoidModel* em = _extent.getSRS()->getEllipsoid();
187
static const double hae = 1e6;
188
static const double hbe = -1e5;
190
// find the geodetic center:
192
_extent.getCentroid( gcenter.x(), gcenter.y() );
194
// convert to a geocentric vector:
196
em->convertLatLongHeightToXYZ(
197
osg::DegreesToRadians(gcenter.y()), osg::DegreesToRadians(gcenter.x()), 0.0, center.x(), center.y(), center.z());
199
osg::Vec3d centerVec = center;
200
centerVec.normalize();
202
// find the 4 geodetic corners:
203
osg::Vec3d gcorner[4];
204
gcorner[0].set( _extent.xMin(), _extent.yMin(), 0.0 );
205
gcorner[1].set( _extent.xMin(), _extent.yMax(), 0.0 );
206
gcorner[2].set( _extent.xMax(), _extent.yMax(), 0.0 );
207
gcorner[3].set( _extent.xMax(), _extent.yMin(), 0.0 );
209
// and convert those to geocentric vectors:
210
osg::Vec3d corner[4];
211
osg::Vec3d cornerVec[4];
212
for(unsigned i=0; i<4; ++i )
214
em->convertLatLongHeightToXYZ(
215
osg::DegreesToRadians(gcorner[i].y()), osg::DegreesToRadians(gcorner[i].x()), 0.0,
216
corner[i].x(), corner[i].y(), corner[i].z() );
217
cornerVec[i] = corner[i];
218
cornerVec[i].normalize();
221
// now extrude the center and corners up and down to get the boundary points:
223
_boundaryPoints[p++] = center + centerVec*hae;
224
_boundaryPoints[p++] = center +centerVec*hbe;
225
for( unsigned i=0; i<4; ++i )
227
_boundaryPoints[p++] = corner[i] + cornerVec[i]*hae;
228
_boundaryPoints[p++] = corner[i] + cornerVec[i]*hbe;
233
GeoCell::computeBound() const
235
osg::BoundingSphere bs;
236
for( unsigned i=0; i<10; ++i )
237
bs.expandBy( _boundaryPoints[i] );
242
GeoCell::generateBoundaryGeometry()
244
osg::Geometry* g = new osg::Geometry();
246
osg::Vec3dArray* v = new osg::Vec3dArray(10);
247
for( unsigned i=0; i<10; ++i )
248
(*v)[i] = _boundaryPoints[i];
249
g->setVertexArray( v );
251
osg::DrawElementsUByte* el = new osg::DrawElementsUByte( GL_QUADS );
252
el->push_back( 7 ); el->push_back( 5 ); el->push_back( 4 ); el->push_back( 6 );
253
el->push_back( 9 ); el->push_back( 7 ); el->push_back( 6 ); el->push_back( 8 );
254
el->push_back( 3 ); el->push_back( 9 ); el->push_back( 8 ); el->push_back( 2 );
255
el->push_back( 5 ); el->push_back( 3 ); el->push_back( 2 ); el->push_back( 4 );
256
//el->push_back( 2 ); el->push_back( 8 ); el->push_back( 6 ); el->push_back( 4 ); //top
257
//el->push_back( 9 ); el->push_back( 3 ); el->push_back( 5 ); el->push_back( 7 ); // bottom
258
g->addPrimitiveSet( el );
260
osg::Vec4Array* c = new osg::Vec4Array(1);
261
(*c)[0].set( 1, 1, 1, 0.25 );
262
g->setColorArray( c );
263
g->setColorBinding( osg::Geometry::BIND_OVERALL );
267
g->setDataVariance( osg::Object::DYNAMIC );
268
g->setUseDisplayList( false );
269
g->setUseVertexBufferObjects( true );
271
osg::StateSet* set = g->getOrCreateStateSet();
272
set->setMode( GL_BLEND, 1 );
273
set->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
274
set->setAttribute( new osg::PolygonOffset(1,1), 1 );
276
_boundaryGeode = new osg::Geode();
277
_boundaryGeode->addDrawable( g );
281
GeoCell::intersects( const osg::Polytope& tope ) const
283
const osg::Polytope::PlaneList& planes = tope.getPlaneList();
284
for( osg::Polytope::PlaneList::const_iterator i = planes.begin(); i != planes.end(); ++i )
286
if ( i->intersect( _boundaryPoints ) < 0 )
293
GeoCell::traverse( osg::NodeVisitor& nv )
295
bool isCull = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR;
301
// process boundary geometry, if present.
302
if ( _boundaryGeode.valid() )
305
(*_boundaryColor)[0].set(1,0,0,0.35);
307
(*_boundaryColor)[0].set(1,1,1,0.25);
308
_boundaryColor->dirty();
310
_boundaryGeode->accept( nv );
313
// custom BSP culling function. this checks that the set of boundary points
314
// for this cell intersects the viewing frustum.
315
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>( &nv );
316
if ( cv && !intersects( cv->getCurrentCullingSet().getFrustum() ) )
321
// passed cull, so record the framestamp.
322
_frameStamp = cv->getFrameStamp()->getFrameNumber();
325
if ( _objects.size() > 0 )
327
for( GeoObjectCollection::iterator i = _objects.begin(); i != _objects.end(); ++i )
329
osg::Node* node = i->second->getNode();
335
if ( _clusterGeode.valid() )
336
_clusterGeode->accept( nv );
342
_frameStamp = nv.getFrameStamp()->getFrameNumber();
345
osg::LOD::traverse( nv );
349
GeoCell::adjustCount( int delta )
353
if ( _depth > 0 && getNumParents() > 0 )
355
static_cast<GeoCell*>(getParent(0))->adjustCount( delta );
358
if ( !_clusterGeode.valid() )
360
_clusterGeode = makeClusterGeode( _extent, _count );
364
osgText::Text* t = static_cast<osgText::Text*>( _clusterGeode->getDrawable(0) );
365
std::stringstream buf;
367
t->setText( buf.str() );
374
GeoCell::insertObject( GeoObject* object )
377
if ( object->getLocation(location) && _extent.contains(location.x(), location.y()) )
379
object->_cell = this;
380
_objects.insert( std::make_pair(object->getPriority(), object) );
382
if ( _objects.size() > _maxObjects )
384
GeoObjectCollection::iterator low = _objects.begin();
385
GeoObject* lowPriObject = low->second.get();
387
if ( getNumChildren() == 0 )
390
lowPriObject->getLocation(location);
391
unsigned index = getIndex(_extent, location, _splitDim, _splitDim);
392
bool insertedOK = static_cast<GeoCell*>(getChild(index))->insertObject( lowPriObject );
395
// remove it from this cell.
396
_objects.erase( low );
400
// should never ever happen..
401
OE_WARN << LC << "Object insertion failed" << std::endl;
415
// the max LOD range for children of this cell:
416
float newRange = _maxRange * _splitRangeFactor;
418
// first create all the child cells:
419
double xInterval = _extent.width() / (double)_splitDim;
420
double yInterval = _extent.height() / (double)_splitDim;
422
for( unsigned row=0; row<_splitDim; ++row )
424
for( unsigned col=0; col<_splitDim; ++col )
426
GeoExtent cellExtent(
428
_extent.xMin() + xInterval*(double)col,
429
_extent.yMin() + yInterval*(double)row,
430
_extent.xMin() + xInterval*(double)(col+1),
431
_extent.yMin() + yInterval*(double)(row+1) );
434
new GeoCell(cellExtent, newRange, _maxObjects, _splitDim, _splitRangeFactor, _depth+1),
442
GeoCell::removeObject( GeoObject* object )
444
if ( object->_cell.get() == this )
447
_objects.erase( findObject(_objects, object) );
450
// if we just fell beneath the threshold, pull one up from below.
451
if ( _objects.size() == _minObjects-1 )
456
// TODO: rebalance, merge the tree, etc.
461
for( unsigned i=0; i<getNumChildren(); ++i )
463
if ( static_cast<GeoCell*>(getChild(i))->removeObject( object ) )
477
GeoCell::reindexObject( GeoObject* object )
479
GeoCell* owner = object->getGeoCell();
483
if ( object->getLocation(location) && !owner->_extent.contains(location.x(), location.y()) )
485
// first remove from its current cell
486
owner->removeObject( object );
487
//object->_cell = 0L;
488
//owner->_objects.erase( findObject(owner->_objects, object) );
489
//owner->adjustCount( -1 );
491
GeoCell* cell = dynamic_cast<GeoCell*>( owner->getParent(0) );
494
if ( cell->getExtent().contains(location.x(), location.y()) )
496
if ( cell->insertObject( object ) )
499
cell = dynamic_cast<GeoCell*>( cell->getParent(0) );
508
return insertObject( object );
514
GeoCell::reindex( GeoObject* object )
517
if ( object->getLocation(location) && !_extent.contains(location.x(), location.y()) )
519
// first remove from its current cell
520
osg::ref_ptr<GeoCell> safeCell = object->_cell.get();
521
if ( safeCell.valid() )
524
safeCell->_objects.erase( findObject(safeCell->_objects, object) );
525
//safeCell->_objects.erase( std::find( _objects.begin(), _objects.end(), std::make_pair(object->getPriority(),object) ) );
526
//safeCell->_objects.erase( std::find( safeCell->_objects.begin(), safeCell->_objects.end(), object ) );
527
safeCell->adjustCount( -1 );
528
//safeCell->removeObject( object );
531
GeoCell* cell = dynamic_cast<GeoCell*>( this->getParent(0) );
534
if ( cell->getExtent().contains(location.x(), location.y()) )
536
if ( cell->insertObject( object ) )
539
cell = dynamic_cast<GeoCell*>( cell->getParent(0) );