在之前的博客中,我们已经看到了gdal对dem数据的强大的处理功能,其中除了坡度坡向等,还有一个比较厉害的,就是使用DEM生成一个彩色的图像。之前关于这方面也翻译了几篇博客,详见《使用GDAL对DEM渲染处理流程》、《使用阿富汗和巴基斯坦地区的SRTM数据生成山体阴影和彩色地形图》和《使用gdaldem创建彩色地形图和坡度阴影——thematicmapping.org译文(三)》,不过这些都是使用的说明。
把gdaldem的源代码进行了整理,整理了一个使用dem数据生成彩色图像的函数,函数的定义如下。关于第四个参数,sColorMode是个枚举值,一共有三个值,默认的是使用线性插值方式,还有两个是最邻近和严格插值。线性插值就是根据你指定的颜色映射关系,把没有指定的高程值,使用颜色映射关系中最近的两个颜色按照线性插值进行处理,得到最后的颜色;而最邻近就是找到最近的一个颜色,而严格插值就是所有的高程数据都必须对应一个颜色值,要不然就是黑色的。
/** * @brief DEM Color Relief(DEM颜色渲染) * @param pszSrcFilename 输入文件路径 * @param pszColorTextFile 输入颜色映射文件 * 颜色映射文件的格式是:“高程值 red green blue”,如:“1022 255 125 32” * @param pszDstFilename 输出文件路径 * @param sColorMode 颜色插值模式,默认为COLOR_SELECTION_INTERPOLATE * @param bAddAlpha 是否添加Alpha通道,默认为不加 * @param bComputeAtEdges 是否计算边界,默认为不计算 * @param pszFormat 输出文件格式,详细参考GDAL支持数据类型 * @param pProcess 进度条指针 * @return 返回值,表示计算过程中出现的各种错误信息 */ int IMGALG_API DemColorRelief(const char* pszSrcFilename, const char* pszColorTextFile, const char* pszDstFilename, ColorSelectionMode sColorMode = COLOR_SELECTION_INTERPOLATE, bool bAddAlpha = false, bool bComputeAtEdges = false, const char *pszFormat = "GTiff", CProcessBase* pProcess = NULL);下面是源代码,是从gdal\apps\gdaldem.cpp中摘的,不想用下面的,自己可以去这个文件里面自己去找。具体的代码如下:
#pragma region GDALColorRelief /************************************************************************/ /* GDALColorRelief() */ /************************************************************************/ /** * @brief 颜色结构体 */ typedef struct { /*! 值 */ double dfVal; /*! R */ int nR; /*! G */ int nG; /*! B */ int nB; /*! A */ int nA; } ColorAssociation; static int GDALColorReliefSortColors(const void* pA, const void* pB) { ColorAssociation* pC1 = (ColorAssociation*)pA; ColorAssociation* pC2 = (ColorAssociation*)pB; return (pC1->dfVal < pC2->dfVal) ? -1 : (pC1->dfVal == pC2->dfVal) ? 0 : 1; } static int GDALColorReliefGetRGBA (ColorAssociation* pasColorAssociation, int nColorAssociation, double dfVal, ColorSelectionMode eColorSelectionMode, int* pnR, int* pnG, int* pnB, int* pnA) { int i; int lower = 0; int upper = nColorAssociation - 1; int mid; /* Find the index of the first element in the LUT input array that */ /* is not smaller than the dfVal value. */ while(TRUE) { mid = (lower + upper) / 2; if (upper - lower <= 1) { if (dfVal < pasColorAssociation[lower].dfVal) i = lower; else if (dfVal < pasColorAssociation[upper].dfVal) i = upper; else i = upper + 1; break; } else if (pasColorAssociation[mid].dfVal >= dfVal) { upper = mid; } else { lower = mid; } } if (i == 0) { if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY && pasColorAssociation[0].dfVal != dfVal) { *pnR = 0; *pnG = 0; *pnB = 0; *pnA = 0; return FALSE; } else { *pnR = pasColorAssociation[0].nR; *pnG = pasColorAssociation[0].nG; *pnB = pasColorAssociation[0].nB; *pnA = pasColorAssociation[0].nA; return TRUE; } } else if (i == nColorAssociation) { if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY && pasColorAssociation[i-1].dfVal != dfVal) { *pnR = 0; *pnG = 0; *pnB = 0; *pnA = 0; return FALSE; } else { *pnR = pasColorAssociation[i-1].nR; *pnG = pasColorAssociation[i-1].nG; *pnB = pasColorAssociation[i-1].nB; *pnA = pasColorAssociation[i-1].nA; return TRUE; } } else { if (eColorSelectionMode == COLOR_SELECTION_EXACT_ENTRY && pasColorAssociation[i-1].dfVal != dfVal) { *pnR = 0; *pnG = 0; *pnB = 0; *pnA = 0; return FALSE; } if (eColorSelectionMode == COLOR_SELECTION_NEAREST_ENTRY && pasColorAssociation[i-1].dfVal != dfVal) { int index; if (dfVal - pasColorAssociation[i-1].dfVal < pasColorAssociation[i].dfVal - dfVal) index = i -1; else index = i; *pnR = pasColorAssociation[index].nR; *pnG = pasColorAssociation[index].nG; *pnB = pasColorAssociation[index].nB; *pnA = pasColorAssociation[index].nA; return TRUE; } double dfRatio = (dfVal - pasColorAssociation[i-1].dfVal) / (pasColorAssociation[i].dfVal - pasColorAssociation[i-1].dfVal); *pnR = (int)(0.45 + pasColorAssociation[i-1].nR + dfRatio * (pasColorAssociation[i].nR - pasColorAssociation[i-1].nR)); if (*pnR < 0) *pnR = 0; else if (*pnR > 255) *pnR = 255; *pnG = (int)(0.45 + pasColorAssociation[i-1].nG + dfRatio * (pasColorAssociation[i].nG - pasColorAssociation[i-1].nG)); if (*pnG < 0) *pnG = 0; else if (*pnG > 255) *pnG = 255; *pnB = (int)(0.45 + pasColorAssociation[i-1].nB + dfRatio * (pasColorAssociation[i].nB - pasColorAssociation[i-1].nB)); if (*pnB < 0) *pnB = 0; else if (*pnB > 255) *pnB = 255; *pnA = (int)(0.45 + pasColorAssociation[i-1].nA + dfRatio * (pasColorAssociation[i].nA - pasColorAssociation[i-1].nA)); if (*pnA < 0) *pnA = 0; else if (*pnA > 255) *pnA = 255; return TRUE; } } /* dfPct : percentage between 0 and 1 */ static double GDALColorReliefGetAbsoluteValFromPct(GDALRasterBandH hSrcBand, double dfPct) { double dfMin, dfMax; int bSuccessMin, bSuccessMax; dfMin = GDALGetRasterMinimum(hSrcBand, &bSuccessMin); dfMax = GDALGetRasterMaximum(hSrcBand, &bSuccessMax); if (!bSuccessMin || !bSuccessMax) { double dfMean, dfStdDev; fprintf(stderr, "Computing source raster statistics...\n"); GDALComputeRasterStatistics(hSrcBand, FALSE, &dfMin, &dfMax, &dfMean, &dfStdDev, NULL, NULL); } return dfMin + dfPct * (dfMax - dfMin); } /** * @brief 名称颜色 */ typedef struct { /*! 名称 */ const char *name; /*! RGB颜色R */ float r; /*! RGB颜色G */ float g; /*! RGB颜色B */ float b; } NamedColor; static const NamedColor namedColors[] = { { "white", 1.00, 1.00, 1.00 }, { "black", 0.00, 0.00, 0.00 }, { "red", 1.00, 0.00, 0.00 }, { "green", 0.00, 1.00, 0.00 }, { "blue", 0.00, 0.00, 1.00 }, { "yellow", 1.00, 1.00, 0.00 }, { "magenta", 1.00, 0.00, 1.00 }, { "cyan", 0.00, 1.00, 1.00 }, { "aqua", 0.00, 0.75, 0.75 }, { "grey", 0.75, 0.75, 0.75 }, { "gray", 0.75, 0.75, 0.75 }, { "orange", 1.00, 0.50, 0.00 }, { "brown", 0.75, 0.50, 0.25 }, { "purple", 0.50, 0.00, 1.00 }, { "violet", 0.50, 0.00, 1.00 }, { "indigo", 0.00, 0.50, 1.00 }, }; static int GDALColorReliefFindNamedColor(const char *pszColorName, int *pnR, int *pnG, int *pnB) { unsigned int i; *pnR = *pnG = *pnB = 0; for (i = 0; i < sizeof(namedColors) / sizeof(namedColors[0]); i++) { if (EQUAL(pszColorName, namedColors[i].name)) { *pnR = (int)(255. * namedColors[i].r); *pnG = (int)(255. * namedColors[i].g); *pnB = (int)(255. * namedColors[i].b); return TRUE; } } return FALSE; } static ColorAssociation* GDALColorReliefParseColorFile(GDALRasterBandH hSrcBand, const char* pszColorFilename, int* pnColors) { FILE* fpColorFile = VSIFOpenL(pszColorFilename, "rt"); if (fpColorFile == NULL) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", pszColorFilename); *pnColors = 0; return NULL; } ColorAssociation* pasColorAssociation = NULL; int nColorAssociation = 0; int bSrcHasNoData = FALSE; double dfSrcNoDataValue = GDALGetRasterNoDataValue(hSrcBand, &bSrcHasNoData); const char* pszLine; while ((pszLine = CPLReadLineL(fpColorFile)) != NULL) { char** papszFields = CSLTokenizeStringComplex(pszLine, " ,\t:", FALSE, FALSE ); /* Skip comment lines */ int nTokens = CSLCount(papszFields); if (nTokens >= 2 && papszFields[0][0] != '#' && papszFields[0][0] != '/') { pasColorAssociation = (ColorAssociation*)CPLRealloc(pasColorAssociation, (nColorAssociation + 1) * sizeof(ColorAssociation)); if (EQUAL(papszFields[0], "nv") && bSrcHasNoData) pasColorAssociation[nColorAssociation].dfVal = dfSrcNoDataValue; else if (strlen(papszFields[0]) > 1 && papszFields[0][strlen(papszFields[0])-1] == '%') { double dfPct = atof(papszFields[0]) / 100.; if (dfPct < 0.0 || dfPct > 1.0) { CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for a percentage : %s", papszFields[0]); CSLDestroy(papszFields); VSIFCloseL(fpColorFile); CPLFree(pasColorAssociation); *pnColors = 0; return NULL; } pasColorAssociation[nColorAssociation].dfVal = GDALColorReliefGetAbsoluteValFromPct(hSrcBand, dfPct); } else pasColorAssociation[nColorAssociation].dfVal = atof(papszFields[0]); if (nTokens >= 4) { pasColorAssociation[nColorAssociation].nR = atoi(papszFields[1]); pasColorAssociation[nColorAssociation].nG = atoi(papszFields[2]); pasColorAssociation[nColorAssociation].nB = atoi(papszFields[3]); pasColorAssociation[nColorAssociation].nA = (CSLCount(papszFields) >= 5 ) ? atoi(papszFields[4]) : 255; } else { int nR, nG, nB; if (!GDALColorReliefFindNamedColor(papszFields[1], &nR, &nG, &nB)) { CPLError(CE_Failure, CPLE_AppDefined, "Unknown color : %s", papszFields[1]); CSLDestroy(papszFields); VSIFCloseL(fpColorFile); CPLFree(pasColorAssociation); *pnColors = 0; return NULL; } pasColorAssociation[nColorAssociation].nR = nR; pasColorAssociation[nColorAssociation].nG = nG; pasColorAssociation[nColorAssociation].nB = nB; pasColorAssociation[nColorAssociation].nA = (CSLCount(papszFields) >= 3 ) ? atoi(papszFields[2]) : 255; } nColorAssociation ++; } CSLDestroy(papszFields); } VSIFCloseL(fpColorFile); if (nColorAssociation == 0) { CPLError(CE_Failure, CPLE_AppDefined, "No color association found in %s", pszColorFilename); *pnColors = 0; return NULL; } qsort(pasColorAssociation, nColorAssociation, sizeof(ColorAssociation), GDALColorReliefSortColors); *pnColors = nColorAssociation; return pasColorAssociation; } static GByte* GDALColorReliefPrecompute(GDALRasterBandH hSrcBand, ColorAssociation* pasColorAssociation, int nColorAssociation, ColorSelectionMode eColorSelectionMode, int* pnIndexOffset) { GDALDataType eDT = GDALGetRasterDataType(hSrcBand); GByte* pabyPrecomputed = NULL; int nIndexOffset = (eDT == GDT_Int16) ? 32768 : 0; *pnIndexOffset = nIndexOffset; int nXSize = GDALGetRasterBandXSize(hSrcBand); int nYSize = GDALGetRasterBandXSize(hSrcBand); if (eDT == GDT_Byte || ((eDT == GDT_Int16 || eDT == GDT_UInt16) && nXSize * nYSize > 65536)) { int iMax = (eDT == GDT_Byte) ? 256: 65536; pabyPrecomputed = (GByte*) VSIMalloc(4 * iMax); if (pabyPrecomputed) { int i; for(i=0; i<iMax; i++) { int nR, nG, nB, nA; GDALColorReliefGetRGBA (pasColorAssociation, nColorAssociation, i - nIndexOffset, eColorSelectionMode, &nR, &nG, &nB, &nA); pabyPrecomputed[4 * i] = (GByte) nR; pabyPrecomputed[4 * i + 1] = (GByte) nG; pabyPrecomputed[4 * i + 2] = (GByte) nB; pabyPrecomputed[4 * i + 3] = (GByte) nA; } } } return pabyPrecomputed; } CPLErr GDALColorRelief (GDALRasterBandH hSrcBand, GDALRasterBandH hDstBand1, GDALRasterBandH hDstBand2, GDALRasterBandH hDstBand3, GDALRasterBandH hDstBand4, const char* pszColorFilename, ColorSelectionMode eColorSelectionMode, GDALProgressFunc pfnProgress, void * pProgressData) { CPLErr eErr; if (hSrcBand == NULL || hDstBand1 == NULL || hDstBand2 == NULL || hDstBand3 == NULL) return CE_Failure; int nColorAssociation = 0; ColorAssociation* pasColorAssociation = GDALColorReliefParseColorFile(hSrcBand, pszColorFilename, &nColorAssociation); if (pasColorAssociation == NULL) return CE_Failure; int nXSize = GDALGetRasterBandXSize(hSrcBand); int nYSize = GDALGetRasterBandYSize(hSrcBand); if (pfnProgress == NULL) pfnProgress = GDALDummyProgress; int nR = 0, nG = 0, nB = 0, nA = 0; /* -------------------------------------------------------------------- */ /* Precompute the map from values to RGBA quadruplets */ /* for GDT_Byte, GDT_Int16 or GDT_UInt16 */ /* -------------------------------------------------------------------- */ int nIndexOffset = 0; GByte* pabyPrecomputed = GDALColorReliefPrecompute(hSrcBand, pasColorAssociation, nColorAssociation, eColorSelectionMode, &nIndexOffset); /* -------------------------------------------------------------------- */ /* Initialize progress counter. */ /* -------------------------------------------------------------------- */ float* pafSourceBuf = NULL; int* panSourceBuf = NULL; if (pabyPrecomputed) panSourceBuf = (int *) CPLMalloc(sizeof(int)*nXSize); else pafSourceBuf = (float *) CPLMalloc(sizeof(float)*nXSize); GByte* pabyDestBuf1 = (GByte*) CPLMalloc( 4 * nXSize ); GByte* pabyDestBuf2 = pabyDestBuf1 + nXSize; GByte* pabyDestBuf3 = pabyDestBuf2 + nXSize; GByte* pabyDestBuf4 = pabyDestBuf3 + nXSize; int i, j; if( !pfnProgress( 0.0, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; goto end; } for ( i = 0; i < nYSize; i++) { /* Read source buffer */ eErr = GDALRasterIO( hSrcBand, GF_Read, 0, i, nXSize, 1, (panSourceBuf) ? (void*) panSourceBuf : (void* )pafSourceBuf, nXSize, 1, (panSourceBuf) ? GDT_Int32 : GDT_Float32, 0, 0); if (eErr != CE_None) goto end; if (panSourceBuf) { for ( j = 0; j < nXSize; j++) { int nIndex = panSourceBuf[j] + nIndexOffset; pabyDestBuf1[j] = pabyPrecomputed[4 * nIndex]; pabyDestBuf2[j] = pabyPrecomputed[4 * nIndex + 1]; pabyDestBuf3[j] = pabyPrecomputed[4 * nIndex + 2]; pabyDestBuf4[j] = pabyPrecomputed[4 * nIndex + 3]; } } else { for ( j = 0; j < nXSize; j++) { GDALColorReliefGetRGBA (pasColorAssociation, nColorAssociation, pafSourceBuf[j], eColorSelectionMode, &nR, &nG, &nB, &nA); pabyDestBuf1[j] = (GByte) nR; pabyDestBuf2[j] = (GByte) nG; pabyDestBuf3[j] = (GByte) nB; pabyDestBuf4[j] = (GByte) nA; } } /* ----------------------------------------- * Write Line to Raster */ eErr = GDALRasterIO(hDstBand1, GF_Write, 0, i, nXSize, 1, pabyDestBuf1, nXSize, 1, GDT_Byte, 0, 0); if (eErr != CE_None) goto end; eErr = GDALRasterIO(hDstBand2, GF_Write, 0, i, nXSize, 1, pabyDestBuf2, nXSize, 1, GDT_Byte, 0, 0); if (eErr != CE_None) goto end; eErr = GDALRasterIO(hDstBand3, GF_Write, 0, i, nXSize, 1, pabyDestBuf3, nXSize, 1, GDT_Byte, 0, 0); if (eErr != CE_None) goto end; if (hDstBand4) { eErr = GDALRasterIO(hDstBand4, GF_Write, 0, i, nXSize, 1, pabyDestBuf4, nXSize, 1, GDT_Byte, 0, 0); if (eErr != CE_None) goto end; } if( !pfnProgress( 1.0 * (i+1) / nYSize, NULL, pProgressData ) ) { CPLError( CE_Failure, CPLE_UserInterrupt, "User terminated" ); eErr = CE_Failure; goto end; } } pfnProgress( 1.0, NULL, pProgressData ); eErr = CE_None; end: VSIFree(pabyPrecomputed); CPLFree(pafSourceBuf); CPLFree(panSourceBuf); CPLFree(pabyDestBuf1); CPLFree(pasColorAssociation); return eErr; } #pragma endregion上面这部分代码就是渲染的核心函数,下面就是函数的实现代码。下面的代码中如果有函数或者变量没有找到定义,请参考我之前的博客中的内容。
int DemColorRelief(const char* pszSrcFilename, const char* pszColorTextFile, const char* pszDstFilename, ColorSelectionMode sColorMode, bool bAddAlpha, bool bComputeAtEdges , const char *pszFormat, CProcessBase* pProcess) { if(pProcess != NULL) { pProcess->ReSetProcess(); pProcess->SetMessage("正在计算颜色渲染..."); } int nBand = 1; double adfGeoTransform[6]; char **papszCreateOptions = NULL; GDALDatasetH hSrcDataset = NULL; GDALDatasetH hDstDataset = NULL; GDALRasterBandH hSrcBand = NULL; GDALRasterBandH hDstBand = NULL; GDALDriverH hDriver = NULL; GDALProgressFunc pfnProgress = ALGTermProgress; string strColorFile = ""; int nXSize = 0; int nYSize = 0; if( pszSrcFilename == NULL ) { if(pProcess != NULL) pProcess->SetMessage("源文件路径为空!"); return RE_FILENOTEXIST; } if( pszColorTextFile == NULL || EQUAL(pszColorTextFile, "")) { const char* pszGDAL_Data = CPLGetConfigOption("GDAL_DATA", "data"); //获取GDAL_DATA目录 strColorFile = string(pszGDAL_Data) + "/dem_color_relief.clr"; } else strColorFile = pszColorTextFile; if( pszDstFilename == NULL ) { if(pProcess != NULL) pProcess->SetMessage("输出文件路径为空!"); return RE_FILENOTEXIST; } if(FileIsUsed(pszDstFilename, pProcess)) return RE_FILEISUESED; GDALAllRegister(); //打开图像 hSrcDataset = GDALOpen( pszSrcFilename, GA_ReadOnly ); if( hSrcDataset == NULL ) { if(pProcess != NULL) pProcess->SetMessage("输入文件不能打开!"); return RE_FILENOTEXIST; } nXSize = GDALGetRasterXSize(hSrcDataset); nYSize = GDALGetRasterYSize(hSrcDataset); if( nBand <= 0 || nBand > GDALGetRasterCount(hSrcDataset) ) { if(pProcess != NULL) pProcess->SetMessage("指定计算的波段不正确!"); return RE_PARAMERROR; } hSrcBand = GDALGetRasterBand( hSrcDataset, nBand ); GDALGetGeoTransform(hSrcDataset, adfGeoTransform); hDriver = GDALGetDriverByName(pszFormat); if( hDriver == NULL) { if(pProcess != NULL) pProcess->SetMessage("不能创建制定类型的文件,请检查该文件类型GDAL是否支持创建!"); GDALClose( hSrcDataset ); return RE_FILENOTSUPPORT; } //设置输出文件数据类型 GDALDataType eDstDataType = GDT_Byte; int nDstBands = (bAddAlpha) ? 4 : 3; hDstDataset = GDALCreate(hDriver, pszDstFilename, nXSize, nYSize, nDstBands, eDstDataType, papszCreateOptions); if( hDstDataset == NULL ) { if(pProcess != NULL) pProcess->SetMessage("创建输出文件失败,请检查文件路径是否正确!"); GDALClose( hSrcDataset ); return RE_CREATEFAILED; //创建图像失败 } hDstBand = GDALGetRasterBand( hDstDataset, 1 ); GDALSetGeoTransform(hDstDataset, adfGeoTransform); GDALSetProjection(hDstDataset, GDALGetProjectionRef(hSrcDataset)); if (GDALColorRelief (hSrcBand, GDALGetRasterBand(hDstDataset, 1), GDALGetRasterBand(hDstDataset, 2), GDALGetRasterBand(hDstDataset, 3), (bAddAlpha) ? GDALGetRasterBand(hDstDataset, 4) : NULL, strColorFile.c_str(), sColorMode, pfnProgress, pProcess) != CE_None) { if(pProcess!=NULL) { if(!pProcess->m_bIsContinue) { GDALClose(hSrcDataset); GDALClose(hDstDataset); GDALDeleteDataset(hDstDataset, pszDstFilename); //删除结果文件 CSLDestroy( papszCreateOptions ); return RE_USERCANCEL; } } GDALClose(hSrcDataset); GDALClose(hDstDataset); GDALDeleteDataset(hDstDataset, pszDstFilename); //删除结果文件 CSLDestroy( papszCreateOptions); return RE_FAILED; } GDALClose(hSrcDataset); GDALClose(hDstDataset); CSLDestroy( papszCreateOptions ); return RE_SUCCESS; }这里需要说明的是,上面的代码中,DEM渲染的颜色文件是一个可选的参数,因为我自己写了一个颜色映射文件,放在GDAL的data目录下面了。通过这个例子,大家也可以学习怎么使用gdal的data目录里的文件了。
dem_color_relief.clr的内容如下,其实就是之前翻译的那篇文章里面的颜色映射,URL为“http://blog.csdn.net/liminlu0314/article/details/8550410”。
0 110 220 110 900 240 250 160 1300 230 220 170 1900 220 220 220 2500 250 250 250至此,使用上面的函数就可以对一个DEM数据进行渲染了,下面贴一下处理的结果。使用上面的颜色映射渲染的结果如图2所示;下面还有一组颜色映射,这个颜色更鲜艳一些,渲染后的图如图3所示。
100% 255 0 0 255 45.5% 255 255 0 255 20.5% 0 255 0 255 6.7% 0 250 255 255 3.5% 0 128 255 255 1.7% 0 64 255 255 0% 0 10 255 255 nv 0 0 0 0