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