三维场景下的通视分析作为GIS中一种重要的分析功能,可应用于旅游中的风景评价,房地产中视线遮挡判断,以及通讯中的信号覆盖,或军事上的火力覆盖等多方面,具有计算结果直观等优点。而通视分析的类型大致有:一点对整个区域的通视面积计算,两点之间的通视性判断,多点通视面积的交集计算,由被覆盖的可视面积反求待定位置与高度等。本文章着重讲解三维场景下前两种通视分析类型的算法和编码。
一点对整个区域的通视面积计算是在给定观察位置后,首先计算此位置与区域内其他顶点之间是否通视,将主要通过下面介绍的TracePoint(…)函数来实现,然后依据通视与否给三维场景中的其他顶点赋黑白颜色。具体代码如下:
bool SinglePointVisibilityAnalysis::VisibilitySinglePointStart(double xaxisnumber, double yaxisnumber, float zaxisadded)
{
int x, y;
double x_Pos, y_Pos;
double dx,dy;
double z_Pos,dz;
m_byRsVisibility = new BYTE[m_iRows * m_iColumns]; //准备设置黑白两种颜色来区分可见与不可见区域
m_byGsVisibility = new BYTE[m_iRows * m_iColumns];
m_byBsVisibility = new BYTE[m_iRows * m_iColumns];
if(!m_p3dDEMGrid->IsInGrid(xaxisnumber, yaxisnumber)) //使用索引的时候注意还原i和j,使用位置的时候注意原点和单位格网长度
{
AfxMessageBox("Point not in Grid");
return false; //点击的位置不在DEM网格内,直接返回
}
//-----------------------------------------------------
long index = 0;
x_Pos = xaxisnumber; //点击位置处X值
y_Pos = yaxisnumber; //点击位置处Y值
index = m_iColumns*(y_Pos - m_dYMin)/m_iYCellSize+(x_Pos - m_dXMin)/m_iXCellSize; //通过点击的位置来计算顶点数组中的索引
z_Pos = m_p3dDEMPoints[index][2] + zaxisadded;//点击位置处Z值加上用户输入的高度数值
//-----------------------------------------------------
for(y=0; y<m_iRows ; y++) //对输入网格Y轴开始循环
{
for(x=0; x<m_iColumns; x++) //对输入网格X轴循环
{
if( m_p3dDEMGrid->IsNodata(x,y))//如果为非赋值数据
{
long tIndex = y * m_iColumns + x; //计算在顶点数组中的索引
m_byRsVisibility[tIndex] = 150; //颜色分量
m_byGsVisibility[tIndex] = 150;
m_byBsVisibility[tIndex] = 150;
continue; //继续下次循环
}
index = y*m_iColumns + x; //计算在顶点数组中的索引
dx = x_Pos - (m_dXMin + x*m_iXCellSize);
dy = y_Pos - (m_dYMin + y*m_iYCellSize);//用户点击处与(x,y)位置处之间实际坐标的差值,也可看成两向量相减
dz = z_Pos - m_p3dDEMPoints[index][2]; //用户点击处与(x,y)位置处之间高度的差值
//-----------------------------------------
if( TracePoint(x, y, dx, dy, dz) ) //若为真,则说明两位置之间是可以相互看见的
{
m_byRsVisibility[index] = 250; //代表照亮(x,y)处
m_byGsVisibility[index] = 250; //代表照亮(x,y)处
m_byBsVisibility[index] = 250; //代表照亮(x,y)处
}
else
{
m_byRsVisibility[index] = 30; //代表(x,y)处不可见
m_byGsVisibility[index] = 30; //代表(x,y)处不可见
m_byBsVisibility[index] = 30; //代表(x,y)处不可见
}
}
}
SetVisibilitySinglePointSurfaceColor();//将m_byBsVisibility[]应用到最后的显示中
return true;
}
bool SinglePointVisibilityAnalysis::TracePoint(int x, int y, double dx, double dy, double dz)
{
double ix, iy, iz, id, d, dist;
d = fabs(dx) > fabs(dy) ? fabs(dx) : fabs(dy); //取dx、dy中绝对值较大的一个
if( d > 0 ) //已经取得绝对值,一般是大于零
{
dist = sqrt(dx*dx + dy*dy); //得到两顶点之间的距离
dx /= d; //dx变成-1 与1之间的一个数
dy /= d; //dy变成-1 与1之间的一个数
dz /= d; //总的高程差被分成几个部分
d = dist / d; //步数
long index0 = y*m_iColumns + x; //要计算的这个点在顶点数组中的索引
id = 0.0;
ix = x + 0.5;
iy = y + 0.5;
iz = m_p3dDEMPoints[index0][2];//准备计算的这个顶点的高程数值
while( id < dist )
{
id += d; //id的数值每次循环增加一步
ix += dx; //X轴的数值增加,向观察处靠近
iy += dy; //Y轴的数值增加,向观察处靠近
iz += dz; //原始高度增加高程差的一个部分
x = (int)ix; //取整,看处于第几列
y = (int)iy; //取整,看处于第几行
int index = y*m_iColumns + x; //计算在顶点数组中的索引
if( !m_p3dDEMGrid->IsInGrid(x, y))
{
return( true );
}
else if( iz < m_p3dDEMPoints[index][2] ) //说明在靠近观察处的过程中,有顶点的高程超过了视线上的高程,所以看不见要计算的顶点
{
return( false );
}
else if( iz > m_dZMax )//若超过整个DEM的最高值,那肯定从观察处看见要计算的顶点
{
return( true );
}
}
}
return( true );
}
两顶点之间的通视就可采用TracePoint(….)函数来计算。
原始场景图
单点通视图
两点通视判断图