您的位置:首页 > 其它

基于Wms协议的Map开发(六:通讯模块)

2010-05-06 15:11 357 查看
声明:此系列文章,不是说教,不是告诉任何人如何利用
C++
开发电子地图的教程,而且本人水平有限,也达不到教程的地步。只是利用此系列的文章,来记录开发基于
C++
应用在
Windows

开发电子地图的过程。愿对
C++
开发感兴趣的朋友,熟知
Gis
开发的朋友,了解
Wms

WFS
协议的朋友,亦或是对
GoogleMap
之类感兴趣的朋友,共同讨论。(废话到此结束)。



通讯模块

此通讯模块目的在于根据输入,下载指定
URL
的内容。由于本人很少从事网络方面的编码,这部分很不擅长,当初,设计时,欲寻找一套简单易用的库,所以选择了
WinInet
实现此部分逻辑,但是随着知识的增长,了解到
WinINet
是极不稳定的,所以对网络编程熟悉的朋友可自行实现此部分功能。例如:可以使用
Socket
来完成此功能。



重中之重

GetCapability

bool
BStarWmsPostMan
::SaveCapFile
(char
* strAddr

,char
buf
[], int
& buflen
)

{


char
* temp
= "SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities"
;


char
url
[512];


memcpy_s
(url
,512, strAddr
,256);


strcat_s
(url
,512,temp
);


if
(!ReciveBuffer
(url
,buf
, buflen
))


{


delete
[] buf
;


buf
= NULL
;


return
false
;


}

}

解释:

strAddr
:是你服务器的地址字符串

char
buf
[]
:接收到的字符串。

int
& buflen

:接收到的字符串的长度。



GetMap

我给出一个如何拼接
GetMap
命令的函数,主要目的是让大家了解
GetMap
字符串如何拼接的,这样有利于大家自己实现下载功能。

bool
BStarWmsPostMan
::GetMap
(LATLONBB
maparea
, char
buf
[], int
& buflen
)

{


char
strTemp
[1280];;


char
url
[2560];


memcpy_s
(url
,2560,m_strAddr
,256);


sprintf_s
(strTemp
,1280,"BBOX=%f,%f,%f,%f&SRS=EPSG:%s&WIDTH=%d&HEIGHT=%d&LAYERS=%s&STYLES=&FORMAT=%s&DPI=96"


,maparea
.minx
,maparea
.miny


,maparea
.maxx
,maparea
.maxy


,m_layerProperty
.Srs


,m_layerProperty
.Width
,m_layerProperty
.Height


,m_layerProperty
.Name


,m_layerProperty
.ImageType
);


strcat_s
(url
,2560, "SERVICE=WMS&version=1.0.0&request=getmap&"
);


strcat_s
(url
, 2560, strTemp
);


if
(!ReciveBuffer
(url
,buf
,buflen
))


{


return
false
;


}


return
true
;

}

解释:

LATLONBB
maparea

:这个数据结构大家看过前文的,应该记得,由四个double
组成,代表左右上下四个边界的经纬度值。

char
buf
[]
:接收到的字符串。

int
& buflen

:接收到的字符串的长度。



如果你看明白了,而且想做个稳定的通讯模块,好的,就别再往下看了,自己实现吧。主要工作就是用你自己的方法实现ReciveBuffer()
这个函数。

模块功能

先让我们来看看此功能模块是做什么用的,有哪些事情是要处理的。

流程图如下:



再让我们用文字描述一下这个流程:

明确任务

首先明确,通讯模块需要得到什么样的数据呢?

(1)

还记得
Wms
协议中的
GetCapability
吗?对了,得到服务器属性的功能,我们需要通过
GetCapability
来得到一个关于当前地图服务器描述的
XML
表。从而得到很多我们需要的信息,例如当前服务器上存在的地图图层有哪一些,每个图层的经纬度范围是什么,每个图层的名称是什么,投影系是什么等等。

(2)

重点当然是我们要通过
GetMap
来获取我们指定图层、指定坐标范围内的图片了。此模块当然还要能下载这些指定的图片。

其次,我们通过
GetCapability
得到的关于服务器属性的
XML
描述,要经过分析得到我们关心的数据集合了。我这里采用了一个轻量级的
XML
库来做到这一点。选择了
TinyXML
(上网看一下关于此的简介吧)。由于我希望透视
XML
分析过程,所以这里没有采用他的库文件,而是直接使用了他的源代码(
TinyXML
是开源的)。经过分析、确认的数据会被我存到我定义好的数据结构中。

再回首

明确了功能后,回过头来,再来看一下流程图



1

其中“设置服务器”是一个拼接命令给
GetCapability
作为输入的过程。简单的说来,比如你的服务器地址是
http://wms.lizardtech.com/lizardtech/iserv/ows

如果你使用的是一个
1.1.1
版本的
WMS
服务器

那么如果加上“
?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
”这个字符串之后,就相当于设置完成
GetCapability
命令的拼接。只需要打开“
http://wms.lizardtech.com/lizardtech/iserv/ows
?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
”这个
URL
,那么你就会得到一个
XML
文件。


2
)接下来,“获取服务器属性”以及“得到服务器相关属性”的两个步骤,就是我要通过
TinyXML
分析这个
XML
文件,并将我们关心的数据存入我们自定义的数据结构中。


3

随后,“工作对象的相关属性”,应为我们是要针对对象开发的,意思就是我可以建立多个独立的地图对象,所以,每个独立的地图对象要拥有各自独立的通讯模
块,所以,通讯模块的实现要通过建立类,用类的实例来实现,所以工作对象的属性,就是指每个类实例中的一些私有化成员变量将得到赋值。


4
)最后,每个通讯对象的实例,要根据自己的输入去下载数据到本地保存,我的是现实是用
WinINet
,强烈建议大家用
socket
实现,这里要说的是,
XML
下载每个通讯实例只需要完成一次,而图片的下载要完成多次。(这里是根据上层模块的多线程安排下载的,之后会在文件模块的说明中介绍)。


5

补充说明,这里有个问题,不知道大家注意到没有,下载文件的过程相对于其他操作是耗时的工作,如果用户没耐心看完当前的地图,或者不关心当前地图,从而又
开始了新的操作,我们需要一种中断机制,让正在下载的工作停止。这是很重要的,个人在工作中感觉到,好的功能代码要做到全代码调用的可控。相当于开线程,
不能让线程成为脱缰野马的道理一样。



模块接口及数据结构

数据结构

//
经纬度范围

typedef
struct
tagLATLONBB


{


double
minx
;
//

最小x


double
miny
;
//

最小y


double
maxx
;
//

最大x


double
maxy
;
//

最大y

}LATLONBB
, *PLATLONBB
;

#endif



//wms
标准协议中包含数据

typedef
struct
tagCAPABILITYDATAS


{


int

Width
;
//

图片像素宽度


int

Height
;
//

图片像素高度


char

ImageType
[BUFFER_SIZE_COMMON
];
//

图片类型


char

Srs
[BUFFER_SIZE_COMMON
];
//

投影类型


char

Name
[BUFFER_SIZE_SPECIAL
];
//

层名(用于拼接)


char

Title
[BUFFER_SIZE_SPECIAL
];
//

层名(用于显示)


LATLONBB
BoundBox
;
//

层范围(该层最大范围)

}CAPABILITYDATAS
,*PCAPABILITYDATAS
;



对外接口

//
登录服务器

DLL_API
int

LogIn
(const
char
* strAddr
);

// 2
次调用
,1
次确定buflen
,2
次真正的操作

DLL_API
bool

GetCapability
(int
index
, CAPABILITYDATAS
itemsName
[], int
& count
);

//
设置
用户关心的层

DLL_API
LATLONBB
SetCapability
(int
index
, CAPABILITYDATAS
itemsName
[],const
int
count
);

//
获取图层2
次获取
,1
次确定buflen
,2
次真正的操作

DLL_API
bool

GetMap
(int
index
, LATLONBB
maparea
, char
buf
[], int
& buflen
);

//
中断操作
,舍弃当前下载的工作

DLL_API
void

AbortMap
(int
index
, BOOL
IsAbort
);

//
登出服务器

DLL_API
void

LogOut
(int
index
);

//
复制服务器信息

DLL_API
void

CopyServerData
(int
indexSrs
, int
indexDes
);





TinyXML

我们看一下针对我们的应用,TinyXML
的使用。(这只是个建议,可以使用其他XML
操作的库),其实使用起来还是很简单的,至少是读取很简单吧。

bool
BStarWmsPostMan
::GetCapItems
(CAPABILITYDATAS
items
[], int
& count
)

{


LATLONBB
tempLatlon
;


TiXmlDocument
* myDocument
= new
TiXmlDocument
();


BOOL
bHaveLatlon
= FALSE
;


char
filename
[64];


sprintf_s
(filename
,"%s-%d"
,"GetCapability"
,m_Index
);


if
(!myDocument
->LoadFile
(filename
,TIXML_ENCODING_UNKNOWN
))


{


return
FALSE
;


}


TiXmlElement
* rootElement
= myDocument
->RootElement
();//class


TiXmlElement
* CapabilityElement
= rootElement
->FirstChildElement
("Capability"
);


TiXmlElement
* LayersElement
= CapabilityElement
->FirstChildElement
("Layer"
);


TiXmlElement
* allName
= LayersElement
->FirstChildElement
("Name"
);


TiXmlElement
* LayersTitleElement
= LayersElement
->FirstChildElement
("Title"
);


TiXmlElement
* LayerElement
= LayersElement
->FirstChildElement
("Layer"
);


TiXmlElement
* LayerTitleElement
=
LayerElement
->FirstChildElement
("Title"
);


while
(LayerElement
)


{


indextemp
++;


TiXmlElement
* LayerSrsElement
= LayerElement
->FirstChildElement
("SRS"
);


TiXmlElement
* LayerTitleElement
=
LayerElement
->FirstChildElement
("Title"
);


TiXmlElement
* LayerNameElement

=
LayerElement
->FirstChildElement
("Name"
);


TiXmlElement
* LayerLatLonBoundingBox
= LayerElement
->FirstChildElement
("LatLonBoundingBox"
);


TiXmlElement
* LayerBoundingBox
= LayerElement
->FirstChildElement
("BoundingBox"
);


TiXmlElement
* LayerSrs
= LayerElement
->FirstChildElement
("SRS"
);


TiXmlAttribute
* RectofLayerLatLonBoundingBox
= LayerLatLonBoundingBox
->FirstAttribute
();


Datas
[indextemp
].BoundBox
.minx
= atof
(RectofLayerLatLonBoundingBox
->Value
());


RectofLayerLatLonBoundingBox
=RectofLayerLatLonBoundingBox
->Next
();


Datas
[indextemp
].BoundBox
.miny
= atof
(RectofLayerLatLonBoundingBox
->Value
());


RectofLayerLatLonBoundingBox
=RectofLayerLatLonBoundingBox
->Next
();


Datas
[indextemp
].BoundBox
.maxx
= atof
(RectofLayerLatLonBoundingBox
->Value
());


RectofLayerLatLonBoundingBox
=RectofLayerLatLonBoundingBox
->Next
();


Datas
[indextemp
].BoundBox
.maxy
= atof
(RectofLayerLatLonBoundingBox
->Value
());


LATLONBB
tempsort
;


tempsort
= Datas
[indextemp
].BoundBox
;


if
(tempsort
.minx
> tempsort
.maxx
)


{


Datas
[indextemp
].BoundBox
.minx
= tempsort
.maxx
;


Datas
[indextemp
].BoundBox
.maxx
= tempsort
.minx
;


}


if
(tempsort
.miny
> tempsort
.maxy
)


{


Datas
[indextemp
].BoundBox
.miny
= tempsort
.maxy
;


Datas
[indextemp
].BoundBox
.maxy
= tempsort
.miny
;


}


memcpy
(Datas
[indextemp
].Name
,(char
*)LayerNameElement
->GetText
(),128);


memcpy
(Datas
[indextemp
].Srs
, "4326"
,sizeof
( "4326"
));


memcpy_s
(Datas
[indextemp
].Title
, BUFFER_SIZE_SPECIAL
,(char
*) LayerTitleElement
->GetText
(),128);


memcpy_s
(Datas
[indextemp
].ImageType
, BUFFER_SIZE_COMMON
, "image/jpeg"
,sizeof
("image/jpeg"
));


Datas
[indextemp
].Width
= 256;


Datas
[indextemp
].Height
= 256;


if
(items
!= NULL
)


{


memcpy_s
(items
[indextemp
].Name
,BUFFER_SIZE_SPECIAL
,Datas
[indextemp
].Name
,BUFFER_SIZE_SPECIAL
);


items
[indextemp
].BoundBox
=
Datas
[indextemp
].BoundBox
;


memcpy_s
(items
[indextemp
].Title
,BUFFER_SIZE_SPECIAL
, Datas
[indextemp
].Title
,BUFFER_SIZE_SPECIAL
);


memcpy_s
(items
[indextemp
].ImageType
,BUFFER_SIZE_COMMON
,Datas
[indextemp
].ImageType
,BUFFER_SIZE_COMMON
);


items
[indextemp
].Width
= Datas
[indextemp
].Width
;


items
[indextemp
].Height
= Datas
[indextemp
].Height
;


memcpy_s
(items
[indextemp
].Srs
,BUFFER_SIZE_COMMON
, Datas
[indextemp
].Srs
,BUFFER_SIZE_COMMON
);


}


LayerElement
= LayerElement
->NextSiblingElement
("Layer"
);


}


count
= indextemp
+ 1;


delete
myDocument
;


myDocument
= NULL
;


return
true
;

}

