47
7 公交信息查询系统 本章将向读者介绍公交信息查询系统的完整开发过程,除了进一步加强 SQL 语言的运用讲解,巩固基本控件的使用外,还将介绍基于 Visual C++ 6.0 的 MapObjects 控件的使用方法。本章会详细说明对地图的放大、缩小、测距、 漫游等操作的实现过程,实现公交信息查询、公交站点维护,高效率获得站点 间最佳路径,并将公交站点和电子地图结合起来,给用户提供一个非常直观、 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于 Visual C++ 6.0 的 GIS 系统的一般步骤有一定的了解。 通过阅读本章,读者可以学习到: MapObjects 控件的使用。 状态栏中信息的显示。 动态的应用程序图标。 公交换乘算法的设计与实现 地图的放大、缩小、测距、漫游等操作的实现。

公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

  • Upload
    others

  • View
    13

  • Download
    0

Embed Size (px)

Citation preview

Page 1: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章

公交信息查询系统

本章将向读者介绍公交信息查询系统的完整开发过程,除了进一步加强

SQL 语言的运用讲解,巩固基本控件的使用外,还将介绍基于 Visual C++ 6.0

的 MapObjects 控件的使用方法。本章会详细说明对地图的放大、缩小、测距、

漫游等操作的实现过程,实现公交信息查询、公交站点维护,高效率获得站点

间最佳路径,并将公交站点和电子地图结合起来,给用户提供一个非常直观、

完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于 Visual

C++ 6.0 的 GIS 系统的一般步骤有一定的了解。

通过阅读本章,读者可以学习到:

MapObjects 控件的使用。

状态栏中信息的显示。

动态的应用程序图标。

公交换乘算法的设计与实现

地图的放大、缩小、测距、漫游等操作的实现。

Page 2: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

265

7.1 开发背景

人们的日常生活与地理信息密切相关,包括衣、食、住、行、学习、工作、娱乐、消费等

各个方面。随着城市建设的迅猛发展,各个大小城市的公交车数量不断增加,为人们的出行提

供了很大的便利。但随着城市道路的不断改建,城市建设的不断发展,公交车的行驶路线也在

不停地改变。基于城市公交现状,根据人们的出行规律,设计一个完整的智能公交信息查询系

统,可以根据公交线路、公共场所和普通公交车站分别进行查询,减少市民乘坐公交车所用时

间,降低了费用。

7.2 系统分析

通过调查分析,得出广大市民对城市信息查询的一些要求:能进行基本的公交信息查询,

它可以解决市民出行乘公交车时所遇到的问题;能进行公交站点的维护,以适应不断规划的城

市交通路线;最好配以电子地图,能够给用户一个比较直观、具体的感觉。本系统仅是为了实

现这些功能而设计的。

7.2.1 需求分析

当今社会,经济正在飞速发展,随着人们物质生活水平的不断提高,购车的人越来越多,

交通问题非常严峻,公交车的好处随之明显地体现出来。为了给人们的出行带来便利,迫切需

要一套完善的公交系统帮助人们以最快的速度,最短的时间到达目的地,也可以帮助从异地来

的人们快速地到达指定的地点。对于从同一个起点,到达同一个终点,肯定有许多不同的公交

路线,为了最快到达或最少换乘次数到达,应该乘哪一辆公交车呢?在这种环境下,提出了对

公交信息查询系统的需求。

7.2.2 项目人员分配

在软件开发过程中,为了很好地完成任务,需要多个不同角色共同完成项目的多个模块。

不同角色会发生人员交叉现象,如项目经理也会转为系统分析员的角色,完成需求调研相关技

术文档的写作;系统分析员也可能转为模块负责人角色,完成某模块具体设计工作等。本项目

团队的成员组成和职责分配如表 7-1 所示。

表 7-1 项目成员组成和职责分配

角 色 职 责 人 数

项目经理 项目管理、项目计划的制订、项目组任务分配、进度确认 1 人

系统架构师 负责整个系统的系统架构 1 人

系统分析员 需求调研、整理、产品规划及相关技术文档的写作 1 人

模块负责人 分别负责各个模块 1 人

数据库管理员(DBA) 负责系统数据库 1 人

测试工程师 负责整个软件的测试和质量保证 1 人

7.3 系统设计

根据需求分析,结合系统的功能特点,其开发主要包括前台应用程序的开发、后台数据库

的设计和维护两个方面。对于前者要求应用程序功能完备,操作方便,而对于后者则要求建立

Page 3: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

266

的数据库具有数据一致性、完整性强和数据安全性较好等特点。下面将从系统目标、系统模块

结构、开发环境与运行环境、系统功能预览 4 个方面进行介绍。

7.3.1 系统目标

结合需求分析,系统前端开发工具采用 Visual C++ 6.0,后台数据库系统可采用大型数据

库系统 SQL Server 2005,系统的运行平台为 Windows。在 Visual C++ 6.0 的开发平台上,嵌入

GIS 功能组件(ESRI 的 MapObjects),充分发挥该开发工具的灵活性与 MapObjects 在空间数据

管理方面的优势。开发的系统实现公交信息查询、公交信息维护、地图信息查询、操作等功能。

7.3.2 系统模块结构

根据前面分析可知,本系统总体分为公交信息查询、地图信息查询和操作、公交信息管理、

界面设置 4 个部分,系统模块结构如图 7-1 所示。

城市公交查询系统

公交信息查询 地图信息查询、操作 公交信息管理 界面设置

车次查询

车站查询

公交换乘

放大

缩小漫游

专题查询帮助

点查询

测距

用户登录

单个站点维护

整条路线维护

主界面设计

各子界面设计

图 7-1 公交信息查询系统结构图

各模块主要功能说明如下。

1.公交信息查询

车次查询:用户输入公交车次号码,启动查询可获得该路车所有站点。

车站查询:用户输入一个站点,启动查询可获得所有经过该站点的公交车车次及其属

性。

公交换乘:用户输入起始点和目的点,启动查询可获得乘车的最优方案。此方案优先

考虑最少的换乘次数,然后考虑最少的站点数,从而可以给出最优方案。

2.地图信息查询和操作

放大、缩小、漫游:这是 GIS 系统、电子地图、图形处理等软件必备的基本功能,是

用户通过有限的窗口去认识无限的地理空间的必备工具。用户可通过缩放按钮更详细

地了解地图信息。

点查询:包括查询点对象和线对象,用户在地图上要查询的目标附近或目标上按下鼠

标左键,属性框中显示该对象的属性,并将该对象高亮显示。

测距:用户在地图上按下两点后,就可以在属性对话框中显示两点间的直线距离。

对象查询:当用户要查询某一对象时,可以在编辑框中输入该对象。启动查询后,地

图就将该对象高亮显示。

Page 4: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

267

帮助:用户可以通过帮助来获得该系统的使用方法,以及可能出现的错误和处理方法。

主题查询:用户可以启动该查询来使地图上只显示某一主题的对象。主题为村庄小区、

商场大厦、旅游景点、教育文化、医疗机构、宾馆酒店、政府部门、交通运输等。

3.公交信息管理

用户登录:系统管理员通过用户名和密码登录系统,只有正确登录后才能进行公交站

点的维护操作。

删除单个站点:管理员先选择车次,然后选择站点进行删除。

修改单个站点:管理员先选择车次,系统将该车次的站点显示在列表框中,管理员可

以直接在列表框中修改。

添加一条路线:管理员添加路线属性及所有站点。

删除一条路线:将所有车次用列表框显示,管理员可以选择要删除的车次来删除。

添加对象属性:管理员通过编辑框来输入对象的名称和属性,如果是景点还可以加入

图片。加入图片时,管理员应先将该图片加入到系统文件夹下,然后输入图片名称。

系统日志:系统可以自动记录管理员的操作时间及动作。

4.界面设置

对界面元素的设置,可以改变界面的风格,改变背景音乐等。结合系统模块结构图,绘出

系统流程图,如图 7-2 所示。

图 7-2 公交信息查询系统流程图

7.3.3 开发环境与运行环境

结合需求分析,开发此管理系统所需要用到的软件环境如下。

操作系统:Windows 2000/NT/XP。

Page 5: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

268

数据库:SQL Server 2005。SQL Server 2005 不仅具有较好系统的集成性及对日常任务

的自动化管理能力,还为 IT 专家和信息工作者提供了一个完整的数据解决方案。鉴于

以上优点,所以选择了 SQL Server 2005 作为数据库管理系统。

编译工具:Visual C++ 6.0。它编译速度快,支持面向对象编程技术,可以提高软件系

统开发的速度和质量,程序调试也比较方便,很适合公交信息查询系统的开发。

7.3.4 系统开发技术介绍

MapObjects(简称 MO)是 ESRI 开发的提供制图与空间分析功能的组件,它可以直接插

入到许多标准开发环境的工具中。MapObjects 的核心是一个称为 Map 控件的 ActiveX 控件,

MapObjects 中还包括 35 个可编程的 ActiveX 对象,这些对象为应用开发人员提供了有力的制

图与 GIS 功能支持。

通常使用MO开发具体应用时,必须同时拥有MapObjects开发环境和某种程序语言环境。

这样,在开发应用时,就可在这种程序语言中嵌入使用 Map 控件。在应用程序中添加 MO 控

件的方法和添加普通控件的方法类似,一旦嵌入 Map 控件后,就可加载地图并使用

MapObjects 的若干种可编程对象对地图实现各种查询、检索和空间分析功能。因此,通常将

MapObjects 称为控件只是狭义上的指代,它真正的含义包含了 Map 控件和多个可编程的

ActiveX 对象。因为 MO 是一个 ActiveX 控件,它可用于大量开发框架中。由于它用于开发

嵌入式 GIS 系统,所以支持 ARC/INFO 层,支持 ESRI 的 Shape 文件格式、SDE(空间数据

库引擎)图层及删格图像格式(如 BMP、TIFF)等。此外,它还支持通过微软 ODBC 规范

进行的外部数据库访问;提供强大的地理信息系统查询与统计功能和通过标准 SQL 表达式进

行特征选择和查询。

MapObjects 是按图层组织计算机地图的。使用 MO 时,一幅计算机地图加工成多个层层

叠加的透明层,这些透明层被称为图层。每个图层包含了地图的某个方面,如河流层、道路

层等。通常每个图层可以认为是一个数据库,但它不是普通的数据库,它包括空间地理信息

和属性信息。因此创建每个图层时,都要为其建立一张对应的表,表中存储此图层对象的空

间数据信息。通常 MO 中的图层都是矢量图层,是一种基于坐标的数据结构。每个图层中的对

象都表达为 X 坐标和 Y 坐标,因此图层可以进行无限放大或缩小而不会丢失图层带有的地理

信息。

MapObjects 默认的图层格式为 Shapefile 格式,它是一种简单的、只用非拓扑关系存储空

间信息和属性信息的格式。Shapefile 包括同一工作区的 3 个不同扩展名的文件:.shp(存储地

理信息)、.dbf(包含属性信息)、.shx(包含两文件的索引关系)。

7.3.5 系统预览

运行系统前需要进行数据库、数据表的创建,本系统提供了自动还原数据库的功能。其中

默认路径为本管理系统运行程序位置,下面介绍系统各主要功能模块。

系统运行后会进入主窗口,该窗口主要包括地图控制工具栏、操作工具栏、地图显示

窗口、缩略图窗口、状态栏共 5 部分,如图 7-3 所示。

Page 6: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

269

图 7-3 公交信息查询系统主窗口

用户登录后可以进行公交信息维护,选择【维护】|【删除一个站点】命令,显示删除

站点的对话框,用户启动该功能时,系统在【车次】下拉框初始化显示所有车次,List Ctrl 控

件自动将该车次的所有站点载入,用户可以选中要删除的项,单击【删除】按钮可将该记录删

除。本操作可以连续进行多次操作,界面如图 7-4 所示。

选择【维护】|【修改一个站点】命令,弹出【修改站点信息】对话框。为了方便用户

操作,使用户能直接在 List Ctrl 控件上进行修改。从 CListCtrl 派生出一个新类 CNewListCtrl。

当用户单击某一个列表项时,可以动态地创建一个编辑框附加到该列表项上,用户就可以对该

项进行修改,修改完后再单击其他项,就可以完成修改。本操作可以进行多次操作,如图 7-5

所示。

图 7-4 【删除单个站点】对话框 图 7-5 【修改站点信息】对话框

Page 7: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

270

选择【维护】|【添加一条路线】命令,弹出【添加一条路线】对话框。本功能的实现

也同样体现方便用户的原则,当用户输入完成后只需按下回车键就可以继续操作,如图 7-6 所示。

选择【维护】|【删除一条线路】命令,弹出【删除一条线路】对话框。用户单击要删

除的路线再单击【删除】按钮,就可以将该路线删除,如图 7-7 所示。

图 7-6 【添加一条路线】对话框 图 7-7 【删除一条路线】对话框

选择【维护】|【所选对象属性】命令,弹出【添加所选对象属性】对话框。该功能可

以添加景点属性及图片。添加图片时要注意,应先将要添加的图片放到 res 文件夹下,并且输

入的图片名称应和 res 文件夹下相同。当用户在地图上选择一个景点时,系统会自动寻找该景

点对应的图片,并把它显示出来,界面如图 7-8 所示。

选择【维护】|【用户日志】命令,弹出【用户日志查询】对话框,该日志文件记录了

管理员的每一步操作,界面如图 7-9 所示。

图 7-8 【添加所选对象属性】对话框 图 7-9 【用户日志查询】对话框

7.4 数据库设计

数据库设计是系统设计中非常重要的环节,作为整个系统的基础,数据表要尽量满足规范

化要求,保证其设计的合理性。根据用户的需求设计数据库,提高系统的运行效率,并且有利

Page 8: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

271

于系统的维护和扩展。下面将从数据库的分析、数据表的设计、E-R 图的绘制和关系图几个方

面来介绍数据库的设计。

7.4.1 数据库分析

目前的数据库系统有 Oracle、Sybase、Informix、DB2、SQL Server 等,根据“杭州市公交

信息查询系统”对数据量的要求,以及要求性能的稳定,本系统采用 SQL Server。由于 SQL Server

处理数据速度快、容量大、稳定性高,所以本系统使用 SQL Server 2005 作为系统开发的数据

库管理系统。它解决了公交查询系统相关数据的添加、修改、删除、查找等操作过程中的信息

数据的处理问题。

7.4.2 数据库表的设计及其 E-R 图

E-R 图是在需求分析的基础上设计的,主要是分析各种实体及它们之间的关系,为逻辑结

构设计打下基础。根据分析设计的结果,该系统包含的实体有:站点实体、车次实体、景点信

息实体、用户实体、日志实体、图层实体。下面将分别介绍各实体及实体间的 E-R 图,通过其

E-R 图读者可以更好地理解各实体的属性关系。

站点实体 E-R 图:记录城市公交系统中每一个站点的属性,如图 7-10 所示。

车次实体 E-R 图:记录每一个公交车车次的属性,如图 7-11 所示。

站点

车次 站点名称

车次

末班时间

发车时间

终点站名称

起始站名称

车次名称

图 7-10 站点实体 E-R 图 图 7-11 车次实体 E-R 图

景点实体 E-R 图:记录城市中景点的属性,如图 7-12 所示。

管理员实体 E-R 图:为维护公交信息而使用,如图 7-13 所示。

景点

图片名称

是否有图片 景点属性

景点名称

管理员

密码 用户名

图 7-12 景点实体 E-R 图 图 7-13 管理员实体 E-R 图

日志实体 E-R 图:记录管理员对数据库的操作,如图 7-14 所示。

图层实体 E-R 图:为了更好地管理图层,将所有的图层名称放到一个表中,这样可以

方便地对图层进行操作,如图 7-15 所示。

Page 9: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

272

日志

操作时间

用户操作

用户名

图层

图层名称 编号

图 7-14 日志实体 E-R 图 图 7-15 图层实体 E-R 图

注意:在系统的开发过程中,数据库的设计直接影响着系统的后期开发,故在数

据库的设计过程中,需要为后期的维护和系统性能的扩展打下良好的基础。

7.4.3 数据库关系图

本系统数据库名称为 HangZhouBus。数据库 HangZhouBus 中包含日志表(Action_Log)、

景点属性表(AttributeSet)、公交车线路表(BUS)、电子地图站点表(Map_Station)、图层表

(Species)、站点表(STATION)和用户表(USER)共 7 个表。下面提供系统运行时动态创建

数据库的方法,数据库的名称为 HangZhouBus,对应的 SQL 语句如下:

IF EXISTS (SELECT name FROM sysdatabases WHERE name = ‘HangZhouBus’)

/*存在数据库*/

DROP DATABASE [HangZhouBus] /*删除数据库*/

GO

CREATE DATABASE [HangZhouBus] ON /*创建数据库*/

(

NAME = ' HangZhouBus_dat', FILENAME = 'C:\ HangZhouBus_dat.mdf' , SIZE = 10,

MAXSIZE = 100,

FILEGROWTH = 10%

)

LOG ON /*设置日志文件属性*/

(

NAME = ' HangZhouBus_log', FILENAME = 'C:\ HangZhouBus.ldf' , SIZE = 5,

MAXSIZE = 25,

FILEGROWTH = 5

)

下面分别对数据库中的各个表进行介绍。

日志表(Action_Log):用来记录用户的操作信息,其主要字段为 USER_ID,其各字段

的详细信息如表 7-2 所示。

表 7-2 日志表(Action_Log)

字段名称 数据类型 字段大小 可否为空 说明

USER_ID varchar 20 Not Null 主键,用户 ID

ACTION varchar 20 Not Null 操作

COME_LEAVE_TIME varchar 20 Not Null 时间

建立该表的 SQL 语句如下:

Page 10: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

273

CREATE TABLE Action_Log ( /*创建数据表*/

USER_ID varchar (20) NOT NULL , /*用户 ID */

ACTION varchar (20) NOT NULL , /*操作*/

COME_LEAVE_TIME varchar (20) NOT NULL, /*时间*/

)

GO

景点属性表(AttributeSet):用来记录景点属性的信息,其主要字段为 NAME,其各字

段的详细信息如表 7-3 所示。

表 7-3 景点属性表(AttributeSet)

字段名称 数据类型 字段大小 可否为空 说明

NAME varchar 20 Not Null 主键,景点名

ATTRIBUTE varchar 200 Ok 属性

ISPICTURE Int 4 Not Null 是否有照片

PICTURE_NAME varchar 20 Ok 照片名称

建立该表的 SQL 语句如下:

CREATE TABLE AttributeSet ( /*创建数据表*/

NAME varchar (20) NOT NULL , /*景点名*/

ATTRIBUTE varchar (200) , /*属性*/

ISPICTURE int NOT NULL, /*是否有照片*/

PICTURE_NAME varchar (20) /*照片名称*/

)

GO

公交车线路表(BUS):用来记录公交线路的信息,其主要字段为 ID_BUS,其各字段

的详细信息如表 7-4 所示。

表 7-4 公交车线路表(BUS)

字段名称 数据类型 字段大小 可否为空 说明

ID_BUS varchar 20 Not Null 主键,编号

BEGIN varchar 20 Not Null 起始站

END varchar 20 Not Null 终点站

AMTIME varchar 20 Not Null 发车时间

PMTIME varchar 20 Not Null 末班时间

建立该表的 SQL 语句如下:

CREATE TABLE BUS ( /*创建数据表*/

ID_BUS varchar (20) NOT NULL , /*编号*/

BEGIN varchar (20) NOT NULL , /*起始站*/

END varchar (20) NOT NULL, /*终点站*/

AMTIME varchar (20) NOT NULL, /*发车时间*/

PMTIME varchar (20) NOT NULL /*末班时间*/

)

GO

电子地图站点表(Map_Station):用来记录地图站点的信息,其主要字段为 ID,其各

字段的详细信息如表 7-5 所示。

表 7-5 电子地图站点表 Map_Station

字段名称 数据类型 字段大小 可否为空 说明

ID varchar 20 Not Null 主键,站点编号

NAME varchar 20 Not Null 名称

Page 11: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

274

建立该表的 SQL 语句如下:

CREATE TABLE Map_Station ( /*创建数据表*/

ID varchar (20) NOT NULL , /*站点编号*/

NAME varchar (20) NOT NULL /*名称*/

)

GO

图层表(Species):用来记录地图图层的信息,其主要字段为 INDEX,其各字段的详

细信息如表 7-6 所示。

表 7-6 图层表(Species)

字段名称 数据类型 字段大小 可否为空 说明

INDEX int 4 Not Null 主键,索引号

SHPNAME varchar 20 Not Null 地图名称

建立该表的 SQL 语句如下:

CREATE TABLE Species ( /*创建数据表*/

INDEX int NOT NULL , /*索引号*/

SHPNAME varchar (20) NOT NULL /*地图名称*/

)

GO

站点表(STATION):用来记录站点的信息,其主要字段为 ID,其各字段的详细信息

如表 7-7 所示。

表 7-7 站点表(STATION)

字段名称 数据类型 字段大小 可否为空 说明

ID varchar 20 Not Null 主键,站点编号

STATION varchar 20 Not Null 站点名称

建立该表的 SQL 语句如下:

CREATE TABLE STATION ( /*创建数据表*/

ID varchar (20)NOT NULL , /*站点编号*/

STATION varchar (20) NOT NULL /*站点名称*/

)

GO

用户表(USER):用来记录用户的信息,其主要字段为 USER_ID,其各字段的详细信

息如表 7-8 所示。

表 7-8 用户表(USER)

字段名称 数据类型 字段大小 可否为空 说明

USER_ID varchar 20 Not Null 主键,用户 ID

USER_PASSWORD varchar 20 Not Null 密码

建立该表的 SQL 语句如下:

CREATE TABLE USER ( /*创建数据表*/

USER_ID varchar (20) NOT NULL, /*用户 ID */

USER_PASSWORD varchar (20) NOT NULL /*密码*/

)

GO

Page 12: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

275

7.5 公交信息查询模块

公交信息查询模块是系统的基础模块,本系统的主要功能都是在这个部分实现的,这个部

分设计的好坏直接影响了系统的性能和运行效果。如果基础信息设置得合理,会使项目的实施

事半功倍。同时,基础信息管理也是各类管理软件最基础、最重要的数据处理功能之一。下面

将从功能分析、技术分析、界面设计及各子模块的实现几个方面进行介绍。

7.5.1 公交信息查询模块功能分析

该模块的功能主要是对车次进行查询,可以查询到车次的始发站、终点站、发车时间、末

班时间等。对车站的查询及公交换乘的实现,系统给出所有可能的乘车方案,并推荐出一条相

对最优方案。所谓最优方案是指最少的换乘次数和最少的公交站点,同时优先考虑最少的换乘

次数。

7.5.2 公交信息查询模块技术分析

本模块通过一个 Tab 控件将车次查询、车站查询、公交换乘集成在一起,查询功能的实现

是通过构造查询语句对数据库的操作来完成的。本系统设计公交换乘的算法,以转车时经过的

站点数最少来考虑的。另外,还有其他情况,例如需要花费的时间、金额等,读者可以考虑自

己来完善。

7.5.3 车次查询的实现

车次的查询是在主界面的右上角 Tab 控件中显示的,关于 Tab 控件的使用,在前面的章节

中已经有了很详细的介绍,在此不做过多的说明。在 Tab 控件上有 3 个标签,分别为车次查询、

车站查询和公交换乘。下面介绍车次的查询。

添加一个对话框资源,将 ID 改为 IDD_ROADWAY,在对话框上添加如图 7-16 所示的控

件。通过类向导更改控件的 ID 和为控件添加相应的变量,读者可以参考光盘中的源代码。修

改对话框的属性,如图 7-17 所示。

图 7-16 车次查询界面设计 图 7-17 对话框属性界面

Page 13: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

276

注意:如果想要把对话框作为 Tab 控件的一个属性页,则需把对话框的 Style 改

为 Child。

在对话框界面上的空白区域双击,进入添加类界面。为对话框添加一个新类 CRoadView。

给【查询】按钮添加消息响应函数,相关代码如下:

代码位置:见光盘中本章源代码的 CRoadView 类。

1 void CRoadView::OnButton()

2 {

3 UpdateData(true); //更新数据

4 bool flag=false; //定义布尔型的变量

5 m_show.Empty(); //将字符串清空

6 CStationSet m_pStationSet; //定义一个记录集对象

7 m_pStationSet.Open(); //打开记录集

8 m_pStationSet.MoveFirst(); //移动到首条记录

9 while(!m_pStationSet.IsEOF()) //如果记录没有到末尾

10 {

11 if(m_pStationSet.m_ID==m_id) //查找匹配的数据

12 {

13 m_show+=m_pStationSet.m_STATION; //将字符串累加

14 m_show+=" -> ";

15 flag=true; //改变标记的值

16 }

17 m_pStationSet.MoveNext(); //移动到下一条记录

18 }

19 if(!flag) //如果没有找到数据

20 {

21 MessageBox("你输入的车次不正确!"); //弹出提示信息

22 m_id.Empty(); //清空编辑框

23 CWnd *pwnd; //定义窗口类的指针

24 pwnd=GetDlgItem(IDC_STATION_ID); //获取控件的指针

25 pwnd->SetFocus(); //设置控件的焦点

26 }

27 UpdateData(false); //更新数据

28 }

第 6~18 行代码为遍历数据表从数据表中查询需要的数据。

第 19~26 行代码为当所要查找的数据不存在时所做的一些工作。

上述代码首先获取输入的车次名称,然后遍历记录集查询所有的车次,选择出与输入车次

相同的车次,获取该车次的详细站点信息,车站间以“->”为间隔,将所有的车站信息显示在

编辑框中。

7.5.4 车站查询的实现

车站的查询同车次的查询是类似的,它也是作为 Tab 的一个属性页。添加一个对话框

资源,将 ID 改为 IDD_ROADWAY,修改对话框的属性。在对话框上添加如图 7-18 所示的

控件。

Page 14: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

277

图 7-18 车站查询界面

在对话框界面上空白区域双击,进入添加类界面。为对话框创建一个新类

CStaionWayView。通过类向导更改控件的 ID和为控件添加相应的变量,添加WM_INITDIALOG

消息响应函数 OnInitDialog(),该函数中代码如下:

代码位置:见光盘中本章源代码的 CStaionWayView 类。

1 BOOL CStaionWayView::OnInitDialog()

2 {

3 CPropertyPage::OnInitDialog();

4 DWORD style=m_list.GetExtendedStyle(); //获取列表控件的风格

5 m_list.SetExtendedStyle(style|LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);

6 m_list.InsertColumn(0,"车次",LVCFMT_LEFT,50); //向列表控件中插入列

7 m_list.InsertColumn(1,"起始站",LVCFMT_LEFT,70);

8 m_list.InsertColumn(2,"终点站",LVCFMT_LEFT,70);

9 m_list.InsertColumn(3,"发车时间",LVCFMT_LEFT,70);

10 m_list.InsertColumn(4,"停止时间",LVCFMT_LEFT,70);

11 return TRUE;

12 }

第 3~10 行代码为列表控件初始化,设置列表控件的风格、列的宽度及标题。

上述代码用列表控件的初始化函数 SetExtendedStyle()设置列表控件的风格,而函数

InsertColumn()实现设置列表控件的列标题。给【查询】按钮添加消息响应函数,该消息函数中

具体代码如下:

代码位置:见光盘中本章源代码的 CStaionWayView 类。

1 void CStaionWayView::OnButton1()

2 {

3 UpdateData(true); //保存数据

4 int i=0; //定义整型变量

5 bool flag=false; //定义布尔型变量

6 CFont m_font; //定义字体

7 m_list.SetBkColor(RGB(247,247,255)); //设置列表控件背景色

8 m_list.SetTextColor(RGB(0,0,255)); //设置列表控件文字色

9 CString str,str1; //定义字符串变量

10 m_list.DeleteAllItems(); //删除列表控件所有项

11 CBusSet m_pBusSet; //定义记录集变量

12 CStationSet m_pStationSet; //定义记录集变量

Page 15: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

278

13 m_pBusSet.Open(); //打开记录集

14 m_pStationSet.Open(); //打开记录集

15 m_pStationSet.MoveFirst(); //移动到首条记录

16 while(!m_pStationSet.IsEOF()) //遍历记录集

17 {

18 if(m_station==m_pStationSet.m_STATION) //查找匹配数据

19 {

20 m_pBusSet.MoveFirst(); //移动到首条记录

21 while(!m_pBusSet.IsEOF()) //遍历记录集

22 {

23 if(m_pStationSet.m_ID==m_pBusSet.m_ID_BUS) //查找匹配数据

24 {

25 m_list.InsertItem(i,m_pBusSet.m_ID_BUS); //插入数据到列表中

26 m_list.SetItemText(i,1,m_pBusSet.m_BEGIN);

27 m_list.SetItemText(i,2,m_pBusSet.m_END);

28 m_list.SetItemText(i,3,m_pBusSet.m_AMTIME);

29 m_list.SetItemText(i,4,m_pBusSet.m_PMTIME);

30 flag=true; //改变标记

31 i++;

32 break;

33 }

34 else

35 m_pBusSet.MoveNext(); //移动到下一条记录

36 }

37 }

38 m_pStationSet.MoveNext(); //移动到下一条记录

39 }

40 m_pBusSet.Close();

41 m_pStationSet.Close(); //关闭记录集

42 CMainFrame *pFrame=(CMainFrame *)AfxGetMainWnd(); //获取主框架指针

43 CMapPublicView *pView=(CMapPublicView *)(pFrame->m_wndSplitter.GetPane

(0,0));

44 pView->FindArea(m_station); //显示查询站点

45 if(!flag) //如果没有找到数据

46 MessageBox("输入站点不存在!");

47 }

第 4~15 行代码为定义辅助的变量和记录集的初始化工作。

第 16~41 行代码实现了从数据表中查找站点的相关信息,插入到列表控件中。

第 42~44 行代码实现了查询后的控件更新显示。

上述代码中,函数 SetBkColor()设置列表控件的背景颜色,函数 SetTextColor ()设置列表控

件中文字的颜色,然后根据用户输入的车站的名称遍历记录集,查找出所有经过该车站的车次

的信息,插入到列表控件中。

上述消息函数最终实现了获取查询的站点名,通过函数 FindArea()显示查询结果,并在该

地方创建闪烁指示灯,方便查询者快速找到。该函数的具体代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::FindArea(CString searchText)

2 {

3 int i=0;

4 CString str;

5 CString expression; //查找条件表达式

6 CMoRecordset recs; //定义记录集对象

7 bool flag=false;

8 float X,Y;

9 while(i<MAXLAYERS)

Page 16: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

279

10 {

11 if(i==15||i==21||i==23||i==25) //不同的层对象不同

12 expression.Format("(Resname Like '%s')",searchText); //构造查询语句

13 else

14 expression.Format("(Name Like '%s')",searchText);

15 if(flag)

16 break;

17 recs=m_mapLayer[i].SearchExpression(expression); //返回满足条件记录

18 if(recs)

19 {

20 recs.MoveFirst(); //移动到第一条记录

21 while(!recs.GetEof()) //如果没到最后一条

22 {

23 CMoFields fields(recs.GetFields());

24 CMoField shapeField(fields.Item(COleVariant("Shape")));

//获取数据

25 if(m_mapLayer[i].GetShapeType()==21) //图层几何类型为点

26 {

27 CMoPoint *pPoint=new CMoPoint();

28 pPoint->AttachDispatch(shapeField.Get_Value().pdispVal);

29 m_Map.CenterAt(pPoint->GetX(),pPoint->GetY()); //获取当前位置坐标

30 m_Map.FromMapPoint(pPoint->m_lpDispatch,&X,&Y);

31 createled(X,Y); //创建闪烁指示灯

32 require_attribute(searchText);

33 flag=true; //查询结果不为空

34 break;

35 }

36 else if(m_mapLayer[i].GetShapeType()==22) //图层几何类型为线

37 {

38 CMoLine *pLine=new CMoLine();

39 pLine->AttachDispatch(shapeField.Get_Value().pdispVal);

40 m_Map.FlashShape(pLine->m_lpDispatch,8);

41 flag=true;

42 require_attribute(searchText); //显示该地点相关属性

43 break;

44 pLine->ReleaseDispatch();

45 }

46 else

47 recs.MoveNext(); //记录后移

48 }

49 }

50 i++;

51 }

52 if(!flag) //没有查询到记录

53 {

54 CMainFrame *pFrame=(CMainFrame *)AfxGetMainWnd();

55 CAttribute *pView=(CAttribute *)(pFrame->m_wndSplitter2.GetPane(1,0));

//获取左边视图指针

56 pView->m_strAttribute=""; //属性框清空

57 pView->UpdateData(false); //输出显示

58 MessageBox("输入地名不存在!");

59 }

60 }

第 11~17 行代码实现了构造查询语句、执行查询并保存查询记录集。

第 25~35 行代码实现了查询图层几何类型为点的处理方法。

第 36~45 行代码实现了查询图层几何类型为线的处理方法。

上面的代码介绍了取记录集中记录并输出其对象属性。其中,介绍了如何通过图层(Layer)

Page 17: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

280

执行一个 SQL 查询语句,获得记录集后如何对其进行检索,并提取出具体字段内容。对于图

形图层,“Shape”字段也是固定存在的,其中存放了该要素的几何图形部分,通过使用字段的

GetShapeType()方法可以获得该图层是点层或线层,并进行相应的定位处理。字段的 Get_value()

方法返回的是一个 VARIANT 类型值,其中封装了各种数据类型,在 Shape 字段中,它封装的

是一个图形要素,可以通过 pdispVal 取得它的真正内容,并根据图层类型将其转换为相应的图

形要素,作为计算地图显示范围的依据。

7.5.5 公交换乘的实现

用户输入起始站和目的站,进行查询操作,系统给出所有可能的乘车方案,并推荐出最优

方案。所谓最优方案是指最少的换乘次数和最少的公交站点,同时优先考虑最少的换乘次数。

对于公交换乘可以分两种情况来考虑,一种是不需要转车,另外一种是需要转车。

1.不需要换乘的算法

查找起始站和目的站有相同的车次。实现时可以在站点表中先寻找到起始站和目的站中的

一个,同时记录它的车次并开始记录站点次数。再查找另一个站点,如找到则记录它的车次,

然后比较两个车次是否相同。如相同则找到,将结果显示出来,并将所过站点次数记录在一个

数组中。当将整个站点表遍历完成后,再与存储站点次数的数组进行比较,找出站点次数最少

的方案,则该方案为最优方案。算法流程如图 7-19 所示。

输入起始站和目的站

开始

第一条记录 0->total

是否是起始站

或目的站

起始和站

目的站相同

车次 total++ 输出结果进 a[i]

站点记录下移

记录是否到最后 对 a[i]进行排序,

找出最小值

输出最优方案

图 7-19 不需要换乘流程图

2.需要换乘的算法

根据现在公交车的实际状况,可以知道,一般的公交换乘只需一次换乘就可以了,但对于

N Y

N

Y

Y

N

Y

Page 18: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

281

一些偏远的地方,可能要两次换乘才行。

这里只考虑经过一次换乘的情况。首先通过遍历站点表中的所有站点来得到经过起始站和

目的站的所有车次,分别将它们存放在两个数组中。可以遍历起始站的车次及它的每一个站点,

并将它们与经过目的站的车次及站点进行比较,如果两者相同,则输出乘车方案,同时将两者

的站点数相加,就可以得到该种方式经过的公交站点数。将它保存在一个数组中,等找到所有

乘车方案后,对该数组排序,得到站点数最少的乘车方案,则该方案为最优方案。算法流程图

如图 7-20 所示。

开始

输入起始站和目的站

站点表第一条记录

是否是起始站或

目的站

s_road++||e_road++,

S_roadway[i]||E_roadway[i]

<-站点

站点表记录后移

记录是否到最后

图 7-20 需要换乘的流程图

公交换乘同样是 Tab 控件的一个属性页。添加一个对话框资源,将 ID 改为

IDD_TRANSFER,修改对话框的属性。在对话框界面上添加如图 7-21 所示的控件。

Y

N

N

Y

Page 19: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

282

图 7-21 公交换乘界面

在对话框界面上空白区域双击,进入添加类对话框。为对话框创建一个新类 CTransView。

通过类向导更改控件的 ID 为控件添加相应的变量,添加 WM_INITDIALOG 消息响应函数

OnInitDialog()代码如下:

代码位置:见光盘中本章源代码的 CTransView 类。

1 BOOL CTransView::OnInitDialog()

2 {

3 CPropertyPage::OnInitDialog();

4 CStationSet m_stationSet; //定义记录集对象

5 int i=0; //定义整型变量

6 m_stationSet.Open(); //打开记录集

7 m_stationSet.MoveFirst(); //移动到首条记录

8 while(!m_stationSet.IsEOF()) //遍历记录集

9 {

10 m_start.InsertString(i,m_stationSet.m_STATION); //插入数据到列表框中

11 m_end.InsertString(i,m_stationSet.m_STATION);

12 i++;

13 m_stationSet.MoveNext(); //移动到下一条记录

14 }

15 m_stationSet.Close(); //关闭记录集

16 m_start.SetCurSel(1);

17 m_end.SetCurSel(5); //设置当前选项

18 return TRUE;

19 }

第 4~15 行代码从数据库中查找数据插入到组合框中。

第 16、17 行代码设置组合框的默认选中项。

上述代码完成了初始化工作,将所有站点的名称插入到开始站点和终点站这两个组合框

中。给【查询】按钮添加消息响应函数,其中的代码如下:

代码位置:见光盘中本章源代码的 CTransView 类。

1 void CTransView::OnButFind()

2 {

3 CWnd *pwnd;

4 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类指针

5 app->m_show="";

6 pwnd=GetDlgItem(IDC_EDIT_SCHEME); //获取控件的指针

7 m_scheme="";

8 UpdateData(false); //更新数据

9 CString start,end;

10 m_start.GetLBText(m_start.GetCurSel(),start); //获取文本

11 m_end.GetLBText(m_end.GetCurSel(),end);

12 if(start!=end) //起始站和终点站不同

13 {

14 if(m_InfoMation.Query(pwnd,start,end)) //查询

15 {

16 m_scheme=app->m_show;

17 UpdateData(false); //显示结果

18 }

19 }

20 else

21 AfxMessageBox("起始站和终点站不能相同!");

22 }

第 3~4 行代码为获取应用类的指针。

Page 20: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

283

第 9~11 行代码为获取下拉框中的文本。

上述代码执行公交线路的查询,通过函数 GetLBText()获取开始站和终点站,最终调用函

数 Query()完成公交的换乘。

上面的代码很简单,读者也许会有疑问,公交换乘的算法是怎样实现的呢?请注意上述代

码中的一个函数的调用 m_InfoMation.Query(),这就是关键所在。

m_InfoMation 是 CInfoMation 类的一个对象,CInfoMation 类实现了公交换乘的算法,下

面对 CInfoMation 类进行介绍,由于本章篇幅的限制,在此只介绍相关的函数,其他函数参见

光盘中的源代码。下面介绍 CInfoMation 类的主要的成员函数。查询函数 Query()查询公交换乘

的信息。代码如下所示:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 BOOL CInfoMation::Query(CWnd *pwnd,CString start,CString end)

2 {

3 Num=0;

4 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类的指针

5 app->m_show="";

6 Num_BusRoad=0; //线路的初始化

7 s_road=e_road=0;

8 if(Same_RoadWay(pwnd,start,end)) //判断是否是同样车次

9 {

10 app->m_show+="\r\n推荐方案:方案"+BestMode(); //输出方案

11 return 1;

12 }

13 else if(!Dif_RoadWay(start,end))

14 {

15 AfxMessageBox("此方案不存在!");

16 return 0;

17 }

18 app->m_show+="\r\n推荐方案:方案"+BestMode(); //显示最佳方案

19 return 1;

20 }

第 8~12 行代码为显示不需要换车的最佳乘车方案。

第 13~18 行代码为显示需要转一次车的最佳乘车方案。

上述代码中函数 Same_RoadWay()实现判断两个站是否在同一条线路中,如果是则返回

TRUE,此时不需要换乘。如果两个站点在不同的线路中,函数 Dif_RoadWay()返回 FALSE,

此时需要换乘公交才能到达目的站,最终调用函数 BestMode()显示最佳的行车路线。函数

Same_RoadWay()查询两站点是否在相同的车次中。其中的代码如下:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 BOOL CInfoMation::Same_RoadWay(CWnd *pwnd,CString start,CString end)

2 {

3 CString str,m_sNum; //定义字符串变量

4 CStationSet m_StationSet; //定义记录集对象

5 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类指针

6 Init(); //初始化函数

7 if(!m_StationSet.IsOpen())

8 m_StationSet.Open(); //打开记录集

9 m_StationSet.MoveFirst(); //移动到首条记录

10 while(!m_StationSet.IsEOF()) //遍历记录集

11 {

12 if(m_StationSet.m_STATION==start) //查找开始站点

13 {

Page 21: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

284

14 startflag=!startflag; //标记的设置

15 m_BusInfo[0].RoadWay=m_StationSet.m_ID; //赋值

16 m_station=m_StationSet.m_ID;

17 m_BusInfo[0].StationName=m_StationSet.m_STATION;

18 }

19 else if(m_StationSet.m_STATION==end) //查找终点站

20 {

21 startflag=!startflag;

22 m_BusInfo[1].RoadWay=m_StationSet.m_ID;

23 m_station=m_StationSet.m_ID;

24 m_BusInfo[1].StationName=m_StationSet.m_STATION;

25 }

26 if(m_BusInfo[1].RoadWay==m_BusInfo[0].RoadWay) //同样的线路

27 {

28 str.Format("\r\n方案%d:从%s乘 %s路 车到%s下车",

29 Num,start,m_BusInfo[0].RoadWay,end); //构造显示字符串

30 app->m_show+=str;

31 m_BusRoad[Num_BusRoad].Precept=Num++;

32 m_BusRoad[Num_BusRoad++].Number=busnum;

33 Init();

34 }

35 if(startflag)

36 {

37 if(m_StationSet.m_ID!=m_station)

38 busnum=0;

39 else

40 busnum++;

41 }

42 m_StationSet.MoveNext(); //移动到下一条记录

43 }

44 if(app->m_show!="") //如果查找到目标

45 return 1;

46 if(m_StationSet.IsEOF()) //没有查找到站点

47 {

48 return 0;

49 }

50 else

51 return 1;

52 }

第 10~18 行代码为查找开始站的信息。

第 19~25 行代码为查找终点站的情况。

第 24~36 行代码为确定最佳的线路。

上述代码中先通过遍历记录集找出包含开始站和终点站的线路,将信息保存在 m_BusInfo

数组中,然后比较 m_BusInfo 数组的信息。如果相同,表示两个站点在同一条线路中,则为最

佳线路。函数 BestMode()实现寻求最优的方案。该函数中代码如下:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 CString CInfoMation::BestMode()

2 {

3 int n=m_BusRoad[0].Number,j=m_BusRoad[0].Precept; //定义整型变量

4 for(int i=0;i<Num_BusRoad;i++)

5 {

6 if(m_BusRoad[i].Number<n) //条件的判断

7 {

8 n=m_BusRoad[i].Number; //查找路线

9 j=m_BusRoad[i].Precept;

10 }

Page 22: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

285

11 }

12 CString str; //定义字符串变量

13 str.Format("%d",j); //格式化操作

14 return str; //返回值

15 }

第 4~11 行代码为比较乘车方案。

第 12~14 行代码为返回最佳的乘车方案。

上述代码中由于 m_BusRoad 数组中保存着符合条件的线路信息,通过在这些线路中循环

找到起始站点和终点站间站点最少的线路,这条线路为最佳线路,生成乘车方案。函数

Num_Station()实现计算同一条路线两点间站点数。该函数其中代码如下所示:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 int CInfoMation::Num_Station(CString s_start,CString roadway,CString s_end)

2 {

3 CStationSet m_StationSet; //定义记录集对象

4 bool flag=false; //定义布尔型变量

5 Init(); //初始化函数

6 if(!m_StationSet.IsOpen()) //打开记录集

7 m_StationSet.Open();

8 m_StationSet.MoveFirst(); //移动到首条记录

9 while(!m_StationSet.IsEOF()) //遍历记录集

10 {

11 if(m_StationSet.m_ID==roadway) //查找路线

12 {

13 if(m_StationSet.m_STATION==s_start||m_StationSet.m_STATION

==s_end)

14 flag=!flag;

15 if(flag)

16 busnum++;

17 }

18 m_StationSet.MoveNext(); //移动到下一条记录

19 }

20 m_StationSet.Close(); //关闭记录集

21 return busnum;

22 }

第 3~8 行代码进行初始化操作。

第 9~20 行代码为计算同一条线路两个站间的站点数。

上述代码中通过参数传递开始站、终点站和线路,通过遍历记录集找到相应的线路,找到

开始站时就开始计数,如果站点为终点站时变量 flag 为 false 就停止计数,则变量 busnum 中保

存的就是两个站点间的站点数。函数 Match()实现对站点进行匹配。其中的代码如下所示:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 BOOL CInfoMation::Match(CString percept,CString station)

2 {

3 CStationSet m_StationSet; //定义记录集对象

4 bool flag=false; //定义布尔变量

5 if(!m_StationSet.IsOpen()) //打开记录集

6 m_StationSet.Open();

7 for(int i=0;i<e_road;i++) //路线比较

8 {

9 m_StationSet.MoveFirst(); //移动到首条记录

10 while(!m_StationSet.IsEOF()) //遍历记录集

11 {

12 if(m_StationSet.m_ID==E_RoadWay[i]&&m_StationSet.m_ID=

Page 23: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

286

13 percept&&m_StationSet.m_STATION==station)

14 {

15 m_percept=m_StationSet.m_ID;

16 flag=true; //设置标记

17 return 1;

18 }

19 else

20 m_StationSet.MoveNext(); //移动到下一条记录

21 }

22 }

23 if(!flag) //返回值

24 return 0;

25 else

26 return 1;

27 }

第 7~22 行代码为查找匹配站点的信息。

第 23~26 行代码为函数的返回值。

上述代码中变量 e_road 保存了所有经过终点站的车次的总数,数组 E_RoadWay 中保存了

经过终点站的所有车次,通过遍历记录集查找符合条件的站点。函数 Precept()实现了生成乘车

方案。该函数中的代码如下:

代码位置:见光盘中本章源代码的 CInfoMatio 类。

1 BOOL CInfoMation::Precept(CString start,CString end)

2 {

3 CString str; //定义字符串变量

4 CStationSet m_StationSet; //定义记录集对象

5 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类指针

6 if(!m_StationSet.IsOpen()) //打开记录集

7 m_StationSet.Open();

8 m_StationSet.MoveFirst(); //移动到首条记录

9 for(int i=0;i<s_road;i++)

10 {

11 while(!m_StationSet.IsEOF()) //遍历记录集

12 {

13 if(m_StationSet.m_ID==S_RoadWay[i]) //查找线路

14 {

15 if(Match(m_StationSet.m_ID,m_StationSet.m_STATION))

16 {

17 str.Format("\r\n方案%d: 从%s乘 %s路 到 %s转乘 %s路 到%s下车",

18 Num,start,m_StationSet.m_ID,m_StationSet.m_STATION,m_

percept,end);

19 app->m_show+=str;

20 m_BusRoad[Num_BusRoad].Precept=Num++;

21 m_BusRoad[Num_BusRoad++].Number=

22 Dif_StationNum(start,m_StationSet.m_ID,

23 m_StationSet.m_STATION,m_percept,end);

24 m_percept="";

25 break;

26 }

27 }

28 m_StationSet.MoveNext(); //移动到下一条记录

29 }

30 }

31 m_StationSet.Close(); //关闭记录集

32 if(str!="")

33 return 1;

34 else

35 return 0;

Page 24: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

287

36 }

第 9~30 行代码为查找信息生成最佳的乘车方案。

第 32~35 行代码为函数的返回值。

上述代码中变量 s_road 保存了所有经过开始站的车次的总数,数组 S_RoadWay 中保存了

经过开始站的所有车次,通过遍历记录集查找符合条件的站点,调用函数 Match()查看站点是

否匹配,生成乘车方案。

7.6 公交信息维护模块

公交信息的维护需要管理员登录后才可以进行,公交信息维护是维护整个系统的保证,此

模块可以确保系统中站点、公交线路的及时更新,通过该模块用户可实现对公交信息进行一般

的管理操作,包括单个站点和整条线路的维护及系统日志的查询操作。下面将从功能分析、技

术分析、界面设计及各子模块的实现 4 个方面介绍。

7.6.1 公交信息维护模块功能分析

该模块要求用户登录系统后可以进行公交信息的维护,如果不登录这些操作都被禁用。包

括单个站点和整条线路的维护操作。具体实现:单个站点的修改及删除操作;对整条线路的添

加及删除操作;给选中景点添加属性和图片。这些功能可以保证系统的即时更新。另外,还实

现了查询系统日志信息。

7.6.2 公交信息维护模块技术分析

本模块的功能主要是对数据库的一些基本的操作,其中大部分操作都是通过 SQL 语句来

完成的。此外还涉及菜单的禁用,当用户登录后,记录登录信息,通过 EnableWindow()实现了

各功能菜单的启用。选中景点属性的添加实现比较容易,是通过执行“ insert into 表名

values(值)”语句实现的。

7.6.3 管理员登录的实现

管理员的登录几乎是每个系统中必不可少的部分,这个功能的实现其实并不复杂,只需要

通过用户输入的用户名和密码在数据表中查找匹配的数据。如果查找到则登录成功,可以进行

相关的操作;否则登录失败。添加一个对话框资源将 ID 改为 IDD_DLG_PASSWORD,添加如

图 7-22 所示的控件。

图 7-22 用户登录界面

在对话框的空白区域双击,进入添加类对话框。为对话框资源创建一个新类 CLoginDlg,

通过类向导为控件关联相关的变量,并给对话框添加 WM_INITDIALOG 初始化消息,该消息

Page 25: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

288

函数中代码如下:

Page 26: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

289

代码位置:见光盘中本章源代码的 CLoginDlg 类。

1 BOOL CLoginDlg::OnInitDialog()

2 {

3 CDialog::OnInitDialog();

4 LOGFONT LogFont; //定义结构变量

5 GetFont()->GetLogFont(&LogFont); //获取字体的信息

6 LogFont.lfHeight+=LogFont.lfHeight/2; //修改字体的信息

7 LogFont.lfWidth+=LogFont.lfWidth/2;

8 CFont m_font; //定义字体变量

9 m_font.CreateFontIndirect(&LogFont); //创建字体

10 GetDlgItem(IDC_STATIC_LOG)->SetFont(&m_font); //设置对话框的字体

11 SetWindowText("用户登录"); //设置对话框的标题

12 return TRUE;

13 }

第 4~10 行代码设置对话框显示的字体。

第 11 行代码设置对话框的标题。

上述代码中调用函数 GetFont()获取当前字体的信息,对字体的信息进行修改后,调用函数

CreateFontIndirect()创建新的字体的信息。给【登录】按钮添加消息响应函数,其中的代码如下:

代码位置:见光盘中本章源代码的 CTransView 类。

1 void CLoginDlg::OnUserlog()

2 {

3 CPasswordSet m_pset; //定义记录集对象

4 UpdateData(); //更新数据

5 try

6 {

7 if(m_pset.Open()) //如果已经打开记录集

8 m_pset.Close(); //则关闭

9 m_pset.m_strFilter.Format("USER_ID='%s'",m_name); //设置查询内容

10 m_pset.Open(CRecordset::snapshot,NULL,CRecordset::none); //打开记录集

11 if(m_pset.IsEOF()) //遍历记录集

12 {

13 m_pset.Close(); //关闭记录集

14 MessageBox("用户名错误请重试!","提示!",MB_ICONWARNING);

15 m_name=""; //清空字符串

16 m_pass="";

17 UpdateData(false);

18 return;

19 }

20 m_pset.m_strFilter="";

21 m_pset.Close();

22 m_pset.m_strFilter.Format("USER_PASSWORD='%s'",m_pass);

//设置查询内容

23 m_pset.Open(CRecordset::snapshot,NULL,CRecordset::none);

24 if(m_pset.IsEOF()) //遍历记录集查询

25 {

26 m_pset.Close();

27 MessageBox("密码错误请重试!","提示!",MB_ICONWARNING);

28 m_pass="";

29 UpdateData(false);

30 return;

31 }

32 else

33 {

34 //… //省略添加日志代码

Page 27: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

290

35 EndDialog(IDOK);

36 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类指针

37 app->m_User_ID=m_name; //变量的赋值

38 app->bEnableMenu=true;

39 }

40 }

41 catch(CDBException *e) //捕捉异常

42 {

43 e->ReportError();

44 return;

45 }

46 }

第 7~19 行代码从数据表中查找用户名和密码是否存在。

第 24~31 行代码为当用户名密码错误时进行的操作。

第 32~39 行代码为用户登录成功后,将用户的操作添加到用户日志表中。

第 41~45 行代码为数据库操作出错时捕捉异常处理。

上述代码中的查找是通过记录集的成员变量m_strFilter来实现的,先判断用户名是否存在,

再判断密码是否正确。如果登录成功,则把用户登录的信息添加到日志表中,记录用户的操作

信息。

7.6.4 单个站点维护的实现

单个站点的维护包括删除单个站点和修改单个站点的信息。下面先介绍单个站点删除的实

现。添加一个对话框资源,将 ID 改为 IDD_DELSINGLESTATION,添加如图 7-23 所示的控件。

图 7-23 【删除单个站点】界面

在该对话框空白区域双击,进入添加类对话框。为对话框资源创建一个新类

DelSingleStation,通过类向导为控件关联相关的变量,并给对话框添加 WM_INITDIALOG 初

始化消息。该消息函数中的代码如下:

代码位置:见光盘中本章源代码的 DelSingleStation 类。

1 BOOL DelSingleStation::OnInitDialog()

2 {

3 CDialog::OnInitDialog();

4 int i=0; //定义整型变量

5 CString str; //定义字符串变量

6 CBusSet m_busset; //定义记录集对象

7 if(!m_busset.IsOpen())

Page 28: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

291

8 m_busset.Open();

9 m_busset.MoveFirst(); //移动到首条记录

10 while(!m_busset.IsEOF()) //遍历记录集

11 {

12 m_roadway.InsertString(i++,m_busset.m_ID_BUS); //插入数据

13 m_busset.MoveNext();

14 }

15 m_roadway.SetCurSel(0); //设置选中项

16 m_roadway.GetLBText(0,str);

17 m_list.InsertColumn(0,"车次"); //插入数据到列表控件

18 m_list.InsertColumn(1,"站点");

19 RECT rect;

20 m_list.GetWindowRect(&rect); //获取控件的区域

21 int wid=rect.right-rect.left;

22 m_list.SetColumnWidth(0,wid/3);

23 m_list.SetColumnWidth(1,2*wid/3);

24 m_list.SetExtendedStyle(LVS_EX_FULLROWSELECT); //设置列表控件的风格

25 i=0;

26 CStationSet m_StationSet; //定义记录集对象

27 if(!m_StationSet.IsOpen())

28 m_StationSet.Open();

29 m_StationSet.MoveFirst(); //移动到首条记录

30 while(!m_StationSet.IsEOF())

31 {

32 if(m_StationSet.m_ID==str) //查找匹配数据

33 {

34 m_list.InsertItem(i,str); //插入数据到列表中

35 m_list.SetItemText(i++,1,m_StationSet.m_STATION);

36

37 }

38 m_StationSet.MoveNext(); //移动到下一条记录

39 }

40 return TRUE;

41 }

第 6~14 行代码将车次的信息插入到下拉列表框中。

第 17~24 行代码设置列表控件的信息。

第 26~39 行代码在列表控件中输出一些初始信息。

上述代码完成初始化工作,首先将线路的信息插入到下拉列表框中,然后对列表控件进行

设置,并根据列表框中的车次读取出该车次经过的所有站点,填充到列表控件中。给【删除】

按钮添加消息响应函数,对应的代码如下:

代码位置:见光盘中本章源代码的 DelSingleStation 类。

1 void DelSingleStation::OnDel()

2 {

3 CStationSet m_StationSet; //定义记录集对象

4 CString str; //定义字符串变量

5 m_roadway.GetLBText(m_roadway.GetCurSel(),str); //获取文本

6 if(!m_StationSet.IsOpen()) //打开记录集

7 m_StationSet.Open();

8 m_StationSet.MoveFirst(); //移动到首条记录

9 while(!m_StationSet.IsEOF()) //遍历记录集

10 {

11 if(m_StationSet.m_ID==str&&m_StationSet.m_STATION==m_str)

12 {

13 m_StationSet.Delete(); //删除数据

14 AfxMessageBox("删除记录成功!");

Page 29: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

292

15 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类指针

16 CAction_Time_Set *m_ActionSet;

17 CDatabase *db;

18 db=new CDatabase;

19 m_ActionSet=new CAction_Time_Set(db);

20 CString strSql;

21 CTime time=CTime::GetCurrentTime(); //获取当前的时间

22 CString strtime=time.Format("%Y-%m-%d %H:%M:%S"); //将时间格式化

23 m_ActionSet->Open();

24 CString straction="删除一个站点";

25 strSql="insert into Action_Log(USER_ID,ACTION,COME_LEAVE_TIME)

26 values('" +app->m_User_ID+"','"+straction+"','"+strtime+"')";

27 db->ExecuteSQL(strSql); //执行 SQL语句

28 m_ActionSet->Requery(); //刷新记录集

29 m_ActionSet->Close();

30 break;

31 }

32 m_StationSet.MoveNext();

33 }

34 Refresh();

35 }

第 6~14 行代码为删除相应的记录。

第 16~29 行代码将用户的操作添加到日志中。

删除站点的信息时注意要删除的是某个车次的站点的信息,而不是所有车次的站点,所以

删除记录时首先获取车次名称,然后获取站点的名称,通过遍历记录集查找到相应的信息,调

用函数 Delete()删除记录。

要实现单个站点的修改,再添加一个对话框资源,将 ID 改为 IDD_CHANGE_STATION,

添加如图 7-24 所示的控件。

图 7-24 修改站点信息界面

双击对话框界面为对话框资源创建一个新类 ChangeStation,通过类向导为控件关联相关的

变量,并给对话框添加 WM_INITDIALOG 初始化消息,该消息函数中代码如下:

代码位置:见光盘中本章源代码的 ChangeStation 类。

1 BOOL ChangeStation::OnInitDialog()

2 {

3 CDialog::OnInitDialog();

Page 30: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

293

4 CBusSet m_busset; //定义记录集对象

5 int i=0; //定义整型变量

6 if(!m_busset.IsOpen()) //打开记录集

7 m_busset.Open();

8 m_busset.MoveFirst(); //移动到首条记录

9 while(!m_busset.IsEOF()) //遍历记录集

10 {

11 m_roadway.InsertString(i++,m_busset.m_ID_BUS); //插入数据到下拉列表框

12 m_busset.MoveNext();

13 }

14 m_roadway.SetCurSel(0); //设置选中项

15 m_busset.Close(); //关闭记录集

16 RECT rect;

17 m_list.GetWindowRect(&rect); //区域的获取

18 int wid=rect.right-rect.left;

19 m_list.InsertColumn(0,"车次",LVCFMT_CENTER,wid/3);

20 m_list.InsertColumn(1,"站点",LVCFMT_CENTER,2*wid/3);

21 CString str; //定义字符串变量

22 m_roadway.GetLBText(0,str); //获取文本

23 i=0;

24 CStationSet m_StationSet; //定义记录集对象

25 if(!m_StationSet.IsOpen()) //打开记录集

26 m_StationSet.Open();

27 m_StationSet.MoveFirst();

28 while(!m_StationSet.IsEOF()) //遍历记录集

29 {

30 if(m_StationSet.m_ID==str)

31 {

32 m_list.InsertItem(i,str); //插入数据到列表中

33 m_list.SetItemText(i++,1,m_StationSet.m_STATION);

34 }

35 m_StationSet.MoveNext();

36 }

37 m_StationSet.Close(); //关闭记录集

38 m_list.SetExtendedStyle(WS_CHILD|WS_CLIPSIBLINGS|WS_EX_TOOLWINDOW|

39 WS_BORDER|LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); //修改其样式

40 return TRUE;

41 }

第 6~14 行代码为添加数据到组合框中。

第 17~20 行代码为设置列表控件的初始信息。

第 25~37 行代码将初始数据添加到列表控件中。

上述代码完成初始化工作,首先填充组合框,然后初始化列表控件,将组合框中车次经过

的站点显示在列表控件中。

给【确定】按钮添加消息响应函数,实现修改选择的站点。同时要把操作写入日志中。其

中的代码如下:

代码位置:见光盘中本章源代码的 ChangeStation 类。

1 void ChangeStation::OnChangeStation()

2 {

3 int i=0; //定义整型变量

4 CString str1,str; //定义字符串变量

5 m_roadway.GetLBText(m_roadway.GetCurSel(),str1); //获取文本

6 CDatabase *m_datebase;

7 m_datebase=new CDatabase; //初始化

8 CStationSet *m_Set;

Page 31: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

294

9 m_Set=new CStationSet(m_datebase); //数据库与记录集关联

10 m_Set->Open(); //打开记录集

11 str.Format("delete from STATION where ID='%s'",str1); //设置 SQL语句

12 m_datebase->ExecuteSQL(str); //执行 SQL语句

13 m_Set->Requery(); //刷新

14 CStationSet m_StationSet;

15 if(!m_StationSet.IsOpen())

16 m_StationSet.Open();

17 m_StationSet.AddNew(); //添加新的记录

18 do

19 {

20 m_StationSet.m_ID=m_list.GetItemText(i,0);

21 m_StationSet.m_STATION=m_list.GetItemText(i++,1);

22 }while(i<m_list.GetItemCount());

23 m_StationSet.Update(); //更新数据

24 m_StationSet.Requery();

25 CMapPublicApp *app=(CMapPublicApp *)AfxGetApp(); //获取应用类的指针

26 CAction_Time_Set *m_ActionSet;

27 CDatabase *db;

28 db=new CDatabase;

29 m_ActionSet=new CAction_Time_Set(db);

30 CString strSql;

31 CTime time=CTime::GetCurrentTime(); //获取当前的系统时间

32 CString strtime=time.Format("%Y-%m-%d %H:%M:%S");

33 m_ActionSet->Open();

34 CString straction="修改一个站点";

35 strSql="insert into Action_Log(USER_ID,ACTION,COME_LEAVE_TIME)

36 values('" +app->m_User_ID+"','"+straction+"','"+strtime+"')";

37 db->ExecuteSQL(strSql); //执行 SQL语句

38 m_ActionSet->Requery();

39 m_ActionSet->Close();

40 AfxMessageBox("修改记录成功!");

41 m_StationSet.Close();

42 }

第 6~12 行代码为删除以前站点的信息。

第 14~24 行代码为添加新的站点信息到数据表中。

第 25~39 行代码将用户的操作添加到日志中。

上述代码中通过构造 SQL 语句删除线路的信息,然后获取列表中的所有数据,将修改后

的站点信息重新添加到数据库中,最后完成用户日志的添加。

7.6.5 整条线路维护的实现

整条线路的维护包括添加一条线路和删除一条线路,先介绍添加一条线路的实现。添加一

个对话框资源并将 ID 改为 IDD_ADD_ROADWAY,添加如图 7-25 所示的控件。

Page 32: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

295

图 7-25 添加线路界面

双击对话框界面为对话框资源创建一个新类 AddOneRoadway,通过类向导为控件关联相

关的变量。给【确定】按钮添加消息响应函数,该函数实现线路的添加操作。函数中代码如下:

代码位置:见光盘中本章源代码的 AddOneRoadway 类。

1 void AddOneRoadway::OnOk()

2 {

3 UpdateData(true); //数据的更新

4 if(!firstflag) //是否第一次添加

5 {

6 if(m_roadway!=""&&m_station!=""&&m_startstation!=""&&

7 m_endstation!=""&&m_sendtime!=""&&m_stoptime!="") //条件的判断

8 {

9 stationflag=!stationflag;

10 CBusSet m_busset; //定义记录集对象

11 if(!m_busset.IsOpen()) //打开记录集

12 m_busset.Open();

13 m_busset.AddNew(); //添加新的记录

14 m_busset.m_ID_BUS=m_roadway; //各字段赋值

15 m_busset.m_BEGIN=m_startstation;

16 m_busset.m_END=m_endstation;

17 m_busset.m_AMTIME=m_sendtime;

18 m_busset.m_PMTIME=m_stoptime;

19 m_busset.Update(); //记录的更新

20 m_busset.Requery();

21 m_busset.Close(); //关闭记录集

22 CStationSet m_StationSet; //定义记录集对象

23 if(!m_StationSet.IsOpen())

24 m_StationSet.Open();

25 m_StationSet.AddNew(); //添加新的记录

26 m_StationSet.m_ID=m_roadway; //记录的赋值

27 m_StationSet.m_STATION=m_station;

28 m_StationSet.Update();

29 m_StationSet.Requery();

30 m_StationSet.Close();

31 m_station="";

32 UpdateData(false); //更新数据

33 firstflag=true;

34 CWnd *pwnd;

35 pwnd=GetDlgItem(IDC_EDIT_ROADWAY); //获取控件的指针

36 pwnd->EnableWindow(false); //禁用控件

37 pwnd=GetDlgItem(IDC_EDIT_START);

38 pwnd->EnableWindow(false);

39 pwnd=GetDlgItem(IDC_EDIT_SEND);

40 pwnd->EnableWindow(false);

41 pwnd=GetDlgItem(IDC_EDIT_END);

42 pwnd->EnableWindow(false);

43 pwnd=GetDlgItem(IDC_EDIT_STOP);

44 pwnd->EnableWindow(false);

45 GetDlgItem(ID_OK)->SetFocus();

46 }

47 else

48 MessageBox("信息不能为空!"); //消息提示

49 }

第 4~32 行代码为新记录的添加。

第 33~45 行代码实现了部分控件的禁用。

Page 33: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

296

上述代码中通过调用函数 AddNew()将用户输入的数据添加到数据库中,这样添加的线路

的信息只有起始站和终点站两个站点,这时禁用除了站点外的其他编辑框。上述代码实现了第

一次添加线路时的操作。下面代码实现了当线路数据表中有数据时的对应操作,代码如下:

50 else

51 {

52 UpdateData(true); //数据的更新

53 CStationSet m_StationSet; //定义记录集

54 if(!m_StationSet.IsOpen()) //打开记录集

55 m_StationSet.Open();

56 m_StationSet.MoveLast(); //移动到最后一条记录

57 m_StationSet.AddNew(); //添加新的记录

58 m_StationSet.m_ID=m_roadway;

59 m_StationSet.m_STATION=m_station;

60 m_StationSet.Update();

61 m_StationSet.Requery();

62 m_StationSet.Close(); //关闭记录集

63 m_station="";

64 UpdateData(false);

65 GetDlgItem(IDC_EDIT_STATION)->SetFocus(); //设置焦点

66 addroadflag=true;

67 }

68 }

第 50~67 行代码实现了当数据表中有数据时对应的操作。

已经添加了起始站和终点站两个站点,还需要添加其他的站点。在上述代码中当只有站

点的编辑框为可用的状态时,通过不断在站点编辑框中添加数据完成线路中所有站点的添加。

下面介绍删除一条线路的实现过程。添加一个对话框资源,将 ID 号修改为 IDD_DEL_

ROADWAY,添加如图 7-26 所示的控件。

图 7-26 删除线路界面

在对话框界的空白区域双击,进入添加类界面。为对话框资源创建一个新类 DelRoadway,

通过类向导为控件关联相关的变量,并给对话框添加初始化消息 WM_INITDIALOG,对应的

函数 OnInitDialog()中的代码如下:

代码位置:见光盘中本章源代码的 DelRoadway 类。

1 BOOL DelRoadway::OnInitDialog()

2 {

3 CDialog::OnInitDialog();

4 int i=0; //定义整型变量

5 m_list.SetTextColor(RGB(0,0,255)); //设置列表文本的颜色

Page 34: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

297

6 RECT rect;

7 m_list.GetWindowRect(&rect);

8 int wid=rect.right-rect.left;

9 DWORD style=m_list.GetExtendedStyle(); //获取控件的类型

10 m_list.SetExtendedStyle(style|LVS_EX_GRIDLINES|LVS_EX_FULLROWSELECT);

11 m_list.InsertColumn(0,"车次",LVCFMT_CENTER,wid);

12 CBusSet m_busset; //定义记录集对象

13 if(!m_busset.IsOpen()) //打开记录集

14 m_busset.Open();

15 m_busset.MoveFirst(); //移动到首条记录

16 while(!m_busset.IsEOF()) //遍历记录集

17 {

18 m_list.InsertItem(i++,m_busset.m_ID_BUS);

19 m_busset.MoveNext(); //移动到下一条记录

20 }

21 m_busset.Close(); //关闭记录集

22 return TRUE;

23 }

第 5~11 行代码为列表控件的初始化工作。

第 12~21 行代码将线路的信息添加到列表控件中。

上述代码完成了初始化工作,把所有车次的名称插入到列表控件中。给【删除】按钮添加

消息响应函数,实现删除选择的线路。同时要把操作写入日志中。其中的代码如下:

代码位置:见光盘中本章源代码的 DelRoadway 类。

1 void DelRoadway::OnDel()

2 {

3 CString str;

4 int CurSel=m_list.GetNextItem(-1,LVNI_ALL | LVNI_SELECTED);

//获取下个子项的索引

5 str=m_list.GetItemText(CurSel,0); //获取列表控件的文本

6 CBusSet m_busset; //定义记录集对象

7 if(!m_busset.IsOpen()) //打开记录集

8 m_busset.Open();

9 m_busset.MoveFirst(); //移动到首条记录

10 while(!m_busset.IsEOF()) //变量记录集

11 {

12 if(m_busset.m_ID_BUS==str)

13 {

14 m_busset.Delete(); //删除数据

15 MessageBox("删除成功!");

16 … //省略添加日志代码

17 break;

18 }

19 else

20 m_busset.MoveNext(); //移动到下一条记录

21 }

22 m_busset.Close();

23 CStationSet m_StationSet; //定义记录集对象

24 if(!m_StationSet.IsOpen()) //打开记录集

25 m_StationSet.Open();

26 m_StationSet.MoveFirst(); //移动到首条记录

27 while(!m_StationSet.IsEOF()) //遍历记录集

28 {

29 if(m_StationSet.m_ID==str)

30 {

31 m_StationSet.Delete();

32 m_StationSet.MoveNext(); //记录后移

Page 35: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

298

33 }

34 else

35 m_StationSet.MoveNext(); //记录后移

36 }

37 m_StationSet.Close(); //关闭记录集

38 m_list.DeleteAllItems(); //删除列表的所有项

39 int i=0;

40 if(!m_busset.IsOpen())

41 m_busset.Open();

42 m_busset.MoveFirst();

43 while(!m_busset.IsEOF()) //如果没到最后一条

44 {

45 m_list.InsertItem(i++,m_busset.m_ID_BUS);

46 m_busset.MoveNext(); //记录后移

47 }

48 m_busset.Close();

49 }

第 3~5 行代码实现了获取组合框中用户选项的线路。

第 10~22 行代码实现了线路的删除操作。

第 23~36 行代码实现了站点记录集中对应线路的删除操作。

第 38~47 行代码为列表控件数据的更新。

上述代码中首先获取用户选择的线路名称,通过遍历记录集查找到当前车次在记录集中的

位置,删除该数据,在用户日志中记录用户的操作,由于车次的信息在其他的表中也存在,所

以还应删除其他表中相关的信息。

7.7 地图信息模块设计

地图信息模块是系统中重要的模块,也是本系统的特色所在。该模块主要的功能是电子地

图的显示,可以通过工具栏上的按钮来控制地图的放大和缩小,也可以通过拖动鼠标来实现地

图的漫游,下面从功能分析、技术分析及各子模块的实现几个方面详细介绍地图信息模块的具

体开发过程。

7.7.1 地图信息模块功能分析

该模块主要是对电子地图的操作,这些操作是通过程序主界面上的两个工具栏控制的,上

面的工具栏控制着地图的放大、缩小、漫游、图片的保存、测量两点间的距离等操作。左侧的

工具栏主要控制地图信息的显示,通过工具栏上的按钮来加载不同的图层来显示医疗机构、政

府部门、人文部门、村庄和学校等信息。

7.7.2 地图信息模块技术分析

本模块功能一部分是基本控件的使用,在这个模块中主要应用的是 Tab 控件和列表控件,

这些都是常用的控件。对数据库的操作通过 SQL 语句的构造来完成数据库的查询、修改及更

新等功能。地图放大、缩小、漫游操作,是通过使用 MapObjects 2.3 Map Control 控件对应的

CMap1 类成员函数来实现的。使用 SetExtent()函数可实现地图的放大或缩小操作;而漫游则使

用 Pan()函数实现。

7.7.3 地图放大、缩小的实现

首先介绍 MapObjects 的使用方法。使用 MapObjects 同其他的 ActiveX 控件没有什么两样,

Page 36: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

299

先安装 MapObjects 控件。安装成功后 MapObjects 就已经注册成功了。MapObjects 的安装包在

本书附带的光盘中会提供给读者。

选择【Project】|【Add To Project】|【Components and Controls...】命令,打开【Components

and Controls Gallery】对话框。从其【Registered ActiveX Controls】文件夹下的已登记控件列表

中选择【MapObjects 2.3 Map Control】选项,然后单击【Insert】按钮,系统将生成该控件及其

自动化对象的一系列包裹类(Wrapper Class)。这样在控制条中会增加一个地图控件图标,把

该图标插入到所应用的窗口中就可以了。可以在相应的头文件中创建该类型的对象,利用它就

可以操作地图了。

当然,还要添加矢量的图层数据,添加适量数据需要用到 CMap、CMoDataConnection、

CMoLayers、CMoMapLayer 和 CMoGeoDataset 这 5 个对象。5 个对象间的关系如图 7-27 所示。

矢量数据 CMoDataConnection CMoGeoDataset

CMap CMoLayers CMoMapLayer

图 7-27 对象间的关系

创建数据的连接,可以定义 CMoDataConnection 类的一个对象 conn ,通过

conn.CreateDispatch("MapObjects2.DataConnection")函数来进行身份的匹配,而且需要设置连接

的目录,conn.SetDatabase(path)函数的 path 参数代表了目录的路径。调用 conn.Connect()函数对

数据库进行连接。创建好数据库连接对象后,连接相应的矢量数据集,具体步骤如下。

获得 Map 控件对应的要素层集合对象(CmoLayers)。

CMoLayers layers=m_map.Getlayers();

然后创建矢量数据层对象(CmomapLayer)的对象,并对所创建的矢量数据层对象进行身

份匹配;

CMoMapLayer layer;

if(!layer.CreateDisatch("MapObjects2.MapLayer"))

return "";

创建一个空间数据集对象。

CString LayerName=FileName;FileName表示数据的名称

CmoGeoDataset GeoDataset=conn.FindGeoDataset(LayerName);

if(!GeoDataset)

return "";

把矢量数据与空间数据集对象进行连接,即给矢量数据层挂上相应的空间数据集。

Layer.SetGeoDataset(GeoDataset);

对新添加的矢量数据进行符号化。

CMoSymbol layerSymbol(layer.GetSymbol())

If(color!=-1)

layerSynbol.SetColor(color);

layerSymbol.SetSize(symbolSize);

layerSymbol.Setstyle(symbolStyle);

向要素层集合中添加矢量数据。

Layers.Add(layer);

下面具体介绍地图放大、缩小的实现过程。创建一个工具栏,当然,由于在本系统中不需

Page 37: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

300

要应用到系统默认提供的工具栏,可以修改默认的工具栏。本系统电子地图上面的工具栏就是

利用默认的工具栏来实现的。把鼠标指针放在工具栏的按钮上,把按钮从工具栏上拖走,这样

可以将默认工具栏上的按钮删除。

关于工具栏上按钮的图标可以利用 Visual C++ 6.0 来手动绘制自己想要的图标,也可以通

过添加资源的方式,从外部添加自己喜欢的 ICO 格式的图标资源。双击工具栏上的空白按钮,

为按钮设置 ID。这里设置放大按钮的 ID 为 ID_MAP_ZOOMIN,缩小按钮的 ID 为

ID_MAP_ZOOMOUT,这些都设置完成后,通过类向导为工具栏上的按钮添加消息响应函数。

由于本系统是将 MapObjects 控件放置在视图类中,故选择在视图类中为该控件添加系列消息

响应函数。

注意:由于本系统采用分割视图的方式显示用户界面,也就是说,在程序中有两

个视图,这样当程序运行后就会出现当激活另一个视图的时候工具栏上的按钮是灰色

的。可以在主框架中响应消息,这样就避免了这个问题,当然同时会带来另外一个麻

烦,那就是消息的传递。可以通过获取指针来解决这个问题,本系统是在视图类中实

现消息响应的。

工具栏上放大按钮的消息响应函数代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnMapZoomin()

2 {

3 PlayResource(IDR_WAVE2); //播放声音

4 m_CurrentOperation=ZOOMIN; //当前操作类型

5 m_Map.SetMousePointer(51); //将鼠标设置放大图形

6 }

第 3 行代码实现当单击按钮时会出现声音,其中 IDR_WAVE2 是资源的 ID,添加一个

音频资源将 ID 设置为 IDR_WAVE2。

第 4 行代码判断当前的操作为鼠标按下放大的按钮。

第 5 行代码调用 CMap1 的成员函数来设置光标的形状。

函数 PlayResource()用于播放声音资源文件,当执行该操作的时候会出现提示声,变量

m_CurrentOperation 保存了当前用户的操作,函数 SetMousePointer()将鼠标的形状设置成放大

的图标。工具栏上缩小按钮的消息响应函数代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnMapZoomin()

2 {

3 PlayResource(IDR_WAVE2); //播放声音

4 m_CurrentOperation=ZOOMOUT; //当前操作类型

5 m_Map.SetMousePointer(52); //将鼠标设置缩小图形

6 }

第 3 行代码实现当单击按钮时会出现声音,其中 IDR_WAVE2 是资源的 ID,添加一个

音频资源将 ID 设置为 IDR_WAVE2。

第 4 行代码判断当前的操作为鼠标按下缩小的按钮。

第 5 行代码调用 CMap1 的成员函数来设置光标的形状。

为鼠标添加按下消息响应函数,这里需要注意的是,添加的消息并不是常用的 WM_

Page 38: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

301

MOUSEMOVE 消息,而是为 MapObjects 控件添加鼠标按下消息,如图 7-28 所示。

图 7-28 添加类向导对话框

函数 OnMouseDownMap1()的代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnMouseDownMap1(short Button, short Shift, long X, long Y)

2 {

3 CString str;

4 CMoPoint m_CenterPoint; //缩放的中心点

5 double m_CenterPointX; //定义双精度类型变量

6 double m_CenterPointY;

7 CMoRectangle rect_Extent; //定义矩形范围

8 double m_TrackRectangle_Heigh; //拉框所选范围的高度

9 double m_TrackRectangle_Width; //拉框所选范围的宽度

10 CMoRectangle rect; //拉框范围

11 double X_Scale; //x 方向拉框比例

12 double Y_Scale; //Y方向拉框比例

13 double m_Scale; //比例系数

14 int x=(int)Info.CalcScale(&m_Map); //比例尺

15 str.Format("比例尺 1:%d",x);

16 switch(m_CurrentOperation) //选择语句

17 {

18 case PAN: //漫游

19 m_Map.Pan();

20 break;

21 case ZOOMIN: //放大

22 if(x<7000)

23 {

24 if(!bControlFlag)

25 ShowLayer(); //显示图层

26 MessageBox("已经到最大");

27 break;

28 }

Page 39: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

302

29 rect=m_Map.TrackRectangle();

30 m_TrackRectangle_Heigh=rect.GetHeight()/2;

31 m_TrackRectangle_Width=rect.GetWidth()/2;

32 if(LPDISPATCH(rect)&&m_TrackRectangle_Heigh!=0&&m_TrackRectangle_

Width!=0)

33 {

34 m_Map.SetExtent(rect); //拉框放大

35 }

36 else //定点放大

37 {

38 m_CenterPoint=m_Map.ToMapPoint(float(X),float(Y)); //转换成空间坐标

39 m_CenterPointX=m_CenterPoint.GetX();

40 m_CenterPointY=m_CenterPoint.GetY();

41 rect_Extent=m_Map.GetExtent(); //获得地图显示范围

42 rect_Extent.ScaleRectangle(0.9); //设置放大系数

43 m_Map.SetExtent(rect_Extent); //设置地图显示范围

44 m_Map.CenterAt(m_CenterPointX,m_CenterPointY); //使图形居中

45 }

46 Number++;

47 if(!bControlFlag)

48 {

49 ShowLayer();

50 }

51 break;

第 3~15 行代码为定义辅助的变量并赋初值。

第 18~20 行代码为地图的漫游,关于漫游的具体实现将在下面进行详细讲解。

第 22~51 行代码为地图放大的实现。当用户在地图上单击时,可以获得按下的点的坐

标,然后以该点为中心,通过设置比例系数,来实现地图的缩放。

上述代码通过 switch 语句判断当前用户的操作,调用 Pan()函数实现了地图的漫游。在地

图放大时先调用 TrackRectangle()函数获取地图的区域,调用函数 ScaleRectangle()设置放大系

数,加载图层实现地图的放大。下面介绍地图缩小的实现,代码如下:

52 case ZOOMOUT: //定点缩小

53 if(x>28000)

54 {

55 MessageBox("已经到最小");

56 break;

57 }

58 rect=m_Map.TrackRectangle();

59 rect_Extent=m_Map.GetExtent(); //获得地图显示范围

60 if(LPDISPATCH(rect)&&rect.GetHeight()!=0&&rect.GetWidth()!=0)

61 {

62 m_CenterPoint=rect.GetCenter();

63 m_CenterPointX=m_CenterPoint.GetX();

64 m_CenterPointY=m_CenterPoint.GetY();

65 X_Scale=rect_Extent.GetHeight()/rect.GetHeight(); //设置缩小系数

66 Y_Scale=rect_Extent.GetWidth()/rect.GetWidth();

67 m_Scale=X_Scale;

68 if(X_Scale<Y_Scale)

69 m_Scale=Y_Scale;

70 rect_Extent.ScaleRectangle(m_Scale);

Page 40: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

303

71 }

72 else

73 {

74 m_CenterPoint=m_Map.ToMapPoint(float(X),float(Y));

//转换成空间坐标

75 m_CenterPointX=m_CenterPoint.GetX();

76 m_CenterPointY=m_CenterPoint.GetY();

77 rect_Extent=m_Map.GetExtent(); //设置放大系数

78 rect_Extent.ScaleRectangle(1.111111); //获得地图显示范围

79 }

80 m_Map.SetExtent(rect_Extent); //设置地图显示范围

81 m_Map.CenterAt(m_CenterPointX,m_CenterPointY); //使图形居中

82 Number--;

83 if(!bControlFlag)

84 HideLayer();

85 break;

第 52~85 行代码为地图缩小的实现。获取鼠标按下的坐标后,设置比例系数,通过

SetExtent()函数实现地图缩小。

上述代码为地图的缩小,地图的缩小同地图的放大是类似的,可以通过变量 Number 的递

减完成。下面分别实现了全图、道路属性、地物属性等数据显示,相关代码如下:

86 case FULLSHOW: //全图显示

87 m_Map.SetExtent(m_Map.GetExtent());

88 break;

89 case GetDisP1toP2: //测量两点距离

90 if(m_mouseDownNum==0)

91 m_startPoint=m_Map.ToMapPoint(float(X),float(Y));

92 else

93 m_endPoint=m_Map.ToMapPoint(float(X),float(Y));

94 m_mouseDownNum++;

95 break;

96 case GetAttribute //获取道路属性

97 GetSelectAttribute(X,Y);

98 break;

99 case SELECTPOINT: //获得地物属性

100 GetPointAttribute(strName,X,Y);

101 break;

102 }

103 CMoPoint m_CurrentPoint;

104 if(!m_CurrentPoint.CreateDispatch("MapObjects2.Point"))

//点的地理坐标

105 return;

106 m_CurrentPoint=m_Map.ToMapPoint(float(X),float(Y));

107 CString m_CoordinateStr,m_XCoordinateStr,m_YCoordinateStr;

108 m_XCoordinateStr.Format("%f",m_CurrentPoint.GetX());

109 m_YCoordinateStr.Format("%f",m_CurrentPoint.GetY());

110 m_CoordinateStr="地理坐标:X:"+m_XCoordinateStr+",Y:"+m_YCoordinateStr;

111 CMainFrame*pFrame=(CMainFrame*)AfxGetApp()->m_pMainWnd;

//获取框架窗口指针

112 CStatusBar *pStatus=&pFrame->m_wndStatusBar; //获取状态栏窗口指针

113 if(pStatus)

114 {

Page 41: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

304

115 pStatus->SetPaneText(1,m_CoordinateStr); //在状态栏上输出信息

116 pStatus->SetPaneText(2,str); //在状态栏上输出信息

117 }

118 }

第 86~101 行代码实现了全图、道路属性、地物属性等操作的数据显示。

第 103~117 行代码为将鼠标单击位置的坐标转换成地图的地理坐标显示在状态栏中,

并把比例尺显示在状态栏中。

上述代码中的 case 语句实现用户的其他操作,然后通过 AfxGetApp()->m_pMainWnd 获取

到主框架的指针,通过调用函数 SetPaneText()完成了状态栏中坐标等数据的更新。

注意:switch 括号中的变量为 DWORD 类型是我们在头文件中定义的,判断当前

的操作,而 case 后面的参数为各种操作是在头文件中定义的宏。

7.7.4 测距的实现

当用户在地图上按下两点后,可以获得这两点的屏幕坐标,将该屏幕坐标转换成地理坐标,

再计算这两点间的距离。该功能的实现同样是用上方工具栏中的按钮来完成的。测距按钮的消

息响应是在视图类中添加的,对应函数 OnMeasure 代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnMeasure()

2 {

3 PlayResource(IDR_WAVE2); //播放声音资源

4 m_Map.SetMousePointer(moArrow); //设置鼠标指针形状为箭头

5 m_CurrentOperation=GetDisP1toP2; //设置当前的操作

6 if(!m_startPoint.CreateDispatch("MapObjects2.Point"))//控件支持自动化

7 return ;

8 if(!m_endPoint.CreateDispatch("MapObjects2.Point"))

9 return ;

10 m_mouseDownNum=0;

11 }

第 3 行代码为播放声音资源。

第 4 行代码为设置鼠标指针的形状为箭头。

第 5 行代码为设置当前的操作状态。GetDisP1toP2 表示操作类型为测量距离。

第 6~9 行代码实现控件支持自动化。

要想实现两点间距离的测量还需要一个重要的函数 OnDblClickMap1(),这个函数是

MapObjects 控件的 DblClick 消息响应函数。代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnDblClickMap1()

2 {

3 if(m_CurrentOperation==GetDisP1toP2) //判断当前的操作

4 {

5 double distance; //定义距离变量

6 CString str=""; //定义字符串变量

7 MPoint pt[2];

8 pt[0].x=m_startPoint.GetX(); //获取点的坐标

Page 42: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

305

9 pt[0].y=m_startPoint.GetY();

10 pt[1].x=m_endPoint.GetX();

11 pt[1].y=m_startPoint.GetY();

12 distance=(int)Info.CalcLenght(pt,2); //获取两点的距离

13 str.Format("%.2f",distance);

14 str="两点间距离约为:"+str+"米";

15 CMainFrame *pFrame=(CMainFrame *)AfxGetMainWnd();//获取主框架的指针

16 CAttribute *pView=(CAttribute *)(pFrame->m_wndSplitter2.GetPane(1,0));

17 pView->m_strAttribute=str;

18 pView->UpdateData(false);

19 m_mouseDownNum=0;

20 m_startPoint.ReleaseDispatch(); //释放

21 m_endPoint.ReleaseDispatch();

22 }

23 }

第 8~11 行代码为获取起点和终点的位置。

第 12~14 行代码为获取两点间的距离,并将距离格式化到字符串中。

第 15~18 行代码为将距离显示在状态栏中。

上述代码首先判断当前用户的操作是否为测距,pt 数组保存了鼠标在两个点的坐标,当双

击时,就会在右侧的编辑框中显示这两个点的距离。

7.7.5 地图漫游的实现

地图漫游的实现主要是通过 CMap1 类中的成员函数 Pan()来实现的,执行漫游操作的按钮

同样是工具栏中上的按钮,视图类中漫游按钮的消息响应函数 OnMapPan()代码如下:

代码位置:见光盘中本章源代码的 CMapPublicView 类。

1 void CMapPublicView::OnMapPan()

2 {

3 PlayResource(IDR_WAVE2); //播放声音资源

4 m_CurrentOperation=PAN; //设置当前操作状态

5 m_Map.SetMousePointer(53); //鼠标指针设置成漫游图形

6 }

第 3 行代码为播放声音资源。

第 4 行代码为设置当前的操作状态。PAN 表示当前操作类型为地图漫游,就是实现地

图的平移操作。

第 5 行代码为设置鼠标指针的形状为漫游形状。

地图漫游的实现还需要一个重要的函数——地图控件的鼠标消息响应函数 MouseDown,

该函数在介绍地图的放大、缩小时已经介绍过了,这里不做重复介绍。

到此,系统的开发过程已经介绍完毕,由于本章篇幅的限制,关于 MapObjects 控件的

使用还有很多没有进行介绍,如地图图层的加载和使用等。注意,MapObjects 地图的显示

是由多个透明的图层组成的,在添加 MapObjects 控件后应该在工程的目录下将图层文件添

加进来。

7.8 系统部署和运行

到这里关于本系统的开发过程已经进行了较详细的介绍,其他一些细节问题的解决办法,

Page 43: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

306

读者可以参考光盘中本章对应的源程序。开发出的公交信息查询系统要正确运行还需要做好几

方面的工作:系统安装配置、数据库和数据表的建立、用户参数定制等。下面将介绍这几个方

面的设置过程。

7.8.1 系统安装配置

本系统主要为杭州市民的出行提供方便,是一个高效的智能工具。在使用时,只需根据数

据库名称,把数据库连接语句稍微修改即可。运行需求如下:

用户界面:本系统采用 Microsoft Visual Studio 设计,用户交互界面采用的是基于

Windows 的窗口界面。

硬件接口:运行本系统的基本硬件是 CPU 为 Intel P3 及以上;内存为 256MB 及以上;

硬盘为 10GB 及以上。

软件接口:数据库服务器为 Microsoft SQL Server 2005 及以上版本。

7.8.2 数据库建立及用户参数定制

本系统在运行时需要 7 个表,故在运行前需完成数据库、数据表的创建。推荐使用查询分

析器执行 SQL 语句,所有数据库、数据表的创建语句都保存在光盘中本章源代码中。当然还

可以通过光盘中的数据库备份文件来直接还原数据库。

系统在开始运行时需要提供一些初始信息,如账户信息、权限设置、服务器设置等。本系统提

供管理员账号为 admin,密码为 1。当然,根据需要可以通过用户管理界面添加修改用户信息。

7.9 开发知识点总结

到本节为止,该系统的开发已全部介绍完毕,在开发过程中用到了许多开发技巧及重要知

识点,下面将对这些技巧及知识点进行简单介绍。

状态栏中信息的显示:很多程序都会在状态栏中显示信息,如用户登录、时间等,其

实这是很容易实现的。在主框架类中有这样的一个结构数组 static UINT indicators[],可

以按照自己想要在工具栏中的布局来添加或者删除一些字符串。之后调用

m_wndStatusBar.SetPaneText(n, str, TRUE)函数就可以实现状态栏信息的显示,其中 n

为状态栏中窗格的索引,str 为要显示的字符串。

分割视图的实现:通过分割视图可以把界面按自己的需要合理布局。各个功能模块之

间既可以独立处理消息又能互相传递数据。本系统实现静态分割,主要是通过添加

CMainFrame::OnCreateClient()消息函数实现的。

动态的应用程序图标:先把需要不断变化的几幅图标资源添加进来。在主框架的头文

件中定义一个图标句柄的数组 HICON m_hIcons[3]来保存图标的句柄。再在 OnCreate()

函数中加载图标。用函数 LoadIcon()完成图标的加载。其中,参数 MAKEINTRESOURCE

是一个宏,其参数为资源的 ID。最后,在 OnCreate()函数中设置一个定时器 SetTimer(),

通过类向导添加一个定时器,在OnTimer中通过 SetClassLong()来改变应用程序的图标。

这样,不断变化的图标就完成了。

MapObjects 控件的使用:MapObjects 同其他的 ActiveX 控件使用基本相同,先安装

MapObjects控件。安装成功后MapObjects就注册成功了。选择【Project】|【Add To Project】

|【Components and Controls...】命令,打开【Components and Controls Gallery】对话框。

从其【Registered ActiveX Controls】文件夹下的已登记控件列表中选择【MapObjects 2.3

Map Control】选项,然后单击【Insert】按钮,系统将生成该控件及其自动化对象的一

Page 44: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

307

系列包裹类(Wrapper Class)。这样在控制条中会增加一个地图控件图标,把该图标插

入到所应用的窗口中就可以了。可以在相应的头文件中创建该类型的对象,利用它就

可以操作地图了。

Page 45: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

308

7.10 章末总结

本章比较详细地介绍了一个具有完善功能的公交查询系统的开发。在开发的过程中当然会

遇到许多困难,要学会自己去解决这些困难,MSDN 永远是最好的帮手。另外,在当今这个网

络时代,互联网给人们带来很大的便利,应该学会利用网络中的资源。本系统基本达到了实现

城市信息查询功能的要求,但系统有一定的局限性,这也是以后要解决的问题。为了使用户使

用更方便,应添加最优路线搜索的功能,也就是当用户在地图上任意选择两点,则系统可以给

出这两点间的最优路径。因为系统的最终用户是广大市民,因此应该充分利用现代化的传输手

段,给广大市民提供方便。但考虑到传统的 C/S 模式存在着客户端部署的问题,B/S 结构存在

着数据交换大容量问题,因此可以采用一种新的接入模式——A/S。

7.11 光盘示例使用指导

本章讲解的公交信息查询系统和前面各章的数据库系统一个最大的不同点是使用了

MapObjects 控件,读者在使用前必须先安装该控件,这样才能顺利使用本章源程序。本章系统

的源程序及数据库文件在光盘中也可以找到。下面将详细介绍 MapObjects 控件的安装、破解操

作过程及容易出现的一些问题和解决办法。

7.11.1 MapObjects 控件安装

读者在运行本章系统前务必要安装 MapObjects 控件,下面给大家讲述安装该控件的过程。

打开光盘中本章文件夹,能看见 MO2.3 文件夹。打开该文件夹可以找到 MapObjects

控件的安装程序,如图 7-29 所示。

图 7-29 MapObjects 控件安装程序及破解文件

双击 Mo23Eval_Only.exe 运行安装程序,显示安装 MapObjects 控件的界面,如图 7-30

所示。

Page 46: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

第 7 章 公交信息查询系统

309

图 7-30 MapObjects 控件安装过程界面

成功安装 MapObjects 控件后,运行本章系统,在主框架显示之前会弹出 MapObjects

控件同意协议的界面,如图 7-31 所示。单击【OK】按钮就可以使用该控件了。

图 7-31 MapObjects 控件同意协议的界面

7.11.2 系统运行相关问题说明

下面给出本系统运行中容易出现的错误界面,并帮助读者快速找到错误的解决办法。

打开本章源程序主对话框界面时,弹出“Active X 控件不能初始化”信息,界面

如图 7-32 所示。单击【确定】按钮后,弹出“Active X 控件没注册”信息,界面如图 7-33

所示。

图 7-32 Active X 控件不能初始化界面

Page 47: 公交信息查询系统 - images.china-pub.comimages.china-pub.com/ebook195001-200000/199072/ch07.pdf · 完整的智能公交信息查询系统。读者通过本章的学习,将会对开发基于Visual

亮剑 Visual C++项目开发案例导航

310

图 7-33 Active X 控件没注册

解决办法:主要是没有安装 MapObjects 控件引起的,按照 7.11.1 节中的 MapObjects 控件

安装即可。

运行本章源程序后,弹出“Debug Assertion Failed”错误信息,界面如图 7-34 所示。

图 7-34 Debug Assertion Failed 错误信息界面

解决办法:根据该错误对应的 File 为“dlgdata.cpp”可以知道,该错误主要是因为主对话

框上的控件引起的,也就是没有安装 MapObjects 控件。按照 7.11.1 节中的 MapObjects 控件安

装即可。

运行本章系统后,公交信息维护模块默认是禁用的,用户必须登录后才能使用,登录

可通过选择【维护】|【用户登录】命令弹出登录界面,如图 7-35 所示。用户名输入 admin,密

码为 1 可登录成功。

图 7-35 用户登录界面