Qt接管下iObjects C++组件的地图自定义绘制
一、什么为自定义绘制
自定义绘制,顾名思义,就是用自己的方法去绘制地图,由于部分地图绘制任务对地图刷新性能的高要求,如果采用GIS组件提供的内置绘制方法,其绘制过程中会有很多的计算和判断,从而消耗大量的不必要时间。
先要缩短地图绘制时间,我们可以使用自己的绘制方法,从两个方面出发去节省GIS组件绘制地图的时间:
1.节约查询时间:
GIS组件在刷新地图(地图需要绘制)的时候总会先查询出需要绘制的范围内的所有要素,然后再进行绘制,而如果使用自己的方法去绘制,我们可以预先查询得到我们的需要绘制的要素,保存下来,再需要进行绘制的时候,我们即可直接使用我们已有的要素信息来进行绘制。(当然,如果是进行缩放后重绘,我们也需要再判断哪些要素在重绘的图像边界内,但这个查询时间远低于对数据集的信息查询)。
2.节约绘制时间:
GIS组件在绘制地图时需要判断绘制的图像类型、准备画刷、然后再去绘制,其间会有大量的判断过程,当我们有数量巨大的同类型要素需要绘制时,绘制每一个要素的时候都需要进行这样的判断,这是一个非常浪费时间的过程。而如果我们使用自己的方法去绘制,已经知道我们要绘制的对象的类型,我们完全可以使用自己的办法去一次性绘制完成,这样那些不必要的判断时间就节约下来了。
二、Qt下的自定义绘制概述
Qt下使用SuperMap iObjects C++组件的总体思想是:利用GIS组件在进行GIS图像绘制(其间可能会有空间查询、分析等),将绘制好的图像传给Qt的界面组件,由Qt将我们的图像绘制到屏幕上。
而Qt也有自己的渲染器,不止可以完成图像的渲染、显示,同时还可以对图像进行绘制,所以说我们可以通过GIS组件绘制地图底图,并查询出业务数据,将底图和业务数据(需要自定义绘制的要素)都交给Qt,由Qt完成业务数据在底图上的绘制并显示出来。
三、地图自定义绘制的实现
3.1 地图自定义绘制需要实现的功能:
- 空间数据的查询–用以得到要绘制的要素信息(主要是地理坐标信息)
- 空间数据坐标准换–将查询得到的要素地理坐标转换到GIS组件底图所对应的显示设备坐标
- 图像绘制–将要素按显示设备坐标绘制到显示的图像上
3.2 功能实现
这里以查询中国部分城市,并自定义绘制到屏幕上为例。
3.2.1 空间数据的查
- 获取要查询的数据集
- 构建查询信息
- 查询并保存数据
实现示例代码
void JustDraw::getFieldPoints()
{
UGVariant v;
UGPoint2D point;
//m_pWorkspace为之前加载地图时打开的Workspace,其中已经有名为"China400"的中国地理数据源,
//数据源中有名为"China_CitDistrict_P"的点数据集
UGDataSource* pDataSource = m_pWorkspace->GetDataSource(_U("China400"));
//GetDataset()方法返回的是一个UGDataset类型,该类型为素有数据集类型的基类,
//由于我已知我要得到的数据集是矢量数据,所以直接强转为UGDatasetVector
UGDatasetVector* pDvNode = (UGDatasetVector*)(pDataSource->GetDataset(_U("China_CitDistrict_P")));
//新建查询信息
UGQueryDef qdf;
qdf.m_nType = UGQueryDef::General;//查询类型:一般的属性条件查询
qdf.m_nOptions = UGQueryDef::Both;//查询选项:几何对象(Geometry)和属性(Attribute)都查询
qdf.m_nMode = UGQueryDef::GeneralQuery;//查询模式:一般查询(非模糊查询)
qdf.m_nCursorType = UGQueryDef::OpenStatic;//游标类型:静态游标
//查询
//查询后,满足条件的要素会存到一个UGRecordset链表中,查询的返回值为该链表头的指针
UGRecordset* pRsNewObjs = pDvNode->Query(qdf);
//为确保我们得到的指针是链表表头,我们需要MoveFirst()一下,当然,当前代码中这句可省略
pRsNewObjs->MoveFirst();
qDebug()<<pRsNewObjs->IsEOF()<<"xxx"<<pRsNewObjs->GetRecordCount();
//遍历,如果不为UGRecordset链表结尾
while(!pRsNewObjs->IsEOF())
{
//从当前指针指向的链表记录中获取要绘制点的地理坐标
if(pRsNewObjs->GetFieldValue(_U("smx"),v))
point.x = v.ToDouble();
if(pRsNewObjs->GetFieldValue(_U("smy"),v))
point.y = v.ToDouble();
//将地理坐标存入m_pGeoPoints的QList<QPointF>中并移动链表指针
m_pGeoPoints->append(point);
pRsNewObjs->MoveNext();
}
}
3.2.2 空间数据坐标准换
在SuperMap iObjects组件中,空间坐标转换到显示坐标需要经过:
- 地理坐标->>逻辑坐标->>显示坐标
- 地理坐标–>逻辑坐标
需要得到地图底图(UGMap)的DrawParamaters,其提供MPtoLP()方法可以将地理坐标转换为逻辑坐标 - 逻辑坐标–>显示坐标
需要得到地图绘制时使用的Graphics,该Graphics提供LPtoDP()方法,可将逻辑坐标转换为绘制图像时的显示坐标。注意:由于Graphics与图像是对应的,要得到正确的显示坐标,则必须使用底图绘制时使用的Graphics。
代码实现
//单个点地理坐标转屏幕坐标的方法
QPointF JustDraw::convertGPToDP(UGPoint2D geoPoint,UGGraphics* pGraphics,UGMap* pMap)
{
QPointF p;
UGPoint2D logicPoint;
//从map中获取到绘图时使用的DrawParamaters,将地理坐标转换为逻辑坐标
pMap->GetDrawing()->GetDrawParamaters()->MPtoLP(geoPoint,logicPoint);
//使用我们绘制底图时的所使用的Graphics将逻辑坐标转换为绘制时的显示坐标
pGraphics->LPtoDP(&logicPoint);
p.setX(logicPoint.x);
p.setY(logicPoint.y);
return p;
}
//将获取到的所有点转换为屏幕坐标
void JustDraw::getPicPoints(UGGraphics *pGraphics, UGMap* pMap)
{
//遍历我们保存下来的地理坐标List,并转换为屏幕坐标
if(!m_pGeoPoints->isEmpty())
{
foreach (UGPoint2D gp, *m_pGeoPoints)
{
m_pPicPoints->append(convertGPToDP(gp, pGraphics, pMap));
}
}
}
3.2.3 图像绘制
将获取到的点,按显示坐标绘制到GIS组件绘制好的底图上,由于本示例只是简单的绘制点,所以直接调用Qt的QPainter的drawPoint()方法即可
实现代码
void JustDraw::drawPointsOnImage(QImage *pImage)
{
//创建一个QPainter
QPainter painter;
//设定QPainter开始状态,并指定绘制设备(绘制设备即为我们传入的底图图像)
painter.begin(pImage);
//遍历需要绘制的点,并绘制到图像上
foreach (QPointF p, *m_pPicPoints)
{
painter.drawPoint(p);
}
//绘制工作完成
painter.end();
}
本文只写出了实现代码中的重点部分,若需要完整是可运行的代码,请至:http://download.csdn.net/detail/ufolr/8577297 下载。