在之前的博客中,我们已经看到了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
上面这部分代码就是渲染的核心函数,下面就是函数的实现代码。下面的代码中如果有函数或者变量没有找到定义,请参考我之前的博客中的内容。
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
图1 原始DEM数据
图2 颜色渲染后的结果
图3 颜色渲染后的结果2