手机端
or

欢迎您加入我爱方案网QQ群

1.智能产品外包服务群(311606115)
2.嵌入式项目开发群(491609563)

基于Qt的电子海图研究与实现(下)

尹加豹,蒋健| Qt,电子海图| 2012-11-27
362 收藏
分享到: 
每日精选
热门文章
热门方案
【导读】本文是在电子海图显示与信息系统应用越来越广泛的趋势下,结合中国船舶工业集团船舶系统工程部研制的某型号电子海图系统功能尚需完善的实际情况,在Linux系统下,对基于Qt的ECDIS,特别是航线设计、航行监控及海图显示等方面进行设计与实现。

相关阅读:基于Qt的电子海图研究与实现(上)

电子海图坐标系概念的说明

参心坐标系是以参考椭球的几何中心为原点的大地坐标系。通常分为:参心空间直角坐标系(以x,y,z为坐标元素)和参心大地坐标系(以B,L,H为坐标元素)。

参心空间直角坐标系是在参考椭球内建立的O-XYZ坐标系。原点O为参考椭球的几何中心,X轴是赤道面与本初子午面的交线,向东为正;Z轴是旋转椭球的短 轴,向北为正;Y轴则与XZ平面垂直构成右手关系。参心大地坐标系简称大地坐标,用大地经度L、大地纬度B和大地高度H表示,这种坐标系是大地测量的通用 坐标系。在我国有1954年北京坐标系(BJZ54)和1980年国家大地坐标系(GDZ80),本电子海图采用的是1954年北京坐标系,其值用 (φ,λ)(纬度、经度)表示。

平面直角坐标系简称平面坐标,在绘制海图时,需要把矢量海图上的大地坐标(φ,λ)通过墨卡托投影方式转换为平面坐标。在电子海图系统中涉及的坐标变换算 法包括墨卡托投影正变换和墨卡托投影逆变换,正变换是把BJZ54大地坐标(φ,λ)转换为平面坐标;逆变换则是将平面直角坐标转化为对应的大地坐标经。

屏幕坐标是导航系统屏幕上显示的坐标,电子海图数据文件中包含平面坐标值以及平面坐标到屏幕坐标的正逆变换。在导航过程中,电子海图系统会定时接收定位设备传输的的经纬度值(φ,λ),然后转换为中间变量平面坐标,最终转换成不同显示比例尺下的屏幕坐标;同时,用户在指定海图中某点位置时,需要将屏幕坐标转换为大地坐标的经纬度值(φ,λ)。

墨卡托投影正变换

墨卡托投影正变换是在绘制海图时,把大地坐标系中的经纬度值(φ,λ)转换为平面直角坐标系中的( , )。其变换公式为:



上式(1)中,
x,y 表示坐标值(墨卡托直角坐标系),
q表示等量纬度,



λ表示经度(大地坐标系);
表示基准纬度的纬度圈半径,



在式(2)和式(3)中:
表示基准纬度外椭球的卯酉圈曲率半径



表示墨卡托投影的基准纬度,
a 表示地球椭球长半径,
e 表示椭球体第一偏心率。
计算可得到(x,y)值。在实际绘制海图时,设坐标原点为(),考虑到最后的换算单位为厘米(cm)以及海图比例尺(S),由此可得最后的正变换公式为:



这样,绘制海图的平面直角坐标(  )就得到了。
[member]
海图数据文件解析

电子海图数据文件按结构结构分为三部分:头区、索引区和数据区,这种结构决定了海图显示时获取数据的接口方式。

1、头区

