GPS导航(6):GPS导航图形界面开发

项目开发 专栏收录该内容
11 篇文章 0 订阅

使用QT来绘制图形=========================

Graphics View的三元素:https://blog.csdn.net/qq_40732350/article/details/90116319

  • 场景类:QGraphicsScene类
  • 视图类:QGraphicsView类
  • 图元类:QGraphicsItem类

QGraphicsltem

QGraphicsitem 提供了半富的子类为程序的编写带来了很大的方便。有
QGraphicsEllipseitem , QGraphicsLineitem , QGraphicsPathitem ,
QGraphicsPixmapItem , QGraphicsPolygonitem , QGraphicsRectitem ,
QGraphicsSimpleTextltem, QGraphicsTextltem 一共 8 种。
在地图的绘制中 QGraphicsLineitem 可绘制道路,河流。 QGraphicsPathitem可以绘制一些曲线。

QGraphicsPixmapItem可以把一些通用的小图标添加到地图中,例如酒店,车站,书店,政府部门等.

QGraphicsPolygonitem 可以灵活的绘制出一些多边形建筑物,或者行政区域,湖泊等.

QGraphicsSimpleTextltem、 QGraphicsTextltem 则可以在地图上添加文字说明或者地标.

 

mapinfo 绘制电子地图===================

Maplnfo 地图图表
使用mapinfo 软件可对地图进行处理、查询、编辑和分析等操作,前提条件是:首先应该对地图信息化,而执行该操作的前提就是建立图表.
图表由行和列组成,行含了特定地理特性或事件的等信息,而列包含有关表中数据项的特定类型信息。
图表的组成类型:一个典型的 mapinfo 表将主要由TAB、MAP、ID 和DAT 文件格式组成。

  1. TAB 属性数据的表结构文件
  2. MAP 空间数据文件
  3. DAT 属性数据文件
  4. ID 交叉索引文件

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 两种格式文件。

  • Mif 文件包含了该图层所有图元的类型、位置、颜色等相关信息,
  • Mid 文件包含了 mif 每个图元对应的标注。

例如:
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<QPointF> 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

 

 

 

 

 

  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值