Graphics View的三元素:https://blog.csdn.net/qq_40732350/article/details/90116319
QGraphicsltem
QGraphicsitem 提供了半富的子类为程序的编写带来了很大的方便。有
QGraphicsEllipseitem , QGraphicsLineitem , QGraphicsPathitem ,
QGraphicsPixmapItem , QGraphicsPolygonitem , QGraphicsRectitem ,
QGraphicsSimpleTextltem, QGraphicsTextltem 一共 8 种。
在地图的绘制中 QGraphicsLineitem 可绘制道路,河流。 QGraphicsPathitem可以绘制一些曲线。
QGraphicsPixmapItem可以把一些通用的小图标添加到地图中,例如酒店,车站,书店,政府部门等.
QGraphicsPolygonitem 可以灵活的绘制出一些多边形建筑物,或者行政区域,湖泊等.
QGraphicsSimpleTextltem、 QGraphicsTextltem 则可以在地图上添加文字说明或者地标.
Maplnfo 地图图表
使用mapinfo 软件可对地图进行处理、查询、编辑和分析等操作,前提条件是:首先应该对地图信息化,而执行该操作的前提就是建立图表.
图表由行和列组成,行含了特定地理特性或事件的等信息,而列包含有关表中数据项的特定类型信息。
图表的组成类型:一个典型的 mapinfo 表将主要由TAB、MAP、ID 和DAT 文件格式组成。
Maplnfo 地图图层与图元
1 地图图层
每 一 个可用地图表示的 mapinfo 表都能在地图上作为图层显示。 一 个mapinfo 电子地图可能包含很多不同图层,而每一层都包含了地图的不同部分。通过将这些图层一层层叠加,就可以看到整个地图信息。
地图图元
图元是图层中的一个元素。在 mapinfo 中共育 4 种基本图元样式。
1. 区域对象
区域对象特指覆盖给定区域的闭合对象。其中包括多边形、椭圆和矩形,例如国家边界、邮政边界等。
2. 点对象
点对象表示数据的单一位置。其示例有饭店。
3. 线对象
线对象是指覆盖指定给定距离的开发对象,包括线、折线、弧线等,其示例有街道、河流和电力线路等。
4. 文本对象
文本对象是特指用千说明地图或其他对象文本,其示例有标注或标题等。
地图的来源
google下载
电子地图绘制
Maplnfo Professional 地图绘制工具
Maplnfo Professional 提供了一组专门的地图绘制工具,其中图形矢量的工具集全集中在该工具栏中,利用这些工具基本上可灵活完成各种绘图任务任务。这些绘图工具不仅能够在当前操作环境绘制图形对象,而且可根据需要进行必要的编辑和调整,使其符合绘图的需要。
工具条部分工具的含义
Maplnfo Professional 绘制地图
在一幅传统的地图中包含多种类型的图纸信息(如省市边界、城市、山川、河流、道路和水库),而对千使用地图的个人或某行业来讲,往往仅对注重对每个或部分类型进行显示和分析。所以根据不同需求设计地图的层次机构,有意识的把同一类对象归类,并放置在同一个图层中。清楚了mapinfo 中图层和图元的概念以及了解绘图工具的使用后,就可以开始绘制地图了。
在本项目中设计了 Key、 Build 、 routel 、 route2 、 waterArea 五层。
Key: 标注层。标注了地名称,道路名称,建筑名称等。
Build: 建筑层。包括了该区域所有建筑物,例如教学楼,宿舍,饭堂等。
route1: 一级道路。适合驾车和步行。
route2: 二级道路。适合步行。
waterArea: 水域边界。包括了湖泊、河流等 D
将道路层分为一级道路和二级道路主要是考虑到导航算法的设计,如果选择驾车,则在导航算法中只考虑一级道路,而不用考虑二级路。 5 个图层绘制完毕后最终显示结果如图:
*.MIF 和*.MID 文件
绘制完成后 mapinfo 把每一个图层保存为相应的图表文件。而每一个表又可以同时转出为*.MIF 和*. MID 两种格式文件。
例如:
eg. TAB 保存了这个信息
文件描述了该对象是折线对象,该线有 4 个点,每一个点的具体坐标,画笔是 7 号像素.,2 号样式,画笔颜色为 16776960 (黄色)。同样的方法就可用理解其他图元相关的信息.
主窗体设计
主窗体 class MapWidget 继承 QGraphicsView 基类,使得地图可以充满整个窗体显示,在主窗体中有滑块 QSlider, 标示 Qlabel, 按钮 QPushButton 三种控件。
滑块 QSlider 通过不同刻度的选择,发送信号给主窗体的槽函数slotZoom (int) 实现对当前显示地图进行放大缩小。实现语旬如下:
connect (slider,SIGNAL(valueChanged(int)),this,SLOT(slotZoom(int)));
按钮 QpushButton, 提示用户选择功能。有“导航”和“退出“两个功能。导航按钮点击后将显示导航子窗体。退出按钮直接发送调 clicked() 信号给主窗体的 close() 信号实现关闭主窗体,退出程序。实现语句如下:
connect (qui tButton, SIGNAL(clicked ()), this, SLOT (close()));
设置经度纬度的Lable的显示的语句
char Longitude[16] = {0};
char Latitude[16] = {0};
sprintf(Longitude,"经度: %f", longitude);
sprintf(Latitude,"纬度: %f", latitude);
ui->label->setText(QString(Longitude));
ui->label->setText(QString(Latitude));
最后使用布局管理器对以上控件进行布局。利用 addWidget ()函数将控件添加进布局管理器,实现布局。
QVBoxLayout *zoomLayout = new QVBoxLayout;//垂直布局
zoomLayout->addWidget(slider);
QSpaceritem *verticalSpacerl;//使用空白空间,目的是为了把滑块放置在布局的上半部分,使得地图有更多的显示空间。
verticalSpacerl = new QSpaceritem(20, 30, QSizePolicy: :Minimum, QSizePolicy:: Expanding) ;
zoomLayout—>additem(verticalSpacerl);
QVBoxLayout *huttonLayout = new QVBoxLayout;//第二个垂直布局
buttonLayout—>additem(verticalSpacer2); //空白
buttonLayout-)addWidget (okButton) ;// "导航”按钮
buttonLayout->addWidget(quitButton) ;//"退出”按钮利用水平布局进行总体布局,通过 setLayout ()把布局完的结果显示出来.
QHBoxLayout *layout= new QHBoxLayout;
layout—>addLayout(zoomLayout);
layout->addltem(horizontalSpacer) ;//空白
layout->addLayout(buttonLayout);
setLayout(layout) ;
运行结果:
子窗体设计
子窗体 class PathLayout 继承千 Qwidget 类。主要有下拉框 QgroupBox、标示 Qlabel 和按钮 QpushButton 这些控件。
下拉框有两个,用千路径规划功能,一个选择起点,另外一个选择终点。选择完后,通过发送 currentlndexChanged(QString) 信号把当前下拉框显示的QString 发送到主窗体的槽函数 setStartPoint(QString) 和set应Poi吐位沁伍0 中完成起点与终点的设置.是过凶It哑0 实裂对两个下拉框内容的初始化.
按钮如shllutton有取消和确定两个,
取消按钮发送 clicked()信号到主窗体的 clearThePath()槽函敷中,如果地图上有规划路径,则把路径清除。
确定按钮发送 click()信号到主窗体的 setThePath()糟函数中,实现归划路径,并退出子窗口。
运行结果:
地图坐标转换
地图文件中的坐标系统采取的是经纬度坐标,显示时采取的是 Sense 坐标(各个 Item 的坐标统一变换到Sense坐标),因此加载地图文件,从地图文作解析出图元坐标系统时。
需要将图元的经纬度坐标转化为 Sence 坐标后才能知道在画布的什么位置显示图元。画布的大小是始终固定不交的.但是它表示的经纬范围可变,而它表示的经纬度范围就是进行坐标转换,地图缩放.地图平移的基准。
地图表示的经纬度范围用下面定义的结构体表示:
typedef struct
{
double xl; //画布左上角代表的经度
double yl; //画布左上角代表的纬度
double x2; //画布右下角代表的经度
double y2; //画布右下角代表的纬度
}
确定好画布表示的经纬度范围后,就可用很方便的将地图数据中的经纬度坐标转换成画布坐标了,而具体的数据可以从MapInfo中得到,具体实现的转换函数:
double x; //当前点的 x
double y; //当前点的 y
double wx ;//当前显示区域经度的范围
double w; //当前显示区域的宽度
double hy; //当前显示区域纬度范围
double h; //当前显示区域的高度
point = new QPointF(((x — x1) / wx) * w — w / 2, ((y1 — y) / hy) * h — h / 2);
利用这个函数,所有图元都可以在画布上找到准确的位置显示出来。最终形成了整幅地图。
地图图元的显示
地图显示功能,实现原理就是把数据绘制还原称地图。地理数据的来源就是之前绘制好的 mapinfo 地图,把地图所有图表全部用 mif 和 mid 格式文件转出。mif 和 mid 格式文件就是 mapinfo 和 QT 的接口。利用 QT 的 QGraphicsitem 将mapinfo 地图中的图元在QGraphicsScene 显示出来。
QPointF *point;//存放图元节点
QVector pointfRegion; //存放一个多边形所有节点容器
QPolygonF *pPolygonF; //多边形指针
QGraphicsPolygonItem *pPolygonItem;// QGraphicsView 框架下的多边形图元,要把图元封装成 QGraphicsItem 才能够在该框架里面显示.
QByteArray brushColor; //存放当前填充颜色
QByteArray penColor; //存放当前画笔颜色
QString sColor; //存放颜色
if (graphicsName = "Region")
{
latlon>>count>>count;//在mif文件结构中该行第二个数字记录了多边形的节点数.
//如下图,这个多边形记录了 5 个点(用红色圈的点).
如图:
for(int i = 0; i < count; i++)
{
latlon>> x >> y; //x 存放经度, y 存放纬度
point = new QPointF(((x — x1) / wx) * w — w / 2, ((y1 — y) / hy) * h — h / 2);
//把经纬度转为 QGraphicsSense 坐标然后存到 point 中
pointfRegion << *point;//把转换后的点存进 pointfRegion 容器中
}
pPolygonF = new QPolygonF(pointfRegion); //创建多边形
pPolygonItem = new QGaphicsPolygonitem(*pPolygonF);//创建多边形图元
if(graphicsAttribute == "Pen") //一个图元的点描述后就是该图元的边界画笔,用 Pen 表示
如图:
{
latlon >> penColor;
sColor.append(penColor);
pen.setColor(QColor(sColor.section(",", 2, 2).remove(QChar(')')),
Qt::CaseInsensitive).toDouble())) ;//获取面笔颜色
pPolygonItem->setPen(pen);//设置图元边界颜色
}
如图:
if (graphicsAttribute == "Brush")//该图元的边界面笔描述完,继续用Brush 表示填充颜色
{
latlon >> brushColor;
sColor.append(brushColor);
brush.setColor(sColor.section(",", 1, 1). toDouble()) ;
}
pPolygonitem->setBrush(brush); //对元进行颜色填充
scene->addItem(pPolygonItem); //最后把图元添加到面布上
}
这样就完成了一个图元的显示.只要将所有图元显示出来就形成了地图.
显示效果设计
某部分图元需要进行特殊处理的,就应该使用数据结构对这部份图元进行存储,以便对这些图元进行控制。例如地图中显示的文字,在地图被放大时,文字不放大.
图元属于同类数据类型,而且又有相当数量,很容易就想到数组。但是数组存放在连续空间,需耍占据一块连续的空间且不可动态添加,而每一幅地图的图元数量是不确定数,所以排除了数组的选择,而选择了链表。
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。
每个结点包括两个部分:
下面是地图文本类.
数据类:
class MapTextltem
{
public:
MapTextitem(QString str);
MapTextItem() ;
virtual ~MapTextltem(void);
QGraphicsTextItem *mapTextItem; //存放当前 QGraphicsTextItem 的指针
MapTextItem *nextTextItem; //存放下一个 QGraphicsTextItem 的地址
};
通过对数据的处理而获取所有地图中文本图元。当检测到一个图元是就添加到链表的末端,直到没有为止。
if (graphicsName == "Text")
{
latlon >> graphicsName; //获取文本信息,如“食堂”
latlon >> x >> y; //获取文本在地图的相应位置
mapText = new MapTextltem(graphicsName);
mapText->mapTextItem->setPos(((x — x1) / wx) * w — w / 2, ((y1 — y) / hy) * h — h / 2);
scene->addItem(mapText->mapTextItem); //在地图上面显示
mapText->nextTextItem = mapTextitem->nextTextItem;
mapTextltem->nextTextItem = mapText; //连接到链表中
}
Dijkstra(迪杰斯特拉)算法
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。 Dijkstra 算法能保证找到从起点到终点的一条最优路径,只要路径权值不会为负。
Dijkstra 一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用 OPEN, CLOSE 表的方式,这里均采用永久和临时标号的方式。注意该算法要求图中不存在负权回路。
可以先参考:
图的最短路径之迪杰斯特拉算法和弗洛伊德算法:https://blog.csdn.net/daaikuaichuan/article/details/80586408
我们这里用邻接表来存储每个节点:
如:
存储:
数据结构:
1. 表头结构体:
typedef struct Head
{
int id; //节点 ID
double lat; //纬度
double lon; //经度
abutltem *abutltem; //指向该行第一个节点
struct Head *nextAbutHead;//指向下一个头结点
}abutHead;
2. 表项结构体:
typedef struct Item{
int id;
double lat; //纬度
double lon; //经度
double distance; //与该行头节点的距离(权值)
struct Item *nextAbutitem;//指向下一节点
}abutltem;
有了以上两个结构体就可以把所有节点数据都存进邻接表中,再加上Dijkstra 算法,就可用自主选择起点和终点,把路径途径节点存到链表里面,利用地图上显示路径是算法把最短路径显示出来,实现了路径规划功能。
参考:
GPS导航(5):GPRS通信
https://blog.csdn.net/supermapsupport/article/details/61204034
https://blog.csdn.net/supermapsupport/article/details/71147160
https://www.supermap.com/html/sofewaresmall_28.html
http://support.supermap.com.cn/product/VedioPlay.aspx?id=100
SuperMap IObjects C++组件学习笔记(一) - Hello iObjects C++:https://blog.csdn.net/ufolr/article/details/44943743
SuperMap iObject常见问题解答集锦(十二):https://blog.csdn.net/supermapsupport/article/details/78733457
SuperMap iObjects C++结合Qt在vs2012上的开发环境搭建:https://blog.csdn.net/ufolr/article/details/44943143