数据文件的系统头区在起始部分,包含以下信息:图号、投影类型、大地坐标系说明、索引区首地址、数据区末地址、属性标识等,这是海图文件的总体控制信息。具体结构如下:

        typedef    struct{
            char  num[16];    /* 图号 */
            char  name[32] ;   /* 图名 */
            char  name_[32] ;  /* 副图名 */
            short  type;       /* 投影类型 */
            short  zone_num;  /* 带号 */
            short  ctrl_size;      /* 控制点个数 */
            char     ctrl _name[6][8] ; /*       名称 */
            LP2     ctrl _xy[6] ;     /*       坐标 */
            double scale ;      /* 海图比例尺 */
            float     xy_unit ;     /* 横坐标单位(基准为0.001mm) */
            float     z_unit ;      /* 垂坐标单位(基准为0. 1M) */
            char     creat_t [26] ;    /* 海图制作时间 */
            char     update_t [26] ;  /* 海图最近修改时间 */
            char     survey_t [32] ;  /* 海图测量时间 */
            char     geo_data[20] ;  /* 大地坐标系说明 */
            char     high_data[32] ;    /* 高程基准说明 */
            char     version[20] ;      /* 版本号 */
            char     survey_u[40] ;    /* 制作单位 */
            char     surveyer[20] ;     /* 制作人 */
            unsigned    long    index_start ;  /* 索引区首地址 */
            unsigned long data_start ;   /* 数据区首地址 */
            unsigned Iong data_end ;   /* 数据区末地址 */
            unsigned    long file_end ;   /* 海图文件尾地址 */
            short  max_ seq ;        /* 要素总数 */
            short  min_distance ;     /* 线性内插法最小距离 */
            short  max_ distance ;    /* 线性内插法最大距离 */
            short  target_radius ;     /* 目标识别半径 */
            short  ver_ distance ;     /* 到目标垂距 */
            short  marigin ;     /* 边缘值 */
            double  l0_lat0 ;     /* 中央经度或基准纬度(弧度)  */
            double  l0_lon1 ;     /* 标准纬度一(弧度)  */
            double  l0_lon2;     /* 标准纬度二(弧度)  */
            short   atr_flag;     /* 海图特征 */
        }HEAD;

2、索引区

索引区用于数据检索,它包含了该幅海图中所有要素的相关检索数据。每种要素都对应一条(或一条以上)索引,具体数目由该要素的数量决定。例如,某幅海图共有30个危险沉船(要素),那么这幅海图的索引区就有30个沉船(要素)索引。

索引区为每个要素定义了48个字节的查询分类标记,另外还包括属性信息,如要素序号、类型、层号、特征码、拓扑关系、数据地址及长度、检索位图等。

具体数据结构如下:

        typedef    struct
        {
            short  seq;          /* 要素序号 */
            unsigned    char    type;   /* 类型 */
            unsigned    char    ly;     /* 层号 */
            unsigned    short fc;    /* 特征码 */
            short  fm;          /* 辅助码 */
            short  alterable[15] ;  /* 可变字段 */
            unsigned    long    data-pt;  /* 数据区指针 */
            unsigned    short length;  /* 长度 */
            unsigned    long    bitmap;  /* 检索位图 */
        }Index;

空间要素分为7类,为使各个要素占据的内存空间相同,数据结构采用了联合定义,结构如下:

        typedef    union{
            P2LINEI    p21; /* 二维线 */
            P21     p2;     /* 二维点 */
            P3I      p3;     /* 三维点 */
            POLYS ply;   /* 多边形 */
            TEXT  text;   /* 文本注记 */
            EDGE  edge;  /* 边界 */
            NODE  rid;   /* 结点 */
        }UN;

3、数据区

数据区的存放所有的要素信息,每个要素占用的范围由索引区中相应的数据标示,数据区存放的属性信息包括拓扑信息、几何信息、文本字符串等。有些类型要素的坐标值已在索引区中给出了,如节点、二维点、三维点等,因此在数据区不存放这些要素的坐标值;而有些类型的要素,要从索引中给出的地址开始,在数据区中按规定的方式存放坐标值信息,如二维线,要按顺序存放各折点的绝对坐标值。
数据区基本的数据结构如下
       
        typedef    struct
        {
            int  mn;    /* 点元编码 */
            char    mx;    /* 源点横轴 */
            char    my;    /* 源点纵轴 */
            char    mw;    /* 宽度 */
            char    mh;    /* 高度 */
            int  flag;    /* 绘图标记 */
            int    pn;    /* 点数 */
            char    x1;    /* 坐标 */
            char    y1;    /* 坐标 */
            char    x2;    /* 坐标 */
            char    y2;    /* 坐标 */
        }Item;

综上所述,海图数据文件由头区、索引区、数据区三部分组成,在打开一幅海图时,要先读取头区信息,之后从序号中找到索引,并判断该要素是否是要找的那个,如果是就按照该索引找到实体数据进行绘图,每个海图要素在索引区都有唯一一个指向数据区的指针。

 
海图图形显示过程

在海图数据文件中,存储着各显示要素(地理实体)的空间数据信息。各要素的数据结构如前文所述,其中位置坐标信息是按照数据文件逻辑坐标存储的。绘制地图的基本过程如下:

1、从数据文件中读出海图控制信息,包括地图比例尺、图幅范围、控制点地理坐标、投影方式等,并获得地理坐标与图形要素逻辑坐标的对应关系。

2、从数据文件中读取图层分层控制信息,以便电子地图可以分层显示。

3、从数据文件中读出实体要素的结构数据,将数据文件逻辑坐标转换为屏幕坐标,并根据该类实体要素对应的符号要素编码绘制图形符号。

计算机屏幕坐标的单位是像素,在显示地图时,无需将数据文件的一个逻辑坐标单位距离转换为屏幕坐标的一个像素距离,而是计算出这两种坐标系单位距离的转换比例,由此可确定转换关系。由于计算机屏幕尺寸有限,显示的电子地图部分往往只是总海图的一个局部区域。如图4.1所示:

逻辑坐标和屏幕坐标转换关系
图4、1 逻辑坐标和屏幕坐标转换关系

 
在转化过程中,需要注意计算机屏幕的坐标原点在左上角,而文件逻辑坐标的原点在左下角。当截取地图的一部分显示时, 要将截取部分的左上角映射到屏幕坐标的左上角,之后再按两个坐标系的转化关系,完成电子地图中具体图元要素的绘制工作。

航线设计模块实现

计划航线设计


1、新建航线

航海需要预先设定航行路线。在航行前,通常用比例尺较小的海区总图作计划航线,然后将此计划航线转标到较大比例尺的航行图上,并检查此计划航线设置是否合适。因为海区总图内容较简单,作计划时难免有不合适的地方。转到航行图上后,就可发现计划航线设置中的不足之处,例如离航行障碍物太近,或者还有更近的航线可以利用等。这时就在航行图上修正计划航线。

通过本电子海图的新建航线功能可以制定一条新的计划航线。航线由航路点组成,用户可以通过鼠标点取、输入经纬度、参照已知点或加载标记点等方式建立新的航路点,航路点建立完成后可以保存为一条新的航线,新航线所包含的航路点会被按顺序存储到数据库中。在航行开始后,系统会加载相应的航路点生成航线并显示。

2、航路点库

在设计航线时,需要输入大量的航路点。虽然可以通过鼠标点取的方式定位,但鼠标点取存在误差较大的弊端;如果通过经纬度值来定位,则需要繁琐的录入工作。为提高设计航线的效率,本电子海图系统提供了航路点库功能。用户可以将常用的航路点存到航路点库中,并为其命名。在设计航线时,可以将其从数据库中调出,这样可以快速高效的制定出计划航线。

航路点库使用方便,且其数据还可以导出保存为文件形式,导出的文件不仅可以作为本系统的备份数据,还可用于其它电子海图系统,用户只需一次输入就可永久使用,极大的简化了电子海图系统的维护工作。

3、航线、航路点数据结构

由于航线是以一系列航路点的形式存储的,所以航线的属性存储在航路点的数据结构中。航路点类及航线类数据结构如下:

        class CrouteNode : public QGraphicsItem  /* 航路点类结构 */
        {
        public:
            virtual ~CRouteNode();
            CRouteNode();
            void InitGui();      /* 绘制航路点初始化 */
            void PrepareSignalProcess();     /* 连接信号槽机制 */
            void AddRouteEdge(CRouteEdge* routeEdge);    /* 添加航线 */
            void DelRouteEdge(CRouteEdge* routeEdge);
            void Adjust(double lon = 0.0, double lat = 0.0);    /* 调整航路点位置 */
            QRectF boundingRect() const;    /* 重绘区域 */
            QPainterPath shape() const;    /* 碰撞检测区域 */
            void paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget);
        protected:
            virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
            virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* event);
            virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
            virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
            virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent* event);
        private:
            double    lon_;    /* 经度 */
            double    lat_;        /* 纬度 */
            double    shipSpeed;    /* 预设速度 */
            Qstring    routeType;    /* 航线类型 */
            int        turnRadius;   /* 转向半径 */
            const int    itemRadius_;  /* 显示半径 */
            QList<CRouteEdge*>    lstRouteEdge_;  /* 航线列表 */
        }
       
        class CRouteEdge : public QGraphicsItem   /* 航线类结构 */
        {
        public:
            CRouteEdge(CRouteNode* prevNode, CRouteNode* nextNode);
            void    InitGui();  /* 绘制航线初始化 */
            void Adjust();  /* 调整航线位置 */
            QRectF boundingRect() const;
            QPainterPath shape() const;
            void paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget);
        protected:
            virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);
        private:
            CRouteNode *prevNode_;   /* 航线的前一个航路点 */
            CRouteNode *nextNode_;   /* 航线的后一个航路点 */
        };

从上述定义可知,航路点与航线类都继承自QGraphicsItem。在Qt中,QGraphicsItem是一种图形化的显示项;QGraphicsScene是QGraphicsItem的画布,它可以把众多的QGraphicsItem整合在一起;QGraphView则是QGraphicsScene显示层,可以完成QGraphicsItem项的显示。

继承自QGraphicsItem 的类都可以根据QGraphicsScene这样的顶层元素来定位,并在QgraphView中显示,QGraphicsItem只提供了最基本的项元素的显示能力,用可可以通过继承QgraphicsItem类,来建立自己的个性化的显示项。

QgraphicsItem的部分函数功能如下:

QRectF boundingRect() const;这个函数返回一个矩形,该矩形定义了项的的外部边界,所有的绘图工作都限定在这个边界矩形之内, QGraphicsView通过该矩形判断该项是否需要重绘。

QPainterPath shape() const;该函数返回项的形状(以本地坐标QPainterPath表示),项会自动处理所有的碰撞检测。

void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);QGraphicsView会调用此函数绘制当前坐标系内的所有项。

QVariant itemChange(GraphicsItemChange change,const QVariant &value);该函数用于自定义自定义项元素。当项元素发生变化时,该函数会被用。重新实现函数可对项的状态变化做出反应。

4、航路点、航线的绘制

在海图上绘制航路点及航线步骤如下:

1)根据各航路点的经纬度分别计算航路点的屏幕坐标;
2)在相应的的屏幕坐标上绘制航路点;
3)根据当前的航路点绘制航线
4)计算航线的航向、航程等并显示在航线两侧。
当用户拖动或缩放海图时,程序会遍历当前QgraphicsView中的所有项元素,其中的航路点、航线项元素会调用自身的Qvariant itemChange (GraphicsItemChange change,const QVariant &value)函数,完成该项元素的重绘。

航程、航向计算


对于一条航线,根据起航点和到达点的经纬度可以求取航向和航程,在墨卡托海图上航线均为直线,即恒向线,但在地球表面上航线为球面螺旋曲线。计算球面螺旋曲线的长度及方向方法有多种,以地球圆球体为基础的平均纬度航法较为简单,但这种方法不适用于跨赤道的航线,此外经度也较低。本电子海图系统采用的是将地球作为椭圆体的墨卡托航法,虽然这种算法较为复杂,但其计算精度较高,也可用于计算跨赤道的航线计算。


更多内容,请点击下载:

技术论文:基于Qt的电子海图研究与实现(上)
http://www.52solution.com/data/datainfo/id/6766
技术论文:基于Qt的电子海图研究与实现(下)
http://www.52solution.com/data/datainfo/id/6767



点赞

深圳市中电网络技术有限公司 Copyright© www.52solution.com 粤ICP备10202284号