GetMap
下载过程

虽然强烈建议大家用
socket
开发图片下载这部分,不过还是给出
WinInet
的实现吧。

我定义了一个类

头文件

#ifndef
_CVodStreamCache_H

#define
_CVodStreamCache_H



#include
<iostream>

#include
<Windows.h>

#include
<WinInet.h>

#include
<tchar.h>

#include
<string>

using
namespace
std
;



#pragma
comment
(lib
, "wininet.lib"
)





class
CVodStreamCache

{

public
:


CVodStreamCache
();


~CVodStreamCache
();


bool
Create
(char
token
[], char
url
[]);


int

ReadBlock
(long
offset
, int
len
, char
*buffer
);


bool
Recive
(char
buf
[], int
& buflen
);


void
Destory
();


void
CacheInfo
();


void
Abort
(BOOL
IsAbort
);

private
:


HINTERNET
internetOpen
;


HINTERNET
internetOpenUrl
;




DWORD
dwStatusCode
;


DWORD
dwContentLength
;




HANDLE
createFile
;


static
const
int
BUFFERSIZE
= 9216;


BOOL
m_IsAbort
;

};

#endif

//_CVodStreamCache_H



CPP
文件

#include
"stdafx.h"

#include
<stdio.h>

#include
"CVodStreamCache.h"



CVodStreamCache
::CVodStreamCache
()

{


internetOpen
= NULL
;


internetOpenUrl
= NULL
;


m_IsAbort
= false
;

}



CVodStreamCache
::~CVodStreamCache
()

{



}



bool
CVodStreamCache
::Create
(char
token
[], char
url
[])

{


internetOpen
= InternetOpen
(NULL
, INTERNET_OPEN_TYPE_PRECONFIG
, NULL
, NULL
, 0);


if
(internetOpen
== NULL
)


{


cout
<< "Internet open failed!"
<< endl
;


return
false
;


}



if
(m_IsAbort
)



{



InternetCloseHandle
(internetOpen
);



return
false
;



}


if
(internetOpen
== NULL
)


{


cout
<< "Internet open failed!"
<< endl
;


return
false
;


}


internetOpenUrl
= InternetOpenUrlA
(internetOpen
, url
,


NULL
, 0, INTERNET_FLAG_TRANSFER_BINARY
| INTERNET_FLAG_PRAGMA_NOCACHE
, 0);


if
(internetOpenUrl
== NULL
)


{


cout
<< "Internet open url failed! Error code = "
<< GetLastError
() << endl
;


InternetCloseHandle
(internetOpen
);


return
false
;


}


return
true
;

}



int
CVodStreamCache
::ReadBlock
(long
offset
, int
len
, char
*buffer
)

{


return
0;

}



bool
CVodStreamCache
::Recive
(char
* buf
, int
& buflen
)

{


BOOL
internetReadFile
;


char
buffer
[BUFFERSIZE
];


memset
(buffer
, 0, sizeof
(buffer
));


DWORD
byteRead
= 0;


DWORD
byteDown
= 0;


while
(1)


{



if
(m_IsAbort
)



{



InternetCloseHandle
(internetOpenUrl
);


InternetCloseHandle
(internetOpen
);



return
false
;



}


if
(!internetOpenUrl
)


{


InternetCloseHandle
(internetOpenUrl
);


InternetCloseHandle
(internetOpen
);


return
false
;


}


internetReadFile
= InternetReadFile
(internetOpenUrl
, buffer
, sizeof
(buffer
), &byteRead
);


if
(byteRead
== 0)


{


break
;


}


if
(m_IsAbort
)


{


InternetCloseHandle
(internetOpenUrl
);


InternetCloseHandle
(internetOpen
);


return
false
;


}


if
(NULL
!= buf
)


{


memcpy_s
(buf
+byteDown
, buflen
- byteDown
, buffer
, byteRead
);


}


byteDown
+= byteRead
;


}


InternetCloseHandle
(internetOpenUrl
);


internetOpenUrl
= NULL
;


InternetCloseHandle
(internetOpen
);


internetOpen
= NULL
;


buflen
= byteDown
;


return
1;

}



void
CVodStreamCache
::Abort
(BOOL
IsAbort
)

{


m_IsAbort
= IsAbort
;

}

................................................
未完待续
................................................
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: