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/TileSource> 00021 #include <osgEarth/FileUtils> 00022 #include <osgEarth/Registry> 00023 #include <osgEarth/ImageUtils> 00024 00025 #include <osgDB/FileNameUtils> 00026 #include <osgDB/FileUtils> 00027 #include <osgDB/Registry> 00028 #include <osgDB/ReadFile> 00029 #include <osgDB/WriteFile> 00030 #include <osgDB/ImageOptions> 00031 00032 #include <sstream> 00033 #include <stdlib.h> 00034 #include <memory.h> 00035 00036 #include <gdal_priv.h> 00037 #include <gdalwarper.h> 00038 #include <ogr_spatialref.h> 00039 00040 #include "GDALOptions" 00041 00042 #define LC "[GDAL driver] " 00043 00044 // From easyrgb.com 00045 float Hue_2_RGB( float v1, float v2, float vH ) 00046 { 00047 if ( vH < 0 ) vH += 1; 00048 if ( vH > 1 ) vH -= 1; 00049 if ( ( 6 * vH ) < 1 ) return ( v1 + ( v2 - v1 ) * 6 * vH ); 00050 if ( ( 2 * vH ) < 1 ) return ( v2 ); 00051 if ( ( 3 * vH ) < 2 ) return ( v1 + ( v2 - v1 ) * ( ( 2 / 3 ) - vH ) * 6 ); 00052 return ( v1 ); 00053 } 00054 00055 #if (GDAL_VERSION_MAJOR > 1 || (GDAL_VERSION_MAJOR >= 1 && GDAL_VERSION_MINOR >= 5)) 00056 # define GDAL_VERSION_1_5_OR_NEWER 1 00057 #endif 00058 00059 #if (GDAL_VERSION_MAJOR > 1 || (GDAL_VERSION_MAJOR >= 1 && GDAL_VERSION_MINOR >= 6)) 00060 # define GDAL_VERSION_1_6_OR_NEWER 1 00061 #endif 00062 00063 #ifndef GDAL_VERSION_1_5_OR_NEWER 00064 # error "**** GDAL 1.5 or newer required ****" 00065 #endif 00066 00067 //GDAL proxy is only available after GDAL 1.6 00068 #if GDAL_VERSION_1_6_OR_NEWER 00069 # include <gdal_proxy.h> 00070 #endif 00071 00072 #include <cpl_string.h> 00073 00074 //GDAL VRT api is only available after 1.5.0 00075 #include <gdal_vrt.h> 00076 00077 using namespace std; 00078 using namespace osgEarth; 00079 using namespace osgEarth::Drivers; 00080 00081 #define GEOTRSFRM_TOPLEFT_X 0 00082 #define GEOTRSFRM_WE_RES 1 00083 #define GEOTRSFRM_ROTATION_PARAM1 2 00084 #define GEOTRSFRM_TOPLEFT_Y 3 00085 #define GEOTRSFRM_ROTATION_PARAM2 4 00086 #define GEOTRSFRM_NS_RES 5 00087 00088 00089 00090 typedef enum 00091 { 00092 LOWEST_RESOLUTION, 00093 HIGHEST_RESOLUTION, 00094 AVERAGE_RESOLUTION 00095 } ResolutionStrategy; 00096 00097 typedef struct 00098 { 00099 int isFileOK; 00100 int nRasterXSize; 00101 int nRasterYSize; 00102 double adfGeoTransform[6]; 00103 int nBlockXSize; 00104 int nBlockYSize; 00105 } DatasetProperty; 00106 00107 typedef struct 00108 { 00109 GDALColorInterp colorInterpretation; 00110 GDALDataType dataType; 00111 GDALColorTableH colorTable; 00112 int bHasNoData; 00113 double noDataValue; 00114 } BandProperty; 00115 00116 static void 00117 getFiles(const std::string &file, const std::vector<std::string> &exts, std::vector<std::string> &files) 00118 { 00119 if (osgDB::fileType(file) == osgDB::DIRECTORY) 00120 { 00121 osgDB::DirectoryContents contents = osgDB::getDirectoryContents(file); 00122 for (osgDB::DirectoryContents::iterator itr = contents.begin(); itr != contents.end(); ++itr) 00123 { 00124 if (*itr == "." || *itr == "..") continue; 00125 std::string f = osgDB::concatPaths(file, *itr); 00126 getFiles(f, exts, files); 00127 } 00128 } 00129 else 00130 { 00131 bool fileValid = false; 00132 //If we have no _extensions specified, assume we should try everything 00133 if (exts.size() == 0) 00134 { 00135 fileValid = true; 00136 } 00137 else 00138 { 00139 //Only accept files with the given _extensions 00140 std::string ext = osgDB::getFileExtension(file); 00141 for (unsigned int i = 0; i < exts.size(); ++i) 00142 { 00143 if (osgDB::equalCaseInsensitive(ext, exts[i])) 00144 { 00145 fileValid = true; 00146 break; 00147 } 00148 } 00149 } 00150 00151 if (fileValid) 00152 { 00153 files.push_back(osgDB::convertFileNameToNativeStyle(file)); 00154 } 00155 } 00156 } 00157 00158 // "build_vrt()" is adapted from the gdalbuildvrt application. Following is 00159 // the copyright notice from the source. The original code can be found at 00160 // http://trac.osgeo.org/gdal/browser/trunk/gdal/apps/gdalbuildvrt.cpp 00161 00162 /****************************************************************************** 00163 * Copyright (c) 2007, Even Rouault 00164 * 00165 * Permission is hereby granted, free of charge, to any person obtaining a 00166 * copy of this software and associated documentation files (the "Software"), 00167 * to deal in the Software without restriction, including without limitation 00168 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 00169 * and/or sell copies of the Software, and to permit persons to whom the 00170 * Software is furnished to do so, subject to the following conditions: 00171 * 00172 * The above copyright notice and this permission notice shall be included 00173 * in all copies or substantial portions of the Software. 00174 * 00175 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 00176 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 00177 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 00178 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 00179 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 00180 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 00181 * DEALINGS IN THE SOFTWARE. 00182 ****************************************************************************/ 00183 static GDALDatasetH 00184 build_vrt(std::vector<std::string> &files, ResolutionStrategy resolutionStrategy) 00185 { 00186 GDAL_SCOPED_LOCK; 00187 00188 char* projectionRef = NULL; 00189 int nBands = 0; 00190 BandProperty* bandProperties = NULL; 00191 double minX = 0, minY = 0, maxX = 0, maxY = 0; 00192 int i,j; 00193 double we_res = 0; 00194 double ns_res = 0; 00195 int rasterXSize; 00196 int rasterYSize; 00197 int nCount = 0; 00198 int bFirst = TRUE; 00199 VRTDatasetH hVRTDS = NULL; 00200 00201 int nInputFiles = files.size(); 00202 00203 DatasetProperty* psDatasetProperties = 00204 (DatasetProperty*) CPLMalloc(nInputFiles*sizeof(DatasetProperty)); 00205 00206 for(i=0;i<nInputFiles;i++) 00207 { 00208 const char* dsFileName = files[i].c_str(); 00209 00210 GDALTermProgress( 1.0 * (i+1) / nInputFiles, NULL, NULL); 00211 00212 GDALDatasetH hDS = GDALOpen(dsFileName, GA_ReadOnly ); 00213 psDatasetProperties[i].isFileOK = FALSE; 00214 00215 if (hDS) 00216 { 00217 const char* proj = GDALGetProjectionRef(hDS); 00218 if (!proj || strlen(proj) == 0) 00219 { 00220 std::string prjLocation = osgDB::getNameLessExtension( std::string(dsFileName) ) + std::string(".prj"); 00221 std::string wkt; 00222 if ( HTTPClient::readString( prjLocation, wkt ) == HTTPClient::RESULT_OK ) 00223 { 00224 proj = CPLStrdup( wkt.c_str() ); 00225 } 00226 } 00227 00228 GDALGetGeoTransform(hDS, psDatasetProperties[i].adfGeoTransform); 00229 if (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] != 0 || 00230 psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] != 0) 00231 { 00232 fprintf( stderr, "GDAL Driver does not support rotated geo transforms. Skipping %s\n", 00233 dsFileName); 00234 GDALClose(hDS); 00235 continue; 00236 } 00237 if (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES] >= 0) 00238 { 00239 fprintf( stderr, "GDAL Driver does not support positive NS resolution. Skipping %s\n", 00240 dsFileName); 00241 GDALClose(hDS); 00242 continue; 00243 } 00244 psDatasetProperties[i].nRasterXSize = GDALGetRasterXSize(hDS); 00245 psDatasetProperties[i].nRasterYSize = GDALGetRasterYSize(hDS); 00246 double product_minX = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_X]; 00247 double product_maxY = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]; 00248 double product_maxX = product_minX + 00249 GDALGetRasterXSize(hDS) * psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]; 00250 double product_minY = product_maxY + 00251 GDALGetRasterYSize(hDS) * psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]; 00252 00253 GDALGetBlockSize(GDALGetRasterBand( hDS, 1 ), 00254 &psDatasetProperties[i].nBlockXSize, 00255 &psDatasetProperties[i].nBlockYSize); 00256 00257 if (bFirst) 00258 { 00259 if (proj) 00260 projectionRef = CPLStrdup(proj); 00261 minX = product_minX; 00262 minY = product_minY; 00263 maxX = product_maxX; 00264 maxY = product_maxY; 00265 nBands = GDALGetRasterCount(hDS); 00266 bandProperties = (BandProperty*)CPLMalloc(nBands*sizeof(BandProperty)); 00267 for(j=0;j<nBands;j++) 00268 { 00269 GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 ); 00270 bandProperties[j].colorInterpretation = GDALGetRasterColorInterpretation(hRasterBand); 00271 bandProperties[j].dataType = GDALGetRasterDataType(hRasterBand); 00272 if (bandProperties[j].colorInterpretation == GCI_PaletteIndex) 00273 { 00274 bandProperties[j].colorTable = GDALGetRasterColorTable( hRasterBand ); 00275 if (bandProperties[j].colorTable) 00276 { 00277 bandProperties[j].colorTable = GDALCloneColorTable(bandProperties[j].colorTable); 00278 } 00279 } 00280 else 00281 bandProperties[j].colorTable = 0; 00282 bandProperties[j].noDataValue = GDALGetRasterNoDataValue(hRasterBand, &bandProperties[j].bHasNoData); 00283 } 00284 } 00285 else 00286 { 00287 if ((proj != NULL && projectionRef == NULL) || 00288 (proj == NULL && projectionRef != NULL) || 00289 (proj != NULL && projectionRef != NULL && EQUAL(proj, projectionRef) == FALSE)) 00290 { 00291 fprintf( stderr, "gdalbuildvrt does not support heterogenous projection. Skipping %s\n",dsFileName); 00292 GDALClose(hDS); 00293 continue; 00294 } 00295 int _nBands = GDALGetRasterCount(hDS); 00296 if (nBands != _nBands) 00297 { 00298 fprintf( stderr, "gdalbuildvrt does not support heterogenous band numbers. Skipping %s\n", 00299 dsFileName); 00300 GDALClose(hDS); 00301 continue; 00302 } 00303 for(j=0;j<nBands;j++) 00304 { 00305 GDALRasterBandH hRasterBand = GDALGetRasterBand( hDS, j+1 ); 00306 if (bandProperties[j].colorInterpretation != GDALGetRasterColorInterpretation(hRasterBand) || 00307 bandProperties[j].dataType != GDALGetRasterDataType(hRasterBand)) 00308 { 00309 fprintf( stderr, "gdalbuildvrt does not support heterogenous band characteristics. Skipping %s\n", 00310 dsFileName); 00311 GDALClose(hDS); 00312 } 00313 if (bandProperties[j].colorTable) 00314 { 00315 GDALColorTableH colorTable = GDALGetRasterColorTable( hRasterBand ); 00316 if (colorTable == NULL || 00317 GDALGetColorEntryCount(colorTable) != GDALGetColorEntryCount(bandProperties[j].colorTable)) 00318 { 00319 fprintf( stderr, "gdalbuildvrt does not support heterogenous band characteristics. Skipping %s\n", 00320 dsFileName); 00321 GDALClose(hDS); 00322 break; 00323 } 00324 /* We should check that the palette are the same too ! */ 00325 } 00326 } 00327 if (j != nBands) 00328 continue; 00329 if (product_minX < minX) minX = product_minX; 00330 if (product_minY < minY) minY = product_minY; 00331 if (product_maxX > maxX) maxX = product_maxX; 00332 if (product_maxY > maxY) maxY = product_maxY; 00333 } 00334 if (resolutionStrategy == AVERAGE_RESOLUTION) 00335 { 00336 we_res += psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]; 00337 ns_res += psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]; 00338 } 00339 else 00340 { 00341 if (bFirst) 00342 { 00343 we_res = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]; 00344 ns_res = psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]; 00345 } 00346 else if (resolutionStrategy == HIGHEST_RESOLUTION) 00347 { 00348 we_res = MIN(we_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]); 00349 /* Yes : as ns_res is negative, the highest resolution is the max value */ 00350 ns_res = MAX(ns_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]); 00351 } 00352 else 00353 { 00354 we_res = MAX(we_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES]); 00355 /* Yes : as ns_res is negative, the lowest resolution is the min value */ 00356 ns_res = MIN(ns_res, psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES]); 00357 } 00358 } 00359 00360 psDatasetProperties[i].isFileOK = 1; 00361 nCount ++; 00362 bFirst = FALSE; 00363 GDALClose(hDS); 00364 } 00365 else 00366 { 00367 fprintf( stderr, "Warning : can't open %s. Skipping it\n", dsFileName); 00368 } 00369 } 00370 00371 if (nCount == 0) 00372 goto end; 00373 00374 if (resolutionStrategy == AVERAGE_RESOLUTION) 00375 { 00376 we_res /= nCount; 00377 ns_res /= nCount; 00378 } 00379 00380 rasterXSize = (int)(0.5 + (maxX - minX) / we_res); 00381 rasterYSize = (int)(0.5 + (maxY - minY) / -ns_res); 00382 00383 hVRTDS = VRTCreate(rasterXSize, rasterYSize); 00384 00385 if (projectionRef) 00386 { 00387 //OE_NOTICE << "Setting projection to " << projectionRef << std::endl; 00388 GDALSetProjection(hVRTDS, projectionRef); 00389 } 00390 00391 double adfGeoTransform[6]; 00392 adfGeoTransform[GEOTRSFRM_TOPLEFT_X] = minX; 00393 adfGeoTransform[GEOTRSFRM_WE_RES] = we_res; 00394 adfGeoTransform[GEOTRSFRM_ROTATION_PARAM1] = 0; 00395 adfGeoTransform[GEOTRSFRM_TOPLEFT_Y] = maxY; 00396 adfGeoTransform[GEOTRSFRM_ROTATION_PARAM2] = 0; 00397 adfGeoTransform[GEOTRSFRM_NS_RES] = ns_res; 00398 GDALSetGeoTransform(hVRTDS, adfGeoTransform); 00399 00400 for(j=0;j<nBands;j++) 00401 { 00402 GDALRasterBandH hBand; 00403 GDALAddBand(hVRTDS, bandProperties[j].dataType, NULL); 00404 hBand = GDALGetRasterBand(hVRTDS, j+1); 00405 GDALSetRasterColorInterpretation(hBand, bandProperties[j].colorInterpretation); 00406 if (bandProperties[j].colorInterpretation == GCI_PaletteIndex) 00407 { 00408 GDALSetRasterColorTable(hBand, bandProperties[j].colorTable); 00409 } 00410 if (bandProperties[j].bHasNoData) 00411 GDALSetRasterNoDataValue(hBand, bandProperties[j].noDataValue); 00412 } 00413 00414 for(i=0;i<nInputFiles;i++) 00415 { 00416 if (psDatasetProperties[i].isFileOK == 0) 00417 continue; 00418 const char* dsFileName = files[i].c_str(); 00419 00420 bool isProxy = true; 00421 00422 #if GDAL_VERSION_1_6_OR_NEWER 00423 00424 //Use a proxy dataset if possible. This helps with huge amount of files to keep the # of handles down 00425 GDALProxyPoolDatasetH hDS = 00426 GDALProxyPoolDatasetCreate(dsFileName, 00427 psDatasetProperties[i].nRasterXSize, 00428 psDatasetProperties[i].nRasterYSize, 00429 GA_ReadOnly, TRUE, projectionRef, 00430 psDatasetProperties[i].adfGeoTransform); 00431 00432 for(j=0;j<nBands;j++) 00433 { 00434 GDALProxyPoolDatasetAddSrcBandDescription(hDS, 00435 bandProperties[j].dataType, 00436 psDatasetProperties[i].nBlockXSize, 00437 psDatasetProperties[i].nBlockYSize); 00438 } 00439 isProxy = true; 00440 OE_DEBUG << LC << "Using GDALProxyPoolDatasetH" << std::endl; 00441 00442 #else // !GDAL_VERSION_1_6_OR_NEWER 00443 00444 OE_DEBUG << LC << "Using GDALDataset, no proxy support enabled" << std::endl; 00445 //Just open the dataset 00446 GDALDatasetH hDS = (GDALDatasetH)GDALOpen(dsFileName, GA_ReadOnly); 00447 isProxy = false; 00448 00449 #endif 00450 00451 int xoffset = (int) 00452 (0.5 + (psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_X] - minX) / we_res); 00453 int yoffset = (int) 00454 (0.5 + (maxY - psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_TOPLEFT_Y]) / -ns_res); 00455 int dest_width = (int) 00456 (0.5 + psDatasetProperties[i].nRasterXSize * psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_WE_RES] / we_res); 00457 int dest_height = (int) 00458 (0.5 + psDatasetProperties[i].nRasterYSize * psDatasetProperties[i].adfGeoTransform[GEOTRSFRM_NS_RES] / ns_res); 00459 00460 for(j=0;j<nBands;j++) 00461 { 00462 VRTSourcedRasterBandH hVRTBand = (VRTSourcedRasterBandH)GDALGetRasterBand(hVRTDS, j + 1); 00463 00464 /* Place the raster band at the right position in the VRT */ 00465 VRTAddSimpleSource(hVRTBand, GDALGetRasterBand((GDALDatasetH)hDS, j + 1), 00466 0, 0, 00467 psDatasetProperties[i].nRasterXSize, 00468 psDatasetProperties[i].nRasterYSize, 00469 xoffset, yoffset, 00470 dest_width, dest_height, "near", 00471 VRT_NODATA_UNSET); 00472 } 00473 //Only dereference if it is a proxy dataset 00474 if (isProxy) 00475 { 00476 GDALDereferenceDataset(hDS); 00477 } 00478 } 00479 end: 00480 CPLFree(psDatasetProperties); 00481 for(j=0;j<nBands;j++) 00482 { 00483 GDALDestroyColorTable(bandProperties[j].colorTable); 00484 } 00485 CPLFree(bandProperties); 00486 CPLFree(projectionRef); 00487 return hVRTDS; 00488 } 00489 00490 00491 // This is simply the method GDALAutoCreateWarpedVRT() with the GDALSuggestedWarpOutput 00492 // logic replaced with something that will work properly for polar projections. 00493 // see: http://www.mail-archive.com/gdal-dev@lists.osgeo.org/msg01491.html 00494 static 00495 GDALDatasetH GDALAutoCreateWarpedVRTforPolarStereographic( 00496 GDALDatasetH hSrcDS, 00497 const char *pszSrcWKT, 00498 const char *pszDstWKT, 00499 GDALResampleAlg eResampleAlg, 00500 double dfMaxError, 00501 const GDALWarpOptions *psOptionsIn ) 00502 { 00503 GDALWarpOptions *psWO; 00504 int i; 00505 00506 VALIDATE_POINTER1( hSrcDS, "GDALAutoCreateWarpedVRTForPolarStereographic", NULL ); 00507 00508 /* -------------------------------------------------------------------- */ 00509 /* Populate the warp options. */ 00510 /* -------------------------------------------------------------------- */ 00511 if( psOptionsIn != NULL ) 00512 psWO = GDALCloneWarpOptions( psOptionsIn ); 00513 else 00514 psWO = GDALCreateWarpOptions(); 00515 00516 psWO->eResampleAlg = eResampleAlg; 00517 00518 psWO->hSrcDS = hSrcDS; 00519 00520 psWO->nBandCount = GDALGetRasterCount( hSrcDS ); 00521 psWO->panSrcBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount); 00522 psWO->panDstBands = (int *) CPLMalloc(sizeof(int) * psWO->nBandCount); 00523 00524 for( i = 0; i < psWO->nBandCount; i++ ) 00525 { 00526 psWO->panSrcBands[i] = i+1; 00527 psWO->panDstBands[i] = i+1; 00528 } 00529 00530 /* TODO: should fill in no data where available */ 00531 00532 /* -------------------------------------------------------------------- */ 00533 /* Create the transformer. */ 00534 /* -------------------------------------------------------------------- */ 00535 psWO->pfnTransformer = GDALGenImgProjTransform; 00536 psWO->pTransformerArg = 00537 GDALCreateGenImgProjTransformer( psWO->hSrcDS, pszSrcWKT, 00538 NULL, pszDstWKT, 00539 TRUE, 1.0, 0 ); 00540 00541 if( psWO->pTransformerArg == NULL ) 00542 { 00543 GDALDestroyWarpOptions( psWO ); 00544 return NULL; 00545 } 00546 00547 /* -------------------------------------------------------------------- */ 00548 /* Figure out the desired output bounds and resolution. */ 00549 /* -------------------------------------------------------------------- */ 00550 double adfDstGeoTransform[6]; 00551 int nDstPixels, nDstLines; 00552 CPLErr eErr; 00553 00554 eErr = 00555 GDALSuggestedWarpOutput( hSrcDS, psWO->pfnTransformer, 00556 psWO->pTransformerArg, 00557 adfDstGeoTransform, &nDstPixels, &nDstLines ); 00558 00559 // override the suggestions: 00560 nDstPixels = GDALGetRasterXSize( hSrcDS ) * 4; 00561 nDstLines = GDALGetRasterYSize( hSrcDS ) / 2; 00562 adfDstGeoTransform[0] = -180.0; 00563 adfDstGeoTransform[1] = 360.0/(double)nDstPixels; 00564 //adfDstGeoTransform[2] = 0.0; 00565 //adfDstGeoTransform[4] = 0.0; 00566 //adfDstGeoTransform[5] = (-90 -adfDstGeoTransform[3])/(double)nDstLines; 00567 00568 /* -------------------------------------------------------------------- */ 00569 /* Update the transformer to include an output geotransform */ 00570 /* back to pixel/line coordinates. */ 00571 /* */ 00572 /* -------------------------------------------------------------------- */ 00573 GDALSetGenImgProjTransformerDstGeoTransform( 00574 psWO->pTransformerArg, adfDstGeoTransform ); 00575 00576 /* -------------------------------------------------------------------- */ 00577 /* Do we want to apply an approximating transformation? */ 00578 /* -------------------------------------------------------------------- */ 00579 if( dfMaxError > 0.0 ) 00580 { 00581 psWO->pTransformerArg = 00582 GDALCreateApproxTransformer( psWO->pfnTransformer, 00583 psWO->pTransformerArg, 00584 dfMaxError ); 00585 psWO->pfnTransformer = GDALApproxTransform; 00586 } 00587 00588 /* -------------------------------------------------------------------- */ 00589 /* Create the VRT file. */ 00590 /* -------------------------------------------------------------------- */ 00591 GDALDatasetH hDstDS; 00592 00593 hDstDS = GDALCreateWarpedVRT( hSrcDS, nDstPixels, nDstLines, 00594 adfDstGeoTransform, psWO ); 00595 00596 GDALDestroyWarpOptions( psWO ); 00597 00598 if( pszDstWKT != NULL ) 00599 GDALSetProjection( hDstDS, pszDstWKT ); 00600 else if( pszSrcWKT != NULL ) 00601 GDALSetProjection( hDstDS, pszDstWKT ); 00602 else if( GDALGetGCPCount( hSrcDS ) > 0 ) 00603 GDALSetProjection( hDstDS, GDALGetGCPProjection( hSrcDS ) ); 00604 else 00605 GDALSetProjection( hDstDS, GDALGetProjectionRef( hSrcDS ) ); 00606 00607 return hDstDS; 00608 } 00609 00610 00611 class GDALTileSource : public TileSource 00612 { 00613 public: 00614 GDALTileSource( const TileSourceOptions& options ) : 00615 TileSource( options ), 00616 _srcDS(NULL), 00617 _warpedDS(NULL), 00618 _options(options), 00619 _maxDataLevel(30) 00620 { 00621 } 00622 00623 virtual ~GDALTileSource() 00624 { 00625 GDAL_SCOPED_LOCK; 00626 00627 if (_warpedDS != _srcDS) 00628 { 00629 GDALClose( _warpedDS ); 00630 } 00631 00632 //Close the datasets if it exists 00633 if (_srcDS) 00634 { 00635 GDALClose(_srcDS); 00636 } 00637 } 00638 00639 00640 void initialize( const std::string& referenceURI, const Profile* overrideProfile) 00641 { 00642 GDAL_SCOPED_LOCK; 00643 00644 if ( !_options.url().isSet() || _options.url()->empty() ) 00645 { 00646 OE_WARN << LC << "No URL or directory specified " << std::endl; 00647 return; 00648 } 00649 00650 URI uri = _options.url().value(); 00651 00652 //Find the full path to the URL 00653 //If we have a relative path and the map file contains a server address, just concat the server path and the _url together 00654 00655 if (osgEarth::isRelativePath(uri.full()) && osgDB::containsServerAddress(referenceURI)) 00656 { 00657 uri = URI(osgDB::getFilePath(referenceURI) + std::string("/") + uri.full()); 00658 } 00659 00660 //If the path doesn't contain a server address, get the full path to the file. 00661 if (!osgDB::containsServerAddress(uri.full())) 00662 { 00663 uri = URI(uri.full(), referenceURI); 00664 } 00665 00666 StringTokenizer izer( ";" ); 00667 StringVector exts; 00668 izer.tokenize( *_options.extensions(), exts ); 00669 00670 //std::vector<std::string> exts; 00671 00672 //tokenize( _options.extensions().value(), exts, ";"); 00673 for (unsigned int i = 0; i < exts.size(); ++i) 00674 { 00675 OE_DEBUG << LC << "Using Extension: " << exts[i] << std::endl; 00676 } 00677 std::vector<std::string> files; 00678 getFiles(uri.full(), exts, files); 00679 00680 OE_INFO << LC << "Driver found " << files.size() << " files:" << std::endl; 00681 for (unsigned int i = 0; i < files.size(); ++i) 00682 { 00683 OE_INFO << LC << "" << files[i] << std::endl; 00684 } 00685 00686 if (files.empty()) 00687 { 00688 OE_WARN << LC << "Could not find any valid files " << std::endl; 00689 return; 00690 } 00691 00692 //If we found more than one file, try to combine them into a single logical dataset 00693 if (files.size() > 1) 00694 { 00695 _srcDS = (GDALDataset*)build_vrt(files, HIGHEST_RESOLUTION); 00696 if (!_srcDS) 00697 { 00698 OE_WARN << "[osgEarth::GDAL] Failed to build VRT from input datasets" << std::endl; 00699 return; 00700 } 00701 } 00702 else 00703 { 00704 //If we couldn't build a VRT, just try opening the file directly 00705 //Open the dataset 00706 _srcDS = (GDALDataset*)GDALOpen( files[0].c_str(), GA_ReadOnly ); 00707 if ( !_srcDS ) 00708 { 00709 OE_WARN << LC << "Failed to open dataset " << files[0] << std::endl; 00710 return; 00711 } 00712 } 00713 00714 //Create a spatial reference for the source. 00715 const char* srcProj = _srcDS->GetProjectionRef(); 00716 if ( srcProj != 0L && overrideProfile != 0L ) 00717 { 00718 OE_WARN << LC << "WARNING, overriding profile of a source that already defines its own SRS (" 00719 << this->getName() << ")" << std::endl; 00720 } 00721 00722 osg::ref_ptr<const SpatialReference> src_srs; 00723 if ( overrideProfile ) 00724 { 00725 src_srs = overrideProfile->getSRS(); 00726 } 00727 else if ( srcProj ) 00728 { 00729 src_srs = SpatialReference::create( srcProj ); 00730 } 00731 00732 // assert SRS is present 00733 if ( !src_srs.valid() ) 00734 { 00735 // not found in the dataset; try loading a .prj file 00736 std::string prjLocation = osgDB::getNameLessExtension( uri.full() ) + std::string(".prj"); 00737 std::string wkt; 00738 if ( HTTPClient::readString( prjLocation, wkt ) == HTTPClient::RESULT_OK ) 00739 { 00740 src_srs = SpatialReference::create( wkt ); 00741 } 00742 00743 if ( !src_srs.valid() ) 00744 { 00745 OE_WARN << LC << "Dataset has no spatial reference information: " << uri.full() << std::endl; 00746 return; 00747 } 00748 } 00749 00750 const Profile* profile = NULL; 00751 00752 if ( overrideProfile ) 00753 { 00754 profile = overrideProfile; 00755 } 00756 00757 if ( !profile && src_srs->isGeographic() ) 00758 { 00759 profile = osgEarth::Registry::instance()->getGlobalGeodeticProfile(); 00760 } 00761 00762 //Note: Can cause odd rendering artifacts if we have a dataset that is mercator that doesn't encompass the whole globe 00763 // if we take on the global profile. 00764 /* 00765 if ( !profile && src_srs->isMercator() ) 00766 { 00767 profile = osgEarth::Registry::instance()->getGlobalMercatorProfile(); 00768 }*/ 00769 00770 std::string warpedSRSWKT; 00771 00772 if ( profile && !profile->getSRS()->isEquivalentTo( src_srs.get() ) ) 00773 { 00774 if ( profile->getSRS()->isGeographic() && (src_srs->isNorthPolar() || src_srs->isSouthPolar()) ) 00775 { 00776 _warpedDS = (GDALDataset*)GDALAutoCreateWarpedVRTforPolarStereographic( 00777 _srcDS, 00778 src_srs->getWKT().c_str(), 00779 profile->getSRS()->getWKT().c_str(), 00780 GRA_NearestNeighbour, 00781 5.0, 00782 NULL); 00783 } 00784 else 00785 { 00786 _warpedDS = (GDALDataset*)GDALAutoCreateWarpedVRT( 00787 _srcDS, 00788 src_srs->getWKT().c_str(), 00789 profile->getSRS()->getWKT().c_str(), 00790 GRA_NearestNeighbour, 00791 5.0, 00792 NULL); 00793 } 00794 00795 if ( _warpedDS ) 00796 { 00797 warpedSRSWKT = _warpedDS->GetProjectionRef(); 00798 } 00799 00800 //GDALAutoCreateWarpedVRT(srcDS, src_wkt.c_str(), t_srs.c_str(), GRA_NearestNeighbour, 5.0, NULL); 00801 } 00802 else 00803 { 00804 _warpedDS = _srcDS; 00805 warpedSRSWKT = src_srs->getWKT(); 00806 } 00807 00808 //Get the _geotransform 00809 if (overrideProfile) 00810 { 00811 _geotransform[0] = overrideProfile->getExtent().xMin(); //Top left x 00812 _geotransform[1] = overrideProfile->getExtent().width() / (double)_warpedDS->GetRasterXSize();//pixel width 00813 _geotransform[2] = 0; 00814 00815 _geotransform[3] = overrideProfile->getExtent().yMax(); //Top left y 00816 _geotransform[4] = 0; 00817 _geotransform[5] = -overrideProfile->getExtent().height() / (double)_warpedDS->GetRasterYSize();//pixel height 00818 00819 } 00820 else 00821 { 00822 _warpedDS->GetGeoTransform(_geotransform); 00823 } 00824 00825 GDALInvGeoTransform(_geotransform, _invtransform); 00826 00827 00828 //Compute the extents 00829 // polar needs a special case when combined with geographic 00830 if ( profile && profile->getSRS()->isGeographic() && (src_srs->isNorthPolar() || src_srs->isSouthPolar()) ) 00831 { 00832 double ll_lon, ll_lat, ul_lon, ul_lat, ur_lon, ur_lat, lr_lon, lr_lat; 00833 00834 pixelToGeo(0.0, 0.0, ul_lon, ul_lat ); 00835 pixelToGeo(0.0, _warpedDS->GetRasterYSize(), ll_lon, ll_lat); 00836 pixelToGeo(_warpedDS->GetRasterXSize(), _warpedDS->GetRasterYSize(), lr_lon, lr_lat); 00837 pixelToGeo(_warpedDS->GetRasterXSize(), 0.0, ur_lon, ur_lat); 00838 00839 _extentsMin.x() = osg::minimum( ll_lon, osg::minimum( ul_lon, osg::minimum( ur_lon, lr_lon ) ) ); 00840 _extentsMax.x() = osg::maximum( ll_lon, osg::maximum( ul_lon, osg::maximum( ur_lon, lr_lon ) ) ); 00841 00842 if ( src_srs->isNorthPolar() ) 00843 { 00844 _extentsMin.y() = osg::minimum( ll_lat, osg::minimum( ul_lat, osg::minimum( ur_lat, lr_lat ) ) ); 00845 _extentsMax.y() = 90.0; 00846 } 00847 else 00848 { 00849 _extentsMin.y() = -90.0; 00850 _extentsMax.y() = osg::maximum( ll_lat, osg::maximum( ul_lat, osg::maximum( ur_lat, lr_lat ) ) ); 00851 } 00852 } 00853 else 00854 { 00855 pixelToGeo(0.0, _warpedDS->GetRasterYSize(), _extentsMin.x(), _extentsMin.y()); 00856 pixelToGeo(_warpedDS->GetRasterXSize(), 0.0, _extentsMax.x(), _extentsMax.y()); 00857 } 00858 00859 OE_INFO << LC << "Geo extents: " << _extentsMin.x() << ", " << _extentsMin.y() << " => " << _extentsMax.x() << ", " << _extentsMax.y() << std::endl; 00860 00861 if ( !profile ) 00862 { 00863 profile = Profile::create( 00864 warpedSRSWKT, 00865 //_warpedDS->GetProjectionRef(), 00866 _extentsMin.x(), _extentsMin.y(), _extentsMax.x(), _extentsMax.y() ); 00867 00868 OE_INFO << LC << "" << uri.full() << " is projected, SRS = " 00869 << warpedSRSWKT << std::endl; 00870 //<< _warpedDS->GetProjectionRef() << std::endl; 00871 } 00872 00873 //Compute the min and max data levels 00874 double resolutionX = (_extentsMax.x() - _extentsMin.x()) / (double)_warpedDS->GetRasterXSize(); 00875 double resolutionY = (_extentsMax.y() - _extentsMin.y()) / (double)_warpedDS->GetRasterYSize(); 00876 00877 double maxResolution = osg::minimum(resolutionX, resolutionY); 00878 00879 OE_INFO << LC << "Resolution= " << resolutionX << "x" << resolutionY << " max=" << maxResolution << std::endl; 00880 00881 if (_options.maxDataLevel().isSet()) 00882 { 00883 _maxDataLevel = _options.maxDataLevel().value(); 00884 OE_INFO << "Using override max data level " << _maxDataLevel << std::endl; 00885 } 00886 else 00887 { 00888 unsigned int max_level = 30; 00889 for (unsigned int i = 0; i < max_level; ++i) 00890 { 00891 _maxDataLevel = i; 00892 double w, h; 00893 profile->getTileDimensions(i, w, h); 00894 double resX = (w / (double)_options.tileSize().value() ); 00895 double resY = (h / (double)_options.tileSize().value() ); 00896 00897 if (resX < maxResolution || resY < maxResolution) 00898 { 00899 break; 00900 } 00901 } 00902 00903 OE_INFO << LC << "Max Data Level: " << _maxDataLevel << std::endl; 00904 } 00905 00906 // record the data extent in profile space: 00907 GeoExtent local_extent( 00908 SpatialReference::create( warpedSRSWKT ), //_warpedDS->GetProjectionRef() ), 00909 _extentsMin.x(), _extentsMin.y(), _extentsMax.x(), _extentsMax.y() ); 00910 GeoExtent profile_extent = local_extent.transform( profile->getSRS() ); 00911 00912 getDataExtents().push_back( DataExtent(profile_extent, 0, _maxDataLevel) ); 00913 00914 OE_INFO << LC << "Data Extents: " << profile_extent.toString() << std::endl; 00915 00916 //Set the profile 00917 setProfile( profile ); 00918 } 00919 00920 00924 static GDALRasterBand* findBand(GDALDataset *ds, GDALColorInterp colorInterp) 00925 { 00926 GDAL_SCOPED_LOCK; 00927 00928 for (int i = 1; i <= ds->GetRasterCount(); ++i) 00929 { 00930 if (ds->GetRasterBand(i)->GetColorInterpretation() == colorInterp) return ds->GetRasterBand(i); 00931 } 00932 return 0; 00933 } 00934 00935 static void getPalleteIndexColor(GDALRasterBand* band, int index, osg::Vec4ub& color) 00936 { 00937 const GDALColorEntry *colorEntry = band->GetColorTable()->GetColorEntry( index ); 00938 GDALPaletteInterp interp = band->GetColorTable()->GetPaletteInterpretation(); 00939 if (!colorEntry) 00940 { 00941 //FIXME: What to do here? 00942 00943 //OE_INFO << "NO COLOR ENTRY FOR COLOR " << rawImageData[i] << std::endl; 00944 color.r() = 255; 00945 color.g() = 0; 00946 color.b() = 0; 00947 color.a() = 1; 00948 00949 } 00950 else 00951 { 00952 if (interp == GPI_RGB) 00953 { 00954 color.r() = colorEntry->c1; 00955 color.g() = colorEntry->c2; 00956 color.b() = colorEntry->c3; 00957 color.a() = colorEntry->c4; 00958 } 00959 else if (interp == GPI_CMYK) 00960 { 00961 // from wikipedia.org 00962 short C = colorEntry->c1; 00963 short M = colorEntry->c2; 00964 short Y = colorEntry->c3; 00965 short K = colorEntry->c4; 00966 color.r() = 255 - C*(255 - K) - K; 00967 color.g() = 255 - M*(255 - K) - K; 00968 color.b() = 255 - Y*(255 - K) - K; 00969 color.a() = 255; 00970 } 00971 else if (interp == GPI_HLS) 00972 { 00973 // from easyrgb.com 00974 float H = colorEntry->c1; 00975 float S = colorEntry->c3; 00976 float L = colorEntry->c2; 00977 float R, G, B; 00978 if ( S == 0 ) //HSL values = 0 - 1 00979 { 00980 R = L; //RGB results = 0 - 1 00981 G = L; 00982 B = L; 00983 } 00984 else 00985 { 00986 float var_2, var_1; 00987 if ( L < 0.5 ) 00988 var_2 = L * ( 1 + S ); 00989 else 00990 var_2 = ( L + S ) - ( S * L ); 00991 00992 var_1 = 2 * L - var_2; 00993 00994 R = Hue_2_RGB( var_1, var_2, H + ( 1 / 3 ) ); 00995 G = Hue_2_RGB( var_1, var_2, H ); 00996 B = Hue_2_RGB( var_1, var_2, H - ( 1 / 3 ) ); 00997 } 00998 color.r() = static_cast<unsigned char>(R*255.0f); 00999 color.g() = static_cast<unsigned char>(G*255.0f); 01000 color.b() = static_cast<unsigned char>(B*255.0f); 01001 color.a() = static_cast<unsigned char>(255.0f); 01002 } 01003 else if (interp == GPI_Gray) 01004 { 01005 color.r() = static_cast<unsigned char>(colorEntry->c1*255.0f); 01006 color.g() = static_cast<unsigned char>(colorEntry->c1*255.0f); 01007 color.b() = static_cast<unsigned char>(colorEntry->c1*255.0f); 01008 color.a() = static_cast<unsigned char>(255.0f); 01009 } 01010 } 01011 } 01012 01013 void pixelToGeo(double x, double y, double &geoX, double &geoY) 01014 { 01015 geoX = _geotransform[0] + _geotransform[1] * x + _geotransform[2] * y; 01016 geoY = _geotransform[3] + _geotransform[4] * x + _geotransform[5] * y; 01017 } 01018 01019 osg::Image* createImage( const TileKey& key, 01020 ProgressCallback* progress) 01021 { 01022 if (key.getLevelOfDetail() > _maxDataLevel) 01023 { 01024 OE_DEBUG << LC << "" << getName() << ": Reached maximum data resolution key=" 01025 << key.getLevelOfDetail() << " max=" << _maxDataLevel << std::endl; 01026 return NULL; 01027 } 01028 01029 GDAL_SCOPED_LOCK; 01030 01031 int tileSize = _options.tileSize().value(); 01032 01033 osg::ref_ptr<osg::Image> image; 01034 if (intersects(key)) //TODO: I think this test is OBE -gw 01035 { 01036 //Get the extents of the tile 01037 double xmin, ymin, xmax, ymax; 01038 key.getExtent().getBounds(xmin, ymin, xmax, ymax); 01039 01040 double dx = (xmax - xmin) / (tileSize-1); 01041 double dy = (ymax - ymin) / (tileSize-1); 01042 01043 01044 int target_width = tileSize; 01045 int target_height = tileSize; 01046 int tile_offset_left = 0; 01047 int tile_offset_top = 0; 01048 01049 int off_x = int((xmin - _geotransform[0]) / _geotransform[1]); 01050 int off_y = int((ymax - _geotransform[3]) / _geotransform[5]); 01051 int width = int(((xmax - _geotransform[0]) / _geotransform[1]) - off_x); 01052 int height = int(((ymin - _geotransform[3]) / _geotransform[5]) - off_y); 01053 01054 if (off_x + width > _warpedDS->GetRasterXSize()) 01055 { 01056 int oversize_right = off_x + width - _warpedDS->GetRasterXSize(); 01057 target_width = target_width - int(float(oversize_right) / width * target_width); 01058 width = _warpedDS->GetRasterXSize() - off_x; 01059 } 01060 01061 if (off_x < 0) 01062 { 01063 int oversize_left = -off_x; 01064 tile_offset_left = int(float(oversize_left) / width * target_width); 01065 target_width = target_width - int(float(oversize_left) / width * target_width); 01066 width = width + off_x; 01067 off_x = 0; 01068 } 01069 01070 if (off_y + height > _warpedDS->GetRasterYSize()) 01071 { 01072 int oversize_bottom = off_y + height - _warpedDS->GetRasterYSize(); 01073 target_height = target_height - (int)osg::round(float(oversize_bottom) / height * target_height); 01074 height = _warpedDS->GetRasterYSize() - off_y; 01075 } 01076 01077 01078 if (off_y < 0) 01079 { 01080 int oversize_top = -off_y; 01081 tile_offset_top = int(float(oversize_top) / height * target_height); 01082 target_height = target_height - int(float(oversize_top) / height * target_height); 01083 height = height + off_y; 01084 off_y = 0; 01085 } 01086 01087 OE_DEBUG << LC << "ReadWindow " << width << "x" << height << " DestWindow " << target_width << "x" << target_height << std::endl; 01088 01089 //Return if parameters are out of range. 01090 if (width <= 0 || height <= 0 || target_width <= 0 || target_height <= 0) 01091 { 01092 return 0; 01093 } 01094 01095 01096 01097 GDALRasterBand* bandRed = findBand(_warpedDS, GCI_RedBand); 01098 GDALRasterBand* bandGreen = findBand(_warpedDS, GCI_GreenBand); 01099 GDALRasterBand* bandBlue = findBand(_warpedDS, GCI_BlueBand); 01100 GDALRasterBand* bandAlpha = findBand(_warpedDS, GCI_AlphaBand); 01101 01102 GDALRasterBand* bandGray = findBand(_warpedDS, GCI_GrayIndex); 01103 01104 GDALRasterBand* bandPalette = findBand(_warpedDS, GCI_PaletteIndex); 01105 01106 //The pixel format is always RGBA to support transparency 01107 GLenum pixelFormat = GL_RGBA; 01108 01109 01110 if (bandRed && bandGreen && bandBlue) 01111 { 01112 unsigned char *red = new unsigned char[target_width * target_height]; 01113 unsigned char *green = new unsigned char[target_width * target_height]; 01114 unsigned char *blue = new unsigned char[target_width * target_height]; 01115 unsigned char *alpha = new unsigned char[target_width * target_height]; 01116 01117 //Initialize the alpha values to 255. 01118 memset(alpha, 255, target_width * target_height); 01119 01120 image = new osg::Image; 01121 image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); 01122 memset(image->data(), 0, image->getImageSizeInBytes()); 01123 01124 //Nearest interpolation just uses RasterIO to sample the imagery and should be very fast. 01125 if (!*_options.interpolateImagery() || _options.interpolation() == INTERP_NEAREST) 01126 { 01127 bandRed->RasterIO(GF_Read, off_x, off_y, width, height, red, target_width, target_height, GDT_Byte, 0, 0); 01128 bandGreen->RasterIO(GF_Read, off_x, off_y, width, height, green, target_width, target_height, GDT_Byte, 0, 0); 01129 bandBlue->RasterIO(GF_Read, off_x, off_y, width, height, blue, target_width, target_height, GDT_Byte, 0, 0); 01130 01131 if (bandAlpha) 01132 { 01133 bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); 01134 } 01135 01136 for (int src_row = 0, dst_row = tile_offset_top; 01137 src_row < target_height; 01138 src_row++, dst_row++) 01139 { 01140 for (int src_col = 0, dst_col = tile_offset_left; 01141 src_col < target_width; 01142 ++src_col, ++dst_col) 01143 { 01144 *(image->data(dst_col, dst_row) + 0) = red[src_col + src_row * target_width]; 01145 *(image->data(dst_col, dst_row) + 1) = green[src_col + src_row * target_width]; 01146 *(image->data(dst_col, dst_row) + 2) = blue[src_col + src_row * target_width]; 01147 *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; 01148 } 01149 } 01150 01151 image->flipVertical(); 01152 } 01153 else 01154 { 01155 //Sample each point exactly 01156 for (unsigned int c = 0; c < tileSize; ++c) 01157 { 01158 double geoX = xmin + (dx * (double)c); 01159 for (unsigned int r = 0; r < tileSize; ++r) 01160 { 01161 double geoY = ymin + (dy * (double)r); 01162 *(image->data(c,r) + 0) = getInterpolatedValue(bandRed, geoX,geoY,false); 01163 *(image->data(c,r) + 1) = getInterpolatedValue(bandGreen,geoX,geoY,false); 01164 *(image->data(c,r) + 2) = getInterpolatedValue(bandBlue, geoX,geoY,false); 01165 if (bandAlpha != NULL) 01166 *(image->data(c,r) + 3) = getInterpolatedValue(bandAlpha,geoX, geoY, false); 01167 else 01168 *(image->data(c,r) + 3) = 255; 01169 } 01170 } 01171 } 01172 01173 delete []red; 01174 delete []green; 01175 delete []blue; 01176 delete []alpha; 01177 } 01178 else if (bandGray) 01179 { 01180 unsigned char *gray = new unsigned char[target_width * target_height]; 01181 unsigned char *alpha = new unsigned char[target_width * target_height]; 01182 01183 //Initialize the alpha values to 255. 01184 memset(alpha, 255, target_width * target_height); 01185 01186 image = new osg::Image; 01187 image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); 01188 memset(image->data(), 0, image->getImageSizeInBytes()); 01189 01190 01191 if (!*_options.interpolateImagery() || _options.interpolation() == INTERP_NEAREST) 01192 { 01193 bandGray->RasterIO(GF_Read, off_x, off_y, width, height, gray, target_width, target_height, GDT_Byte, 0, 0); 01194 01195 if (bandAlpha) 01196 { 01197 bandAlpha->RasterIO(GF_Read, off_x, off_y, width, height, alpha, target_width, target_height, GDT_Byte, 0, 0); 01198 } 01199 01200 for (int src_row = 0, dst_row = tile_offset_top; 01201 src_row < target_height; 01202 src_row++, dst_row++) 01203 { 01204 for (int src_col = 0, dst_col = tile_offset_left; 01205 src_col < target_width; 01206 ++src_col, ++dst_col) 01207 { 01208 *(image->data(dst_col, dst_row) + 0) = gray[src_col + src_row * target_width]; 01209 *(image->data(dst_col, dst_row) + 1) = gray[src_col + src_row * target_width]; 01210 *(image->data(dst_col, dst_row) + 2) = gray[src_col + src_row * target_width]; 01211 *(image->data(dst_col, dst_row) + 3) = alpha[src_col + src_row * target_width]; 01212 } 01213 } 01214 01215 image->flipVertical(); 01216 } 01217 else 01218 { 01219 for (int c = 0; c < tileSize; ++c) 01220 { 01221 double geoX = xmin + (dx * (double)c); 01222 01223 for (int r = 0; r < tileSize; ++r) 01224 { 01225 double geoY = ymin + (dy * (double)r); 01226 float color = getInterpolatedValue(bandGray,geoX,geoY,false); 01227 01228 *(image->data(c,r) + 0) = color; 01229 *(image->data(c,r) + 1) = color; 01230 *(image->data(c,r) + 2) = color; 01231 if (bandAlpha != NULL) 01232 *(image->data(c,r) + 3) = getInterpolatedValue(bandAlpha,geoX,geoY,false); 01233 else 01234 *(image->data(c,r) + 3) = 255; 01235 } 01236 } 01237 } 01238 01239 delete []gray; 01240 delete []alpha; 01241 01242 } 01243 else if (bandPalette) 01244 { 01245 //Pallete indexed imagery doesn't support interpolation currently and only uses nearest 01246 //b/c interpolating pallete indexes doesn't make sense. 01247 unsigned char *palette = new unsigned char[target_width * target_height]; 01248 01249 image = new osg::Image; 01250 image->allocateImage(tileSize, tileSize, 1, pixelFormat, GL_UNSIGNED_BYTE); 01251 memset(image->data(), 0, image->getImageSizeInBytes()); 01252 01253 bandPalette->RasterIO(GF_Read, off_x, off_y, width, height, palette, target_width, target_height, GDT_Byte, 0, 0); 01254 01255 for (int src_row = 0, dst_row = tile_offset_top; 01256 src_row < target_height; 01257 src_row++, dst_row++) 01258 { 01259 for (int src_col = 0, dst_col = tile_offset_left; 01260 src_col < target_width; 01261 ++src_col, ++dst_col) 01262 { 01263 osg::Vec4ub color; 01264 getPalleteIndexColor( bandPalette, palette[src_col + src_row * target_width], color ); 01265 01266 *(image->data(dst_col, dst_row) + 0) = color.r(); 01267 *(image->data(dst_col, dst_row) + 1) = color.g(); 01268 *(image->data(dst_col, dst_row) + 2) = color.b(); 01269 *(image->data(dst_col, dst_row) + 3) = color.a(); 01270 } 01271 } 01272 01273 image->flipVertical(); 01274 01275 delete [] palette; 01276 01277 } 01278 else 01279 { 01280 OE_WARN 01281 << LC << "Could not find red, green and blue bands or gray bands in " 01282 << _options.url()->full() 01283 << ". Cannot create image. " << std::endl; 01284 01285 return NULL; 01286 } 01287 } 01288 01289 // Moved this logic up into ImageLayer::createImageWrapper. 01291 //if (!image.valid()) 01292 //{ 01293 // //OE_WARN << LC << "Illegal state-- should not get here" << std::endl; 01294 // return ImageUtils::createEmptyImage(); 01295 //} 01296 return image.release(); 01297 } 01298 01299 bool isValidValue(float v, GDALRasterBand* band) 01300 { 01301 GDAL_SCOPED_LOCK; 01302 01303 float bandNoData = -32767.0f; 01304 int success; 01305 float value = band->GetNoDataValue(&success); 01306 if (success) 01307 { 01308 bandNoData = value; 01309 } 01310 01311 //Check to see if the value is equal to the bands specified no data 01312 if (bandNoData == v) return false; 01313 //Check to see if the value is equal to the user specified nodata value 01314 if (getNoDataValue() == v) return false; 01315 01316 //Check to see if the user specified a custom min/max 01317 if (v < getNoDataMinValue()) return false; 01318 if (v > getNoDataMaxValue()) return false; 01319 01320 //Check within a sensible range 01321 if (v < -32000) return false; 01322 if (v > 32000) return false; 01323 01324 return true; 01325 } 01326 01327 01328 float getInterpolatedValue(GDALRasterBand *band, double x, double y, bool applyOffset=true) 01329 { 01330 double r, c; 01331 GDALApplyGeoTransform(_invtransform, x, y, &c, &r); 01332 01333 //Account for slight rounding errors. If we are right on the edge of the dataset, clamp to the edge 01334 double eps = 0.0001; 01335 if (osg::equivalent(c, 0, eps)) c = 0; 01336 if (osg::equivalent(r, 0, eps)) r = 0; 01337 if (osg::equivalent(c, (double)_warpedDS->GetRasterXSize(), eps)) c = _warpedDS->GetRasterXSize(); 01338 if (osg::equivalent(r, (double)_warpedDS->GetRasterYSize(), eps)) r = _warpedDS->GetRasterYSize(); 01339 01340 if (applyOffset) 01341 { 01342 //Apply half pixel offset 01343 r-= 0.5; 01344 c-= 0.5; 01345 01346 //Account for the half pixel offset in the geotransform. If the pixel value is -0.5 we are still technically in the dataset 01347 //since 0,0 is now the center of the pixel. So, if are within a half pixel above or a half pixel below the dataset just use 01348 //the edge values 01349 if (c < 0 && c >= -0.5) 01350 { 01351 c = 0; 01352 } 01353 else if (c > _warpedDS->GetRasterXSize()-1 && c <= _warpedDS->GetRasterXSize()-0.5) 01354 { 01355 c = _warpedDS->GetRasterXSize()-1; 01356 } 01357 01358 if (r < 0 && r >= -0.5) 01359 { 01360 r = 0; 01361 } 01362 else if (r > _warpedDS->GetRasterYSize()-1 && r <= _warpedDS->GetRasterYSize()-0.5) 01363 { 01364 r = _warpedDS->GetRasterYSize()-1; 01365 } 01366 } 01367 01368 float result = 0.0f; 01369 01370 //If the location is outside of the pixel values of the dataset, just return 0 01371 if (c < 0 || r < 0 || c > _warpedDS->GetRasterXSize()-1 || r > _warpedDS->GetRasterYSize()-1) 01372 return NO_DATA_VALUE; 01373 01374 if ( _options.interpolation() == INTERP_NEAREST ) 01375 { 01376 band->RasterIO(GF_Read, (int)osg::round(c), (int)osg::round(r), 1, 1, &result, 1, 1, GDT_Float32, 0, 0); 01377 if (!isValidValue( result, band)) 01378 { 01379 return NO_DATA_VALUE; 01380 } 01381 } 01382 else 01383 { 01384 int rowMin = osg::maximum((int)floor(r), 0); 01385 int rowMax = osg::maximum(osg::minimum((int)ceil(r), (int)(_warpedDS->GetRasterYSize()-1)), 0); 01386 int colMin = osg::maximum((int)floor(c), 0); 01387 int colMax = osg::maximum(osg::minimum((int)ceil(c), (int)(_warpedDS->GetRasterXSize()-1)), 0); 01388 01389 if (rowMin > rowMax) rowMin = rowMax; 01390 if (colMin > colMax) colMin = colMax; 01391 01392 float urHeight, llHeight, ulHeight, lrHeight; 01393 01394 band->RasterIO(GF_Read, colMin, rowMin, 1, 1, &llHeight, 1, 1, GDT_Float32, 0, 0); 01395 band->RasterIO(GF_Read, colMin, rowMax, 1, 1, &ulHeight, 1, 1, GDT_Float32, 0, 0); 01396 band->RasterIO(GF_Read, colMax, rowMin, 1, 1, &lrHeight, 1, 1, GDT_Float32, 0, 0); 01397 band->RasterIO(GF_Read, colMax, rowMax, 1, 1, &urHeight, 1, 1, GDT_Float32, 0, 0); 01398 01399 /* 01400 if (!isValidValue(urHeight, band)) urHeight = 0.0f; 01401 if (!isValidValue(llHeight, band)) llHeight = 0.0f; 01402 if (!isValidValue(ulHeight, band)) ulHeight = 0.0f; 01403 if (!isValidValue(lrHeight, band)) lrHeight = 0.0f; 01404 */ 01405 if (!isValidValue(urHeight, band) || (!isValidValue(llHeight, band)) ||(!isValidValue(ulHeight, band)) || (!isValidValue(lrHeight, band))) 01406 { 01407 return NO_DATA_VALUE; 01408 } 01409 01410 if ( _options.interpolation() == INTERP_AVERAGE ) 01411 { 01412 double x_rem = c - (int)c; 01413 double y_rem = r - (int)r; 01414 01415 double w00 = (1.0 - y_rem) * (1.0 - x_rem) * (double)llHeight; 01416 double w01 = (1.0 - y_rem) * x_rem * (double)lrHeight; 01417 double w10 = y_rem * (1.0 - x_rem) * (double)ulHeight; 01418 double w11 = y_rem * x_rem * (double)urHeight; 01419 01420 result = (float)(w00 + w01 + w10 + w11); 01421 } 01422 else if ( _options.interpolation() == INTERP_BILINEAR ) 01423 { 01424 //Check for exact value 01425 if ((colMax == colMin) && (rowMax == rowMin)) 01426 { 01427 //OE_NOTICE << "Exact value" << std::endl; 01428 result = llHeight; 01429 } 01430 else if (colMax == colMin) 01431 { 01432 //OE_NOTICE << "Vertically" << std::endl; 01433 //Linear interpolate vertically 01434 result = ((float)rowMax - r) * llHeight + (r - (float)rowMin) * ulHeight; 01435 } 01436 else if (rowMax == rowMin) 01437 { 01438 //OE_NOTICE << "Horizontally" << std::endl; 01439 //Linear interpolate horizontally 01440 result = ((float)colMax - c) * llHeight + (c - (float)colMin) * lrHeight; 01441 } 01442 else 01443 { 01444 //OE_NOTICE << "Bilinear" << std::endl; 01445 //Bilinear interpolate 01446 float r1 = ((float)colMax - c) * llHeight + (c - (float)colMin) * lrHeight; 01447 float r2 = ((float)colMax - c) * ulHeight + (c - (float)colMin) * urHeight; 01448 01449 //OE_INFO << "r1, r2 = " << r1 << " , " << r2 << std::endl; 01450 result = ((float)rowMax - r) * r1 + (r - (float)rowMin) * r2; 01451 } 01452 } 01453 } 01454 01455 return result; 01456 } 01457 01458 01459 osg::HeightField* createHeightField( const TileKey& key, 01460 ProgressCallback* progress) 01461 { 01462 if (key.getLevelOfDetail() > _maxDataLevel) 01463 { 01464 //OE_NOTICE << "Reached maximum data resolution key=" << key.getLevelOfDetail() << " max=" << _maxDataLevel << std::endl; 01465 return NULL; 01466 } 01467 01468 GDAL_SCOPED_LOCK; 01469 01470 int tileSize = _options.tileSize().value(); 01471 01472 //Allocate the heightfield 01473 osg::ref_ptr<osg::HeightField> hf = new osg::HeightField; 01474 hf->allocate(tileSize, tileSize); 01475 01476 if (intersects(key)) 01477 { 01478 //Get the meter extents of the tile 01479 double xmin, ymin, xmax, ymax; 01480 key.getExtent().getBounds(xmin, ymin, xmax, ymax); 01481 01482 //Just read from the first band 01483 GDALRasterBand* band = _warpedDS->GetRasterBand(1); 01484 01485 double dx = (xmax - xmin) / (tileSize-1); 01486 double dy = (ymax - ymin) / (tileSize-1); 01487 01488 for (int c = 0; c < tileSize; ++c) 01489 { 01490 double geoX = xmin + (dx * (double)c); 01491 for (int r = 0; r < tileSize; ++r) 01492 { 01493 double geoY = ymin + (dy * (double)r); 01494 float h = getInterpolatedValue(band, geoX, geoY); 01495 hf->setHeight(c, r, h); 01496 } 01497 } 01498 } 01499 else 01500 { 01501 for (unsigned int i = 0; i < hf->getHeightList().size(); ++i) hf->getHeightList()[i] = NO_DATA_VALUE; 01502 } 01503 return hf.release(); 01504 } 01505 01506 bool intersects(const TileKey& key) 01507 { 01508 //Get the native extents of the tile 01509 double xmin, ymin, xmax, ymax; 01510 key.getExtent().getBounds(xmin, ymin, xmax, ymax); 01511 01512 return ! ( xmin >= _extentsMax.x() || xmax <= _extentsMin.x() || ymin >= _extentsMax.y() || ymax <= _extentsMin.y() ); 01513 } 01514 01515 01516 private: 01517 01518 GDALDataset* _srcDS; 01519 GDALDataset* _warpedDS; 01520 double _geotransform[6]; 01521 double _invtransform[6]; 01522 01523 osg::Vec2d _extentsMin; 01524 osg::Vec2d _extentsMax; 01525 01526 //std::string _url; 01527 //int _tile_size; 01528 //std::string _extensions; 01529 //ElevationInterpolation _interpolation; 01530 01531 const GDALOptions _options; 01532 //osg::ref_ptr<const GDALOptions> _settings; 01533 01534 unsigned int _maxDataLevel; 01535 }; 01536 01537 01538 class ReaderWriterGDALTile : public TileSourceDriver 01539 { 01540 public: 01541 ReaderWriterGDALTile() {} 01542 01543 virtual const char* className() 01544 { 01545 return "GDAL Tile Reader"; 01546 } 01547 01548 virtual bool acceptsExtension(const std::string& extension) const 01549 { 01550 return osgDB::equalCaseInsensitive( extension, "osgearth_gdal" ); 01551 } 01552 01553 virtual ReadResult readObject(const std::string& file_name, const Options* opt) const 01554 { 01555 if ( !acceptsExtension( osgDB::getFileExtension( file_name ) ) ) 01556 { 01557 return ReadResult::FILE_NOT_HANDLED; 01558 } 01559 return new GDALTileSource( getTileSourceOptions(opt) ); 01560 } 01561 }; 01562 01563 REGISTER_OSGPLUGIN(osgearth_gdal, ReaderWriterGDALTile)