osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarthFeatures/ScatterFilter.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 <osgEarthFeatures/ScatterFilter>
00020 #include <osgEarth/GeoMath>
00021 #include <stdlib.h>
00022 
00023 #define LC "[ScatterFilter] "
00024 
00025 using namespace osgEarth;
00026 using namespace osgEarth::Features;
00027 using namespace osgEarth::Symbology;
00028 
00029 //------------------------------------------------------------------------
00030 
00031 
00032 //------------------------------------------------------------------------
00033 
00034 ScatterFilter::ScatterFilter() :
00035 _density   ( 10.0f ),
00036 _random    ( true ),
00037 _randomSeed( 1 )
00038 {
00039     //NOP
00040 }
00041 
00042 void
00043 ScatterFilter::polyScatter(const Geometry*         input,
00044                            const SpatialReference* inputSRS,
00045                            const FilterContext&    context,
00046                            PointSet*               output )
00047 {
00048     Bounds bounds;
00049     double areaSqKm = 0.0;
00050 
00051     ConstGeometryIterator iter( input, false );
00052     while( iter.hasMore() )
00053     {
00054         const Polygon* polygon = dynamic_cast<const Polygon*>( iter.next() );
00055         if ( !polygon )
00056             continue;
00057 
00058         if ( /*context.isGeocentric() ||*/ context.profile()->getSRS()->isGeographic() )
00059         {
00060             bounds = polygon->getBounds();
00061 
00062             double avglat = bounds.yMin() + 0.5*bounds.height();
00063             double h = bounds.height() * 111.32;
00064             double w = bounds.width() * 111.32 * sin( 1.57079633 + osg::DegreesToRadians(avglat) );
00065 
00066             areaSqKm = w * h;
00067         }
00068 
00069         else if ( context.profile()->getSRS()->isProjected() )
00070         {
00071             bounds = polygon->getBounds();
00072             areaSqKm = (0.001*bounds.width()) * (0.001*bounds.height());
00073         }
00074 
00075         double zMin = 0.0;
00076         unsigned numInstancesInBoundingRect = areaSqKm * (double)osg::clampAbove( 0.1f, _density );
00077         if ( numInstancesInBoundingRect == 0 )
00078             continue;
00079 
00080         if ( _random )
00081         {
00082             // Random scattering. Note, we try to place as many instances as would
00083             // fit in the bounding rectangle; The real placed number will be less since
00084             // we only place models inside the actual polygon. But the density will 
00085             // be correct.
00086             for( unsigned j=0; j<numInstancesInBoundingRect; ++j )
00087             {
00088                 double x = bounds.xMin() + _prng.next() * bounds.width();
00089                 double y = bounds.yMin() + _prng.next() * bounds.height();
00090 
00091                 bool include = true;
00092 
00093                 if ( include && polygon->contains2D( x, y ) )
00094                     output->push_back( osg::Vec3d(x, y, zMin) );
00095             }
00096         }
00097 
00098         else
00099         {
00100             // regular interval scattering:
00101             double numInst1D = sqrt((double)numInstancesInBoundingRect);
00102             double ar = bounds.width() / bounds.height();
00103             unsigned cols = (unsigned)( numInst1D * ar );
00104             unsigned rows = (unsigned)( numInst1D / ar );
00105             double colInterval = bounds.width() / (double)(cols-1);
00106             double rowInterval = bounds.height() / (double)(rows-1);
00107             double interval = 0.5*(colInterval+rowInterval);
00108 
00109             for( double cy=bounds.yMin(); cy<=bounds.yMax(); cy += interval )
00110             {
00111                 for( double cx = bounds.xMin(); cx <= bounds.xMax(); cx += interval )
00112                 {
00113                     bool include = true;
00114 
00115                     if ( include && polygon->contains2D( cx, cy ) )
00116                         output->push_back( osg::Vec3d(cx, cy, zMin) );
00117                 }
00118             }
00119         }
00120     }
00121 }
00122 
00123 void
00124 ScatterFilter::lineScatter(const Geometry*         input,
00125                            const SpatialReference* inputSRS,
00126                            const FilterContext&    context,
00127                            PointSet*               output )
00128 {
00129     // calculate the number of instances per linear km.
00130     float instPerKm = sqrt( osg::clampAbove( 0.1f, _density ) );
00131 
00132     bool isGeo = inputSRS->isGeographic();
00133 
00134     ConstGeometryIterator iter( input );
00135     while( iter.hasMore() )
00136     {
00137         const Geometry* part = iter.next();
00138 
00139         // see whether it's a ring, because rings have to connect the last two points.
00140         bool isRing = part->getType() == Geometry::TYPE_RING;
00141         
00142         for( unsigned i=0; i<part->size(); ++i )
00143         {
00144             // done if we're not traversing a ring.
00145             if ( !isRing && i+1 == part->size() )
00146                 break;
00147 
00148             // extract the segment:
00149             const osg::Vec3d& p0 = (*part)[i];
00150             const osg::Vec3d& p1 = isRing && i+1 == part->size() ? (*part)[0] : (*part)[i+1];
00151 
00152             // figure out the segment length in meters (assumed geodetic coords)
00153             double seglen_m;
00154             double seglen_native = (p1-p0).length();
00155             
00156             if ( isGeo )
00157             {
00158                 seglen_m = GeoMath::distance(
00159                     osg::DegreesToRadians( p0.y() ), osg::DegreesToRadians( p0.x() ),
00160                     osg::DegreesToRadians( p1.y() ), osg::DegreesToRadians( p1.x() ) );
00161             }
00162             else // projected
00163             {
00164                 seglen_m = seglen_native;
00165             }
00166 
00167             unsigned numInstances = (seglen_m*0.001) * instPerKm;
00168             if ( numInstances > 0 )
00169             {            
00170                 // a unit vector for scattering points along the segment
00171                 osg::Vec3d unit = p1-p0;
00172                 unit.normalize();
00173 
00174                 for( unsigned n=0; n<numInstances; ++n )
00175                 {
00176                     double offset = _prng.next() * seglen_native;
00177                     output->push_back( p0 + unit*offset );
00178                 }
00179             }
00180         }
00181     }
00182 }
00183 
00184 FilterContext
00185 ScatterFilter::push(FeatureList& features, FilterContext& context )
00186 {
00187     if ( !isSupported() ) {
00188         OE_WARN << LC << "support for this filter is not enabled" << std::endl;
00189         return context;
00190     }
00191 
00192     // seed the random number generator so the randomness is the same each time
00193     _prng = Random( _randomSeed, Random::METHOD_FAST );
00194 
00195     for( FeatureList::iterator i = features.begin(); i != features.end(); ++i )
00196     {
00197         Feature* f = i->get();
00198         
00199         Geometry* geom = f->getGeometry();
00200         if ( !geom )
00201             continue;
00202 
00203         const SpatialReference* geomSRS = context.profile()->getSRS();
00204 
00205         PointSet* points = new PointSet();
00206 
00207         if ( geom->getComponentType() == Geometry::TYPE_POLYGON )
00208         {
00209             polyScatter( geom, geomSRS, context, points );
00210         }
00211         else if (
00212             geom->getComponentType() == Geometry::TYPE_LINESTRING ||
00213             geom->getComponentType() == Geometry::TYPE_RING )            
00214         {
00215             lineScatter( geom, geomSRS, context, points );
00216         }
00217         else {
00218             OE_WARN << LC << "Sorry, don't know how to scatter a PointSet yet" << std::endl;
00219         }
00220 
00221         // replace the source geometry with the scattered points.
00222         f->setGeometry( points );
00223     }
00224 
00225     return context;
00226 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines