osgEarth 2.1.1

/home/cube/sources/osgearth/src/osgEarth/GeoData.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 
00020 #include <osgEarth/GeoData>
00021 #include <osgEarth/ImageUtils>
00022 #include <osgEarth/Registry>
00023 #include <osgEarth/Cube>
00024 
00025 #include <osg/Notify>
00026 #include <osg/Timer>
00027 
00028 #include <gdal_priv.h>
00029 #include <gdalwarper.h>
00030 #include <ogr_spatialref.h>
00031 #include <memory.h>
00032 
00033 #include <sstream>
00034 #include <iomanip>
00035 
00036 #define LC "[GeoData] "
00037 
00038 using namespace osgEarth;
00039 
00040 
00041 Bounds::Bounds() :
00042 osg::BoundingBoxImpl<osg::Vec3d>( DBL_MAX, DBL_MAX, DBL_MAX, -DBL_MAX, -DBL_MAX, -DBL_MAX )
00043 {
00044     //nop
00045 }
00046 
00047 Bounds::Bounds(double xmin, double ymin, double xmax, double ymax ) :
00048 osg::BoundingBoxImpl<osg::Vec3d>( xmin, ymin, -DBL_MAX, xmax, ymax, DBL_MAX )
00049 {
00050     //nop
00051 }
00052 
00053 bool
00054 Bounds::isValid() const
00055 {
00056     return xMin() <= xMax() && yMin() <= yMax();
00057 }
00058 
00059 bool 
00060 Bounds::contains(double x, double y ) const
00061 {
00062     return 
00063         isValid() &&
00064         x >= xMin() && x <= xMax() && y >= yMin() && y <= yMax();
00065 }
00066 
00067 bool
00068 Bounds::contains(const Bounds& rhs) const
00069 {
00070     return 
00071         isValid() && rhs.isValid() && 
00072         xMin() <= rhs.xMin() && xMax() >= rhs.xMax() &&
00073         yMin() <= rhs.yMin() && yMax() >= rhs.yMax();
00074 }
00075 
00076 void
00077 Bounds::expandBy( double x, double y )
00078 {
00079     osg::BoundingBoxImpl<osg::Vec3d>::expandBy( x, y, 0 );
00080 }
00081 
00082 void
00083 Bounds::expandBy( double x, double y, double z )
00084 {
00085     osg::BoundingBoxImpl<osg::Vec3d>::expandBy( x, y, z );
00086 }
00087 
00088 void
00089 Bounds::expandBy( const Bounds& rhs )
00090 {
00091     osg::BoundingBoxImpl<osg::Vec3d>::expandBy( rhs );
00092 }
00093 
00094 Bounds 
00095 Bounds::unionWith(const Bounds& rhs) const
00096 {
00097     if ( valid() && !rhs.valid() ) return *this;
00098     if ( !valid() && rhs.valid() ) return rhs;
00099 
00100     Bounds u;
00101     if ( intersects(rhs) ) {
00102         u.xMin() = xMin() >= rhs.xMin() && xMin() <= rhs.xMax() ? xMin() : rhs.xMin();
00103         u.xMax() = xMax() >= rhs.xMin() && xMax() <= rhs.xMax() ? xMax() : rhs.xMax();
00104         u.yMin() = yMin() >= rhs.yMin() && yMin() <= rhs.yMax() ? yMin() : rhs.yMin();
00105         u.yMax() = yMax() >= rhs.yMin() && yMax() <= rhs.yMax() ? yMax() : rhs.yMax();
00106         u.zMin() = zMin() >= rhs.zMin() && zMin() <= rhs.zMax() ? zMin() : rhs.zMin();
00107         u.zMax() = zMax() >= rhs.zMin() && zMax() <= rhs.zMax() ? zMax() : rhs.zMax();
00108     }
00109     return u;
00110 }
00111 
00112 Bounds
00113 Bounds::intersectionWith(const Bounds& rhs) const 
00114 {
00115     if ( valid() && !rhs.valid() ) return *this;
00116     if ( !valid() && rhs.valid() ) return rhs;
00117 
00118     if ( this->contains(rhs) ) return rhs;
00119     if ( rhs.contains(*this) ) return *this;
00120 
00121     if ( !intersects(rhs) ) return Bounds();
00122 
00123     double xmin, xmax, ymin, ymax;
00124 
00125     xmin = ( xMin() > rhs.xMin() && xMin() < rhs.xMax() ) ? xMin() : rhs.xMin();
00126     xmax = ( xMax() > rhs.xMin() && xMax() < rhs.xMax() ) ? xMax() : rhs.xMax();
00127     ymin = ( yMin() > rhs.yMin() && yMin() < rhs.yMax() ) ? yMin() : rhs.yMin();
00128     ymax = ( yMax() > rhs.yMin() && yMax() < rhs.yMax() ) ? yMax() : rhs.yMax();
00129 
00130     return Bounds(xmin, ymin, xmax, ymax);
00131 }
00132 
00133 double
00134 Bounds::width() const {
00135     return xMax()-xMin();
00136 }
00137 
00138 double
00139 Bounds::height() const {
00140     return yMax()-yMin();
00141 }
00142 
00143 double
00144 Bounds::depth() const {
00145     return zMax()-zMin();
00146 }
00147 
00148 osg::Vec2d
00149 Bounds::center2d() const {
00150     osg::Vec3d c = center();
00151     return osg::Vec2d( c.x(), c.y() );
00152 }
00153 
00154 double
00155 Bounds::radius2d() const {
00156     return (center2d() - osg::Vec2d(xMin(),yMin())).length();
00157 }
00158 
00159 std::string
00160 Bounds::toString() const {
00161     std::stringstream buf;
00162     buf << "(" << xMin() << "," << yMin() << " => " << xMax() << "," << yMax() << ")";
00163     std::string result = buf.str();
00164     return result;
00165 }
00166 
00167 void
00168 Bounds::transform( const SpatialReference* from, const SpatialReference* to )
00169 {
00170     from->transformExtent( to, _min.x(), _min.y(), _max.x(), _max.y() );
00171 }
00172 
00173 /*************************************************************/
00174 
00175 GeoExtent GeoExtent::INVALID = GeoExtent();
00176 
00177 
00178 GeoExtent::GeoExtent():
00179 _xmin(FLT_MAX),
00180 _ymin(FLT_MAX),
00181 _xmax(-FLT_MAX),
00182 _ymax(-FLT_MAX)
00183 {
00184     //NOP - invalid
00185 }
00186 
00187 GeoExtent::GeoExtent(const SpatialReference* srs,
00188                      double xmin, double ymin, double xmax, double ymax) :
00189 _srs( srs ),
00190 _xmin(xmin),_ymin(ymin),_xmax(xmax),_ymax(ymax)
00191 {
00192     //NOP
00193 }
00194 
00195 
00196 GeoExtent::GeoExtent( const SpatialReference* srs, const Bounds& bounds ) :
00197 _srs( srs ),
00198 _xmin( bounds.xMin() ),
00199 _ymin( bounds.yMin() ),
00200 _xmax( bounds.xMax() ),
00201 _ymax( bounds.yMax() )
00202 {
00203     //nop
00204 }
00205 
00206 GeoExtent::GeoExtent( const GeoExtent& rhs ) :
00207 _srs( rhs._srs ),
00208 _xmin( rhs._xmin ), _ymin( rhs._ymin ), _xmax( rhs._xmax ), _ymax( rhs._ymax )
00209 {
00210     //NOP
00211 }
00212 
00213 bool
00214 GeoExtent::operator == ( const GeoExtent& rhs ) const
00215 {
00216     if ( !isValid() && !rhs.isValid() )
00217         return true;
00218 
00219     else return
00220         isValid() && rhs.isValid() &&
00221         _xmin == rhs._xmin &&
00222         _ymin == rhs._ymin &&
00223         _xmax == rhs._xmax &&
00224         _ymax == rhs._ymax &&
00225         _srs.valid() && rhs._srs.valid() &&
00226         _srs->isEquivalentTo( rhs._srs.get() );
00227 }
00228 
00229 bool
00230 GeoExtent::operator != ( const GeoExtent& rhs ) const
00231 {
00232     return !( *this == rhs );
00233 }
00234 
00235 bool
00236 GeoExtent::isValid() const
00237 {
00238     return _srs.valid() && width() > 0 && height() > 0;
00239 }
00240 
00241 const SpatialReference*
00242 GeoExtent::getSRS() const {
00243     return _srs.get(); 
00244 }
00245 
00246 double
00247 GeoExtent::width() const
00248 {
00249     return crossesDateLine()?
00250         (180-_xmin) + (_xmax+180) :
00251         _xmax - _xmin;
00252 }
00253 
00254 double
00255 GeoExtent::height() const
00256 {
00257     return _ymax - _ymin;
00258 }
00259 
00260 void
00261 GeoExtent::getCentroid( double& out_x, double& out_y ) const
00262 {
00263     out_x = _xmin+width()/2.0;
00264     out_y = _ymin+height()/2.0;
00265 }
00266 
00267 bool
00268 GeoExtent::crossesDateLine() const
00269 {
00270     return _xmax < _xmin;
00271     //return _srs.valid() && _srs->isGeographic() && _xmax < _xmin;
00272 }
00273 
00274 bool
00275 GeoExtent::splitAcrossDateLine( GeoExtent& out_first, GeoExtent& out_second ) const
00276 {
00277     bool success = false;
00278 
00279     if ( crossesDateLine() )
00280     {
00281         if ( _srs->isGeographic() )
00282         {
00283             out_first = GeoExtent( _srs.get(), _xmin, _ymin, 180.0, _ymax );
00284             out_second = GeoExtent( _srs.get(), -180.0, _ymin, _xmax, _ymax );
00285             success = true;
00286         }
00287         else
00288         {
00289             GeoExtent latlong_extent = transform( _srs->getGeographicSRS() );
00290             GeoExtent first, second;
00291             if ( latlong_extent.splitAcrossDateLine( first, second ) )
00292             {
00293                 out_first = first.transform( _srs.get() );
00294                 out_second = second.transform( _srs.get() );
00295                 success = out_first.isValid() && out_second.isValid();
00296             }
00297         }
00298     }
00299     return success;
00300 }
00301 
00302 GeoExtent
00303 GeoExtent::transform( const SpatialReference* to_srs ) const 
00304 {       
00305     if ( _srs.valid() && to_srs )
00306     {
00307         double xmin = _xmin, ymin = _ymin;
00308         double xmax = _xmax, ymax = _ymax;
00309         
00310         if ( _srs->transformExtent( to_srs, xmin, ymin, xmax, ymax ) )
00311         {
00312             return GeoExtent( to_srs, xmin, ymin, xmax, ymax );
00313         }
00314 
00315     }
00316     return GeoExtent::INVALID;
00317 }
00318 
00319 void
00320 GeoExtent::getBounds(double &xmin, double &ymin, double &xmax, double &ymax) const
00321 {
00322     xmin = _xmin;
00323     ymin = _ymin;
00324     xmax = _xmax;
00325     ymax = _ymax;
00326 }
00327 
00328 Bounds
00329 GeoExtent::bounds() const
00330 {
00331     return Bounds( _xmin, _ymin, _xmax, _ymax );
00332 }
00333 
00334 //TODO:: support crossesDateLine!
00335 bool
00336 GeoExtent::contains(double x, double y, const SpatialReference* srs) const
00337 {
00338     double local_x = x, local_y = y;
00339     if (srs &&
00340         !srs->isEquivalentTo( _srs.get() ) &&
00341         !srs->transform2D(x, y, _srs.get(), local_x, local_y) )
00342     {
00343         return false;
00344     }
00345     else
00346     {
00347         //Account for small rounding errors along the edges of the extent
00348         if (osg::equivalent(_xmin, local_x)) local_x = _xmin;
00349         if (osg::equivalent(_xmax, local_x)) local_x = _xmax;
00350         if (osg::equivalent(_ymin, local_y)) local_y = _ymin;
00351         if (osg::equivalent(_ymax, local_y)) local_y = _ymax;
00352         return local_x >= _xmin && local_x <= _xmax && local_y >= _ymin && local_y <= _ymax;
00353     }
00354 }
00355 
00356 bool
00357 GeoExtent::contains( const Bounds& rhs ) const
00358 {
00359     return 
00360         rhs.xMin() >= _xmin &&
00361         rhs.yMin() >= _ymin &&
00362         rhs.xMax() <= _xmax &&
00363         rhs.yMax() <= _ymax;
00364 }
00365 
00366 bool
00367 GeoExtent::intersects( const GeoExtent& rhs ) const
00368 {
00369     bool valid = isValid();
00370     if ( !valid ) return false;
00371     bool exclusive =
00372         _xmin > rhs.xMax() ||
00373         _xmax < rhs.xMin() ||
00374         _ymin > rhs.yMax() ||
00375         _ymax < rhs.yMin();
00376     return !exclusive;
00377 }
00378 
00379 void
00380 GeoExtent::expandToInclude( double x, double y )
00381 {
00382     if ( x < _xmin ) _xmin = x;
00383     if ( x > _xmax ) _xmax = x;
00384     if ( y < _ymin ) _ymin = y;
00385     if ( y > _ymax ) _ymax = y;
00386 }
00387 
00388 void GeoExtent::expandToInclude(const Bounds& rhs)
00389 {
00390     expandToInclude( rhs.xMin(), rhs.yMin() );
00391     expandToInclude( rhs.xMax(), rhs.yMax() );
00392 }
00393 
00394 GeoExtent
00395 GeoExtent::intersectionSameSRS( const Bounds& rhs ) const
00396 {
00397     Bounds b(
00398         osg::maximum( xMin(), rhs.xMin() ),
00399         osg::maximum( yMin(), rhs.yMin() ),
00400         osg::minimum( xMax(), rhs.xMax() ),
00401         osg::minimum( yMax(), rhs.yMax() ) );
00402 
00403     return b.width() > 0 && b.height() > 0 ? GeoExtent( getSRS(), b ) : GeoExtent::INVALID;
00404 }
00405 
00406 void
00407 GeoExtent::scale(double x_scale, double y_scale)
00408 {
00409     double orig_width = width();
00410     double orig_height = height();
00411 
00412     double new_width  = orig_width  * x_scale;
00413     double new_height = orig_height * y_scale;
00414 
00415     double halfXDiff = (new_width - orig_width) / 2.0;
00416     double halfYDiff = (new_height - orig_height) /2.0;
00417 
00418     _xmin -= halfXDiff;
00419     _xmax += halfXDiff;
00420     _ymin -= halfYDiff;
00421     _ymax += halfYDiff;
00422 }
00423 
00424 void
00425 GeoExtent::expand( double x, double y )
00426 {
00427     _xmin -= .5*x;
00428     _xmax += .5*x;
00429     _ymin -= .5*y;
00430     _ymax += .5*y;
00431 }
00432 
00433 double
00434 GeoExtent::area() const
00435 {
00436     return width() * height();
00437 }
00438 
00439 std::string
00440 GeoExtent::toString() const
00441 {
00442     std::stringstream buf;
00443     if ( !isValid() )
00444         buf << "INVALID";
00445     else
00446         buf << "MIN=" << _xmin << "," << _ymin << " MAX=" << _xmax << "," << _ymax;
00447 
00448     buf << ", SRS=" << _srs->getName();
00449 
00450         std::string bufStr;
00451         bufStr = buf.str();
00452     return bufStr;
00453 }
00454 
00455 
00456 /***************************************************************************/
00457 
00458 DataExtent::DataExtent(const osgEarth::GeoExtent &extent, unsigned int minLevel,  unsigned int maxLevel):
00459 GeoExtent(extent),
00460 _minLevel(minLevel),
00461 _maxLevel(maxLevel)
00462 {
00463 }
00464 
00465 unsigned int
00466 DataExtent::getMinLevel() const
00467 {
00468     return _minLevel;
00469 }
00470 
00471 unsigned int
00472 DataExtent::getMaxLevel() const
00473 {
00474     return _maxLevel;
00475 }
00476 
00477 
00478 /***************************************************************************/
00479 
00480 // static
00481 GeoImage GeoImage::INVALID( 0L, GeoExtent::INVALID );
00482 
00483 GeoImage::GeoImage() :
00484 _image(0L),
00485 _extent( GeoExtent::INVALID )
00486 {
00487     //nop
00488 }
00489 
00490 
00491 GeoImage::GeoImage( osg::Image* image, const GeoExtent& extent ) :
00492 _image(image),
00493 _extent(extent)
00494 {
00495     //NOP
00496 }
00497 
00498 osg::Image*
00499 GeoImage::getImage() const {
00500     return _image.get();
00501 }
00502 
00503 const SpatialReference*
00504 GeoImage::getSRS() const {
00505     return _extent.getSRS();
00506 }
00507 
00508 const GeoExtent&
00509 GeoImage::getExtent() const {
00510     return _extent;
00511 }
00512 
00513 double
00514 GeoImage::getUnitsPerPixel() const {
00515         double uppw = _extent.width() / (double)_image->s();
00516         double upph = _extent.height() / (double)_image->t();
00517         return (uppw + upph) / 2.0;
00518 }
00519 
00520 GeoImage
00521 GeoImage::crop( const GeoExtent& extent, bool exact, unsigned int width, unsigned int height  ) const
00522 {
00523     //Check for equivalence
00524     if ( extent.getSRS()->isEquivalentTo( getSRS() ) )
00525     {
00526         //If we want an exact crop or they want to specify the output size of the image, use GDAL
00527         if (exact || width != 0 || height != 0 )
00528         {
00529             OE_DEBUG << "[osgEarth::GeoImage::crop] Performing exact crop" << std::endl;
00530 
00531             //Suggest an output image size
00532             if (width == 0 || height == 0)
00533             {
00534                 double xRes = (getExtent().xMax() - getExtent().xMin()) / (double)_image->s();
00535                 double yRes = (getExtent().yMax() - getExtent().yMin()) / (double)_image->t();
00536 
00537                 width =  osg::maximum(1u, (unsigned int)((extent.xMax() - extent.xMin()) / xRes));
00538                 height = osg::maximum(1u, (unsigned int)((extent.yMax() - extent.yMin()) / yRes));
00539 
00540                 OE_DEBUG << "[osgEarth::GeoImage::crop] Computed output image size " << width << "x" << height << std::endl;
00541             }
00542 
00543             //Note:  Passing in the current SRS simply forces GDAL to not do any warping
00544             return reproject( getSRS(), &extent, width, height);
00545         }
00546         else
00547         {
00548             OE_DEBUG << "[osgEarth::GeoImage::crop] Performing non-exact crop " << std::endl;
00549             //If an exact crop is not desired, we can use the faster image cropping code that does no resampling.
00550             double destXMin = extent.xMin();
00551             double destYMin = extent.yMin();
00552             double destXMax = extent.xMax();
00553             double destYMax = extent.yMax();
00554 
00555             osg::Image* new_image = ImageUtils::cropImage(
00556                 _image.get(),
00557                 _extent.xMin(), _extent.yMin(), _extent.xMax(), _extent.yMax(),
00558                 destXMin, destYMin, destXMax, destYMax );
00559 
00560             //The destination extents may be different than the input extents due to not being able to crop along pixel boundaries.
00561             return new_image?
00562                 GeoImage( new_image, GeoExtent( getSRS(), destXMin, destYMin, destXMax, destYMax ) ) :
00563                 GeoImage::INVALID;
00564         }
00565     }
00566     else
00567     {
00568         //TODO: just reproject the image before cropping
00569         OE_NOTICE << "[osgEarth::GeoImage::crop] Cropping extent does not have equivalent SpatialReference" << std::endl;
00570         return GeoImage::INVALID;
00571     }
00572 }
00573 
00574 GeoImage
00575 GeoImage::addTransparentBorder(bool leftBorder, bool rightBorder, bool bottomBorder, bool topBorder)
00576 {
00577     unsigned int buffer = 1;
00578 
00579     unsigned int newS = _image->s();
00580     if (leftBorder) newS += buffer;
00581     if (rightBorder) newS += buffer;
00582 
00583     unsigned int newT = _image->t();
00584     if (topBorder)    newT += buffer;
00585     if (bottomBorder) newT += buffer;
00586 
00587     osg::Image* newImage = new osg::Image;
00588     newImage->allocateImage(newS, newT, _image->r(), _image->getPixelFormat(), _image->getDataType(), _image->getPacking());
00589     newImage->setInternalTextureFormat(_image->getInternalTextureFormat());
00590     memset(newImage->data(), 0, newImage->getImageSizeInBytes());
00591     unsigned startC = leftBorder ? buffer : 0;
00592     unsigned startR = bottomBorder ? buffer : 0;
00593     ImageUtils::copyAsSubImage(_image.get(), newImage, startC, startR );
00594 
00595     //double upp = getUnitsPerPixel();
00596     double uppw = _extent.width() / (double)_image->s();
00597         double upph = _extent.height() / (double)_image->t();
00598 
00599     double xmin = leftBorder ? _extent.xMin() - buffer * uppw : _extent.xMin();
00600     double ymin = bottomBorder ? _extent.yMin() - buffer * upph : _extent.yMin();
00601     double xmax = rightBorder ? _extent.xMax() + buffer * uppw : _extent.xMax();
00602     double ymax = topBorder ? _extent.yMax() + buffer * upph : _extent.yMax();
00603 
00604     return GeoImage(newImage, GeoExtent(getSRS(), xmin, ymin, xmax, ymax));
00605 }
00606 
00607 static osg::Image*
00608 createImageFromDataset(GDALDataset* ds)
00609 {
00610     // called internally -- GDAL lock not required
00611 
00612     //Allocate the image
00613     osg::Image *image = new osg::Image;
00614     image->allocateImage(ds->GetRasterXSize(), ds->GetRasterYSize(), 1, GL_RGBA, GL_UNSIGNED_BYTE);
00615 
00616     ds->RasterIO(GF_Read, 0, 0, image->s(), image->t(), (void*)image->data(), image->s(), image->t(), GDT_Byte, 4, NULL, 4, 4 * image->s(), 1);
00617     ds->FlushCache();
00618 
00619     image->flipVertical();
00620 
00621     return image;
00622 }
00623 
00624 static GDALDataset*
00625 createMemDS(int width, int height, double minX, double minY, double maxX, double maxY, const std::string &projection)
00626 {
00627     //Get the MEM driver
00628     GDALDriver* memDriver = (GDALDriver*)GDALGetDriverByName("MEM");
00629     if (!memDriver)
00630     {
00631         OE_NOTICE << "[osgEarth::GeoData] Could not get MEM driver" << std::endl;
00632     }
00633 
00634     //Create the in memory dataset.
00635     GDALDataset* ds = memDriver->Create("", width, height, 4, GDT_Byte, 0);
00636 
00637     //Initialize the color interpretation
00638     ds->GetRasterBand(1)->SetColorInterpretation(GCI_RedBand);
00639     ds->GetRasterBand(2)->SetColorInterpretation(GCI_GreenBand);
00640     ds->GetRasterBand(3)->SetColorInterpretation(GCI_BlueBand);
00641     ds->GetRasterBand(4)->SetColorInterpretation(GCI_AlphaBand);
00642 
00643     //Initialize the geotransform
00644     double geotransform[6];
00645     double x_units_per_pixel = (maxX - minX) / (double)width;
00646     double y_units_per_pixel = (maxY - minY) / (double)height;
00647     geotransform[0] = minX;
00648     geotransform[1] = x_units_per_pixel;
00649     geotransform[2] = 0;
00650     geotransform[3] = maxY;
00651     geotransform[4] = 0;
00652     geotransform[5] = -y_units_per_pixel;
00653     ds->SetGeoTransform(geotransform);
00654     ds->SetProjection(projection.c_str());
00655 
00656     return ds;
00657 }
00658 
00659 static GDALDataset*
00660 createDataSetFromImage(const osg::Image* image, double minX, double minY, double maxX, double maxY, const std::string &projection)
00661 {
00662     //Clone the incoming image
00663     osg::ref_ptr<osg::Image> clonedImage = new osg::Image(*image);
00664 
00665     //Flip the image
00666     clonedImage->flipVertical();  
00667 
00668     GDALDataset* srcDS = createMemDS(image->s(), image->t(), minX, minY, maxX, maxY, projection);
00669 
00670     //Write the image data into the memory dataset
00671     //If the image is already RGBA, just read all 4 bands in one call
00672     if (image->getPixelFormat() == GL_RGBA)
00673     {
00674         srcDS->RasterIO(GF_Write, 0, 0, clonedImage->s(), clonedImage->t(), (void*)clonedImage->data(), clonedImage->s(), clonedImage->t(), GDT_Byte, 4, NULL, 4, 4 * image->s(), 1);
00675     }
00676     else if (image->getPixelFormat() == GL_RGB)
00677     {    
00678         //OE_NOTICE << "[osgEarth::GeoData] Reprojecting RGB " << std::endl;
00679         //Read the read, green and blue bands
00680         srcDS->RasterIO(GF_Write, 0, 0, clonedImage->s(), clonedImage->t(), (void*)clonedImage->data(), clonedImage->s(), clonedImage->t(), GDT_Byte, 3, NULL, 3, 3 * image->s(), 1);
00681 
00682         //Initialize the alpha values to 255.
00683         unsigned char *alpha = new unsigned char[clonedImage->s() * clonedImage->t()];
00684         memset(alpha, 255, clonedImage->s() * clonedImage->t());
00685 
00686         GDALRasterBand* alphaBand = srcDS->GetRasterBand(4);
00687         alphaBand->RasterIO(GF_Write, 0, 0, clonedImage->s(), clonedImage->t(), alpha, clonedImage->s(),clonedImage->t(), GDT_Byte, 0, 0);
00688 
00689         delete[] alpha;
00690     }
00691     else
00692     {
00693         OE_WARN << LC << "createDataSetFromImage: unsupported pixel format " << std::hex << image->getPixelFormat() << std::endl;
00694     }
00695     srcDS->FlushCache();
00696 
00697     return srcDS;
00698 }
00699 
00700 static osg::Image*
00701 reprojectImage(osg::Image* srcImage, const std::string srcWKT, double srcMinX, double srcMinY, double srcMaxX, double srcMaxY,
00702                const std::string destWKT, double destMinX, double destMinY, double destMaxX, double destMaxY,
00703                int width = 0, int height = 0)
00704 {
00705     //OE_NOTICE << "Reprojecting..." << std::endl;
00706     GDAL_SCOPED_LOCK;
00707         osg::Timer_t start = osg::Timer::instance()->tick();
00708 
00709     //Create a dataset from the source image
00710     GDALDataset* srcDS = createDataSetFromImage(srcImage, srcMinX, srcMinY, srcMaxX, srcMaxY, srcWKT);
00711 
00712         OE_DEBUG << "Source image is " << srcImage->s() << "x" << srcImage->t() << std::endl;
00713 
00714 
00715     if (width == 0 || height == 0)
00716     {
00717         double outgeotransform[6];
00718         double extents[4];
00719         void* transformer = GDALCreateGenImgProjTransformer(srcDS, srcWKT.c_str(), NULL, destWKT.c_str(), 1, 0, 0);
00720         GDALSuggestedWarpOutput2(srcDS,
00721             GDALGenImgProjTransform, transformer,
00722             outgeotransform,
00723             &width,
00724             &height,
00725             extents,
00726             0);
00727         GDALDestroyGenImgProjTransformer(transformer);
00728     }
00729         OE_DEBUG << "Creating warped output of " << width <<"x" << height << std::endl;
00730    
00731     GDALDataset* destDS = createMemDS(width, height, destMinX, destMinY, destMaxX, destMaxY, destWKT);
00732 
00733     GDALReprojectImage(srcDS, NULL,
00734                        destDS, NULL,
00735                        //GDALResampleAlg::GRA_NearestNeighbour,
00736                        GRA_Bilinear,
00737                        0,0,0,0,0);                    
00738 
00739     osg::Image* result = createImageFromDataset(destDS);
00740     
00741     delete srcDS;
00742     delete destDS;  
00743 
00744         osg::Timer_t end = osg::Timer::instance()->tick();
00745 
00746         OE_DEBUG << "Reprojected image in " << osg::Timer::instance()->delta_m(start,end) << std::endl;
00747 
00748     return result;
00749 }    
00750 
00751 static osg::Image*
00752 manualReproject(const osg::Image* image, const GeoExtent& src_extent, const GeoExtent& dest_extent,
00753                 unsigned int width = 0, unsigned int height = 0)
00754 {
00755     //TODO:  Compute the optimal destination size
00756     if (width == 0 || height == 0)
00757     {
00758         //If no width and height are specified, just use the minimum dimension for the image
00759         width = osg::minimum(image->s(), image->t());
00760         height = osg::minimum(image->s(), image->t());        
00761     }
00762 
00763     // need to know this in order to choose the right interpolation algorithm
00764     const bool isSrcContiguous = src_extent.getSRS()->isContiguous();
00765 
00766     osg::Image *result = new osg::Image();
00767     result->allocateImage(width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE);
00768     //Initialize the image to be completely transparent
00769     memset(result->data(), 0, result->getImageSizeInBytes());
00770 
00771     ImageUtils::PixelReader ra(result);
00772     const double dx = dest_extent.width() / (double)width;
00773     const double dy = dest_extent.height() / (double)height;
00774 
00775     // offset the sample points by 1/2 a pixel so we are sampling "pixel center".
00776     // (This is especially useful in the UnifiedCubeProfile since it nullifes the chances for
00777     // edge ambiguity.)
00778 
00779     unsigned int numPixels = width * height;
00780 
00781     // Start by creating a sample grid over the destination
00782     // extent. These will be the source coordinates. Then, reproject
00783     // the sample grid into the source coordinate system.
00784     double *srcPointsX = new double[numPixels * 2];
00785     double *srcPointsY = srcPointsX + numPixels;
00786     dest_extent.getSRS()->transformExtentPoints(
00787         src_extent.getSRS(),
00788         dest_extent.xMin() + .5 * dx, dest_extent.yMin() + .5 * dy,
00789         dest_extent.xMax() - .5 * dx, dest_extent.yMax() - .5 * dy,
00790         srcPointsX, srcPointsY, width, height, 0, true);
00791 
00792     // Next, go through the source-SRS sample grid, read the color at each point from the source image,
00793     // and write it to the corresponding pixel in the destination image.
00794     int pixel = 0;
00795     ImageUtils::PixelReader ia(image);
00796     double xfac = (image->s() - 1) / src_extent.width();
00797     double yfac = (image->t() - 1) / src_extent.height();
00798     for (unsigned int c = 0; c < width; ++c)
00799     {
00800         for (unsigned int r = 0; r < height; ++r)
00801         {   
00802             double src_x = srcPointsX[pixel];
00803             double src_y = srcPointsY[pixel];
00804 
00805             if ( src_x < src_extent.xMin() || src_x > src_extent.xMax() || src_y < src_extent.yMin() || src_y > src_extent.yMax() )
00806             {
00807                 //If the sample point is outside of the bound of the source extent, increment the pixel and keep looping through.
00808                 //OE_WARN << LC << "ERROR: sample point out of bounds: " << src_x << ", " << src_y << std::endl;
00809                 pixel++;
00810                 continue;
00811             }
00812 
00813             float px = (src_x - src_extent.xMin()) * xfac;
00814             float py = (src_y - src_extent.yMin()) * yfac;
00815 
00816             int px_i = osg::clampBetween( (int)osg::round(px), 0, image->s()-1 );
00817             int py_i = osg::clampBetween( (int)osg::round(py), 0, image->t()-1 );
00818 
00819             osg::Vec4 color(0,0,0,0);
00820 
00821             if ( ! isSrcContiguous ) // non-contiguous space- use nearest neighbot
00822             {
00823                 color = ia(px_i, py_i);
00824             }
00825 
00826             else // contiguous space - use bilinear sampling
00827             {
00828                 int rowMin = osg::maximum((int)floor(py), 0);
00829                 int rowMax = osg::maximum(osg::minimum((int)ceil(py), (int)(image->t()-1)), 0);
00830                 int colMin = osg::maximum((int)floor(px), 0);
00831                 int colMax = osg::maximum(osg::minimum((int)ceil(px), (int)(image->s()-1)), 0);
00832 
00833                 if (rowMin > rowMax) rowMin = rowMax;
00834                 if (colMin > colMax) colMin = colMax;
00835 
00836                 osg::Vec4 urColor = ia(colMax, rowMax);
00837                 osg::Vec4 llColor = ia(colMin, rowMin);
00838                 osg::Vec4 ulColor = ia(colMin, rowMax);
00839                 osg::Vec4 lrColor = ia(colMax, rowMin);
00840 
00841                 /*Average Interpolation*/
00842                 /*double x_rem = px - (int)px;
00843                 double y_rem = py - (int)py;
00844 
00845                 double w00 = (1.0 - y_rem) * (1.0 - x_rem);
00846                 double w01 = (1.0 - y_rem) * x_rem;
00847                 double w10 = y_rem * (1.0 - x_rem);
00848                 double w11 = y_rem * x_rem;
00849                 double wsum = w00 + w01 + w10 + w11;
00850                 wsum = 1.0/wsum;
00851 
00852                 color.r() = (w00 * llColor.r() + w01 * lrColor.r() + w10 * ulColor.r() + w11 * urColor.r()) * wsum;
00853                 color.g() = (w00 * llColor.g() + w01 * lrColor.g() + w10 * ulColor.g() + w11 * urColor.g()) * wsum;
00854                 color.b() = (w00 * llColor.b() + w01 * lrColor.b() + w10 * ulColor.b() + w11 * urColor.b()) * wsum;
00855                 color.a() = (w00 * llColor.a() + w01 * lrColor.a() + w10 * ulColor.a() + w11 * urColor.a()) * wsum;*/
00856 
00857                 /*Nearest Neighbor Interpolation*/
00858                 /*if (px_i >= 0 && px_i < image->s() &&
00859                 py_i >= 0 && py_i < image->t())
00860                 {
00861                 //OE_NOTICE << "[osgEarth::GeoData] Sampling pixel " << px << "," << py << std::endl;
00862                 color = ImageUtils::getColor(image, px_i, py_i);
00863                 }
00864                 else
00865                 {
00866                 OE_NOTICE << "[osgEarth::GeoData] Pixel out of range " << px_i << "," << py_i << "  image is " << image->s() << "x" << image->t() << std::endl;
00867                 }*/
00868 
00869                 /*Bilinear interpolation*/
00870                 //Check for exact value
00871                 if ((colMax == colMin) && (rowMax == rowMin))
00872                 {
00873                     //OE_NOTICE << "[osgEarth::GeoData] Exact value" << std::endl;
00874                     color = ia(px_i, py_i);
00875                 }
00876                 else if (colMax == colMin)
00877                 {
00878                     //OE_NOTICE << "[osgEarth::GeoData] Vertically" << std::endl;
00879                     //Linear interpolate vertically
00880                     for (unsigned int i = 0; i < 4; ++i)
00881                     {
00882                         color[i] = ((float)rowMax - py) * llColor[i] + (py - (float)rowMin) * ulColor[i];
00883                     }
00884                 }
00885                 else if (rowMax == rowMin)
00886                 {
00887                     //OE_NOTICE << "[osgEarth::GeoData] Horizontally" << std::endl;
00888                     //Linear interpolate horizontally
00889                     for (unsigned int i = 0; i < 4; ++i)
00890                     {
00891                         color[i] = ((float)colMax - px) * llColor[i] + (px - (float)colMin) * lrColor[i];
00892                     }
00893                 }
00894                 else
00895                 {
00896                     //OE_NOTICE << "[osgEarth::GeoData] Bilinear" << std::endl;
00897                     //Bilinear interpolate
00898                     float col1 = colMax - px, col2 = px - colMin;
00899                     float row1 = rowMax - py, row2 = py - rowMin;
00900                     for (unsigned int i = 0; i < 4; ++i)
00901                     {
00902                         float r1 = col1 * llColor[i] + col2 * lrColor[i];
00903                         float r2 = col1 * ulColor[i] + col2 * urColor[i];
00904 
00905                         //OE_INFO << "r1, r2 = " << r1 << " , " << r2 << std::endl;
00906                         color[i] = row1 * r1 + row2 * r2;
00907                     }
00908                 }
00909             }
00910                
00911             unsigned char* rgba = const_cast<unsigned char*>(ra.data(c,r,0));
00912 
00913             rgba[0] = (unsigned char)(color.r() * 255);
00914             rgba[1] = (unsigned char)(color.g() * 255);
00915             rgba[2] = (unsigned char)(color.b() * 255);
00916             rgba[3] = (unsigned char)(color.a() * 255);
00917 
00918             pixel++;            
00919         }
00920     }
00921 
00922     delete[] srcPointsX;
00923 
00924     return result;
00925 }
00926 
00927 
00928 
00929 GeoImage
00930 GeoImage::reproject(const SpatialReference* to_srs, const GeoExtent* to_extent, unsigned int width, unsigned int height) const
00931 {  
00932     GeoExtent destExtent;
00933     if (to_extent)
00934     {
00935         destExtent = *to_extent;
00936     }
00937     else
00938     {
00939          destExtent = getExtent().transform(to_srs);    
00940     }
00941 
00942     osg::Image* resultImage = 0L;
00943 
00944     if ( getSRS()->isUserDefined() || to_srs->isUserDefined() ||
00945         ( getSRS()->isMercator() && to_srs->isGeographic() ) ||
00946         ( getSRS()->isGeographic() && to_srs->isMercator() ) )
00947     {
00948         // if either of the SRS is a custom projection, we have to do a manual reprojection since
00949         // GDAL will not recognize the SRS.
00950         resultImage = manualReproject(getImage(), getExtent(), *to_extent, width, height);
00951     }
00952     else
00953     {
00954         // otherwise use GDAL.
00955         resultImage = reprojectImage(getImage(),
00956             getSRS()->getWKT(),
00957             getExtent().xMin(), getExtent().yMin(), getExtent().xMax(), getExtent().yMax(),
00958             to_srs->getWKT(),
00959             destExtent.xMin(), destExtent.yMin(), destExtent.xMax(), destExtent.yMax(),
00960             width, height);
00961     }   
00962     return GeoImage(resultImage, destExtent);
00963 }
00964 
00965 osg::Image*
00966 GeoImage::takeImage()
00967 {
00968     return _image.release();
00969 }
00970 
00971 
00972 /***************************************************************************/
00973 
00974 // static
00975 GeoHeightField GeoHeightField::INVALID( 0L, GeoExtent::INVALID, 0L );
00976 
00977 GeoHeightField::GeoHeightField() :
00978 _heightField( 0L ),
00979 _extent( GeoExtent::INVALID ),
00980 _vsrs( 0L )
00981 {
00982     //nop
00983 }
00984 
00985 GeoHeightField::GeoHeightField(osg::HeightField* heightField,
00986                                const GeoExtent& extent,
00987                                const VerticalSpatialReference* vsrs) :
00988 _heightField( heightField ),
00989 _extent( extent ),
00990 _vsrs( vsrs )
00991 {
00992     if ( _heightField )
00993     {
00994         double minx, miny, maxx, maxy;
00995         _extent.getBounds(minx, miny, maxx, maxy);
00996 
00997         _heightField->setOrigin( osg::Vec3d( minx, miny, 0.0 ) );
00998         _heightField->setXInterval( (maxx - minx)/(double)(_heightField->getNumColumns()-1) );
00999         _heightField->setYInterval( (maxy - miny)/(double)(_heightField->getNumRows()-1) );
01000         _heightField->setBorderWidth( 0 );
01001     }
01002 }
01003 
01004 bool
01005 GeoHeightField::getElevation(const osgEarth::SpatialReference* inputSRS, 
01006                              double x, double y, 
01007                              ElevationInterpolation interp,
01008                              const VerticalSpatialReference* outputVSRS,
01009                              float &elevation) const
01010 {
01011     double local_x = x, local_y = y;
01012 
01013     if ( inputSRS && !inputSRS->transform2D(x, y, _extent.getSRS(), local_x, local_y) )
01014         return false;
01015 
01016     if ( _extent.contains(local_x, local_y) )
01017     {
01018         double xInterval = _extent.width()  / (double)(_heightField->getNumColumns()-1);
01019         double yInterval = _extent.height() / (double)(_heightField->getNumRows()-1);
01020 
01021         elevation = HeightFieldUtils::getHeightAtLocation(
01022             _heightField.get(), 
01023             local_x, local_y, 
01024             _extent.xMin(), _extent.yMin(), 
01025             xInterval, yInterval, 
01026             interp);
01027 
01028         if ( elevation != NO_DATA_VALUE )
01029         {
01030             if ( VerticalSpatialReference::canTransform( _vsrs.get(), outputVSRS ) )
01031             {
01032                 // need geodetic coordinates for a VSRS transformation:
01033                 double lat_deg, lon_deg, newElevation;
01034 
01035                 if ( inputSRS->isGeographic() ) {
01036                     lat_deg = y;
01037                     lon_deg = x;
01038                 }
01039                 else if ( _extent.getSRS()->isGeographic() ) {
01040                     lat_deg = local_y;
01041                     lon_deg = local_x;
01042                 }
01043                 else {
01044                     _extent.getSRS()->transform2D( x, y, inputSRS->getGeographicSRS(), lon_deg, lat_deg );
01045                 }
01046 
01047                 if ( _vsrs->transform( outputVSRS, lat_deg, lon_deg, elevation, newElevation ) )
01048                     elevation = newElevation;
01049             }
01050         }
01051 
01052         return true;
01053     }
01054     else
01055     {
01056         elevation = 0.0f;
01057         return false;
01058     }
01059 }
01060 
01061 GeoHeightField
01062 GeoHeightField::createSubSample( const GeoExtent& destEx, ElevationInterpolation interpolation) const
01063 {
01064     double div = destEx.width()/_extent.width();
01065     if ( div >= 1.0f )
01066         return GeoHeightField::INVALID;
01067 
01068     int w = _heightField->getNumColumns();
01069     int h = _heightField->getNumRows();
01070     //double dx = _heightField->getXInterval() * div;
01071     //double dy = _heightField->getYInterval() * div;
01072     double xInterval = _extent.width() / (double)(_heightField->getNumColumns()-1);
01073     double yInterval = _extent.height() / (double)(_heightField->getNumRows()-1);
01074     double dx = xInterval * div;
01075     double dy = yInterval * div;
01076 
01077     osg::HeightField* dest = new osg::HeightField();
01078     dest->allocate( w, h );
01079     dest->setXInterval( dx );
01080     dest->setYInterval( dy );
01081 
01082     // copy over the skirt height, adjusting it for tile size.
01083     dest->setSkirtHeight( _heightField->getSkirtHeight() * div );
01084 
01085     double x, y;
01086     int col, row;
01087 
01088     for( x = destEx.xMin(), col=0; col < w; x += dx, col++ )
01089     {
01090         for( y = destEx.yMin(), row=0; row < h; y += dy, row++ )
01091         {
01092             float height = HeightFieldUtils::getHeightAtLocation( _heightField.get(), x, y, _extent.xMin(), _extent.yMin(), xInterval, yInterval, interpolation);
01093             dest->setHeight( col, row, height );
01094         }
01095     }
01096 
01097     osg::Vec3d orig( destEx.xMin(), destEx.yMin(), _heightField->getOrigin().z() );
01098     dest->setOrigin( orig );
01099 
01100     return GeoHeightField( dest, destEx, _vsrs.get() );
01101 }
01102 
01103 const GeoExtent&
01104 GeoHeightField::getExtent() const
01105 {
01106     return _extent;
01107 }
01108 
01109 const osg::HeightField*
01110 GeoHeightField::getHeightField() const
01111 {
01112     return _heightField.get();
01113 }
01114 
01115 osg::HeightField*
01116 GeoHeightField::getHeightField() 
01117 {
01118     return _heightField.get();
01119 }
01120 
01121 osg::HeightField*
01122 GeoHeightField::takeHeightField()
01123 {
01124     return _heightField.release();
01125 }
01126 
01127 // --------------------------------------------------------------------------
01128 
01129 #undef  LC
01130 #define LC "[Geoid] "
01131 
01132 Geoid::Geoid() :
01133 _hf( GeoHeightField::INVALID ),
01134 _units( Units::METERS ),
01135 _valid( false )
01136 {
01137     //nop
01138 }
01139 
01140 void
01141 Geoid::setName( const std::string& name )
01142 {
01143     _name = name;
01144     validate();
01145 }
01146 
01147 void
01148 Geoid::setHeightField( const GeoHeightField& hf )
01149 {
01150     _hf = hf;
01151     validate();
01152 }
01153 
01154 void
01155 Geoid::setUnits( const Units& units ) 
01156 {
01157     _units = units;
01158     validate();
01159 }
01160 
01161 void
01162 Geoid::validate()
01163 {
01164     _valid = false;
01165     if ( !_hf.valid() ) {
01166         //OE_WARN << LC << "ILLEGAL GEOID: no heightfield" << std::endl;
01167     }
01168     else if ( !_hf.getExtent().getSRS() || !_hf.getExtent().getSRS()->isGeographic() ) {
01169         OE_WARN << LC << "ILLEGAL GEOID: heightfield must be geodetic" << std::endl;
01170     }
01171     else {
01172         _valid = true;
01173     }
01174 }
01175 
01176 float 
01177 Geoid::getOffset(double lat_deg, double lon_deg, const ElevationInterpolation& interp ) const
01178 {
01179     float result = 0.0f;
01180 
01181     if ( _valid )
01182     {
01183         // first convert the query coordinates to the geoid heightfield range if neccesary.
01184         if ( lat_deg < _hf.getExtent().yMin() )
01185             lat_deg = 90.0 - (-90.0-lat_deg);
01186         else if ( lat_deg > _hf.getExtent().yMax() )
01187             lat_deg = -90 + (lat_deg-90.0);
01188         if ( lon_deg < _hf.getExtent().xMin() )
01189             lon_deg += 360.0;
01190         else if ( lon_deg > _hf.getExtent().xMax() )
01191             lon_deg -= 360.0;
01192 
01193         bool ok = _hf.getElevation( 0L, lon_deg, lat_deg, interp, 0L, result );
01194         if ( !ok )
01195             result = 0.0f;
01196     }
01197 
01198     return result;
01199 }
01200 
01201 bool
01202 Geoid::isEquivalentTo( const Geoid& rhs ) const
01203 {
01204     // weak..
01205     return
01206         _valid &&
01207         _name == rhs._name &&
01208         _hf.getExtent() == rhs._hf.getExtent() &&
01209         _units == rhs._units;
01210 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines