osgEarth 2.1.1
|
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 }