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/TransformFilter> 00020 #include <osg/ClusterCullingCallback> 00021 00022 #define LC "[TransformFilter] " 00023 00024 using namespace osgEarth; 00025 using namespace osgEarth::Features; 00026 using namespace osgEarth::Symbology; 00027 00028 //--------------------------------------------------------------------------- 00029 00030 namespace 00031 { 00032 osg::Matrixd 00033 createGeocentricInvRefFrame( const osg::Vec3d& input, const SpatialReference* inputSRS ) 00034 { 00035 // convert to gencentric first: 00036 double X = input.x(), Y = input.y(), Z = input.z(); 00037 00038 osg::Matrixd localToWorld; 00039 localToWorld.makeTranslate(X,Y,Z); 00040 00041 // normalize X,Y,Z 00042 double inverse_length = 1.0/sqrt(X*X + Y*Y + Z*Z); 00043 00044 X *= inverse_length; 00045 Y *= inverse_length; 00046 Z *= inverse_length; 00047 00048 double length_XY = sqrt(X*X + Y*Y); 00049 double inverse_length_XY = 1.0/length_XY; 00050 00051 // Vx = |(-Y,X,0)| 00052 localToWorld(0,0) = -Y*inverse_length_XY; 00053 localToWorld(0,1) = X*inverse_length_XY; 00054 localToWorld(0,2) = 0.0; 00055 00056 // Vy = /(-Z*X/(sqrt(X*X+Y*Y), -Z*Y/(sqrt(X*X+Y*Y),sqrt(X*X+Y*Y))| 00057 double Vy_x = -Z*X*inverse_length_XY; 00058 double Vy_y = -Z*Y*inverse_length_XY; 00059 double Vy_z = length_XY; 00060 inverse_length = 1.0/sqrt(Vy_x*Vy_x + Vy_y*Vy_y + Vy_z*Vy_z); 00061 localToWorld(1,0) = Vy_x*inverse_length; 00062 localToWorld(1,1) = Vy_y*inverse_length; 00063 localToWorld(1,2) = Vy_z*inverse_length; 00064 00065 // Vz = (X,Y,Z) 00066 localToWorld(2,0) = X; 00067 localToWorld(2,1) = Y; 00068 localToWorld(2,2) = Z; 00069 00070 return localToWorld; 00071 } 00072 00073 void 00074 localizeGeometry( Feature* input, const osg::Matrixd& refFrame ) 00075 { 00076 if ( input && input->getGeometry() ) 00077 { 00078 GeometryIterator iter( input->getGeometry() ); 00079 while( iter.hasMore() ) 00080 { 00081 Geometry* geom = iter.next(); 00082 for( unsigned int i=0; i<geom->size(); i++ ) 00083 { 00084 (*geom)[i] = (*geom)[i] * refFrame; 00085 } 00086 } 00087 } 00088 } 00089 } 00090 00091 //--------------------------------------------------------------------------- 00092 00093 TransformFilter::TransformFilter() : 00094 _makeGeocentric( false ), 00095 _localize( false ) 00096 { 00097 // nop 00098 } 00099 00100 TransformFilter::TransformFilter( const osg::Matrixd& xform ) : 00101 _makeGeocentric( false ), 00102 _localize ( false ), 00103 _mat ( xform ) 00104 { 00105 //nop 00106 } 00107 00108 TransformFilter::TransformFilter(const SpatialReference* outputSRS, 00109 bool outputGeocentric ) : 00110 _outputSRS( outputSRS ), 00111 _makeGeocentric( outputGeocentric ), 00112 _localize( false ) 00113 { 00114 //NOP 00115 } 00116 00117 bool 00118 TransformFilter::push( Feature* input, FilterContext& context ) 00119 { 00120 if ( !input || !input->getGeometry() ) 00121 return true; 00122 00123 bool needsSRSXform = 00124 _outputSRS.valid() && 00125 ( ! context.profile()->getSRS()->isEquivalentTo( _outputSRS.get() ) ); 00126 00127 bool needsMatrixXform = !_mat.isIdentity(); 00128 00129 // optimize: do nothing if nothing needs doing 00130 if ( !needsSRSXform && !_makeGeocentric && !_localize && !needsMatrixXform ) 00131 return true; 00132 00133 // iterate over the feature geometry. 00134 GeometryIterator iter( input->getGeometry() ); 00135 while( iter.hasMore() ) 00136 { 00137 Geometry* geom = iter.next(); 00138 00139 // pre-transform the point before doing an SRS transformation. 00140 if ( needsMatrixXform ) 00141 { 00142 for( unsigned i=0; i < geom->size(); ++i ) 00143 (*geom)[i] = (*geom)[i] * _mat; 00144 } 00145 00146 // first transform the geometry to the output SRS: 00147 if ( needsSRSXform ) 00148 context.profile()->getSRS()->transformPoints( _outputSRS.get(), geom->asVector(), false ); 00149 00150 // convert to ECEF if required: 00151 if ( _makeGeocentric ) 00152 _outputSRS->transformToECEF( geom->asVector(), false ); 00153 00154 // update the bounding box. 00155 if ( _localize ) 00156 { 00157 for( unsigned i=0; i<geom->size(); ++i ) 00158 _bbox.expandBy( (*geom)[i] ); 00159 } 00160 } 00161 00162 return true; 00163 } 00164 00165 FilterContext 00166 TransformFilter::push( FeatureList& input, FilterContext& incx ) 00167 { 00168 _bbox = osg::BoundingBoxd(); 00169 00170 // first transform all the points into the output SRS, collecting a bounding box as we go: 00171 bool ok = true; 00172 for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) 00173 if ( !push( i->get(), incx ) ) 00174 ok = false; 00175 00176 FilterContext outcx( incx ); 00177 outcx.isGeocentric() = _makeGeocentric; 00178 00179 if ( _outputSRS.valid() ) 00180 { 00181 if ( incx.extent()->isValid() ) 00182 outcx.profile() = new FeatureProfile( incx.extent()->transform( _outputSRS.get() ) ); 00183 else 00184 outcx.profile() = new FeatureProfile( incx.profile()->getExtent().transform( _outputSRS.get() ) ); 00185 } 00186 00187 // set the reference frame to shift data to the centroid. This will 00188 // prevent floating point precision errors in the openGL pipeline for 00189 // properly gridded data. 00190 if ( _bbox.valid() && _localize ) 00191 { 00192 // create a suitable reference frame: 00193 osg::Matrixd localizer; 00194 if ( _makeGeocentric ) 00195 { 00196 localizer = createGeocentricInvRefFrame( _bbox.center(), _outputSRS.get() ); 00197 localizer.invert( localizer ); 00198 } 00199 else 00200 { 00201 localizer = osg::Matrixd::translate( -_bbox.center() ); 00202 } 00203 00204 // localize the geometry relative to the reference frame. 00205 for( FeatureList::iterator i = input.begin(); i != input.end(); i++ ) 00206 { 00207 localizeGeometry( i->get(), localizer ); 00208 } 00209 outcx.setReferenceFrame( localizer ); 00210 } 00211 00212 return outcx; 00213 }