前言
研一跟着导师做了一个关于城市沙井盖监控的项目,其中的沙井盖管理中心(网站形式)就采用了百度地图的WEB接口。其实项目早在14年9月份就已经结束了,奈何本人患有拖延症,一直没有静下心来写一写关于百度地图二次开发的经历。转眼间,研二上学期就结束了,还有十天,就是农历新年了。就现在吧,写写开发经验,技术大牛轻喷。
项目需求
我做的是一个物联网项目——城市沙井盖监控。我们通过百度地图,将沙井盖节点可视化显示在百度地图上,然后通过点击井盖节点进行管理(授权、去授权、告警响应、查询)。由于WEB不能一直保持在线,故我们在进行节点管理的时候,实际上是通过数据库来进行数据中转。硬件端直接与上位机数据交互,上位机和WEB端实时读取数据库的数据信息,实现与硬件端的数据交互。整个系统的架构如下图所示(CANET可近似认为是井盖物理节点):
举个例子,现在某井盖节点被恶意打开,此时节点通过通信线路将数据发送到上位机软件,上位机软件接收到数据后立即将该节点的状态信息更新至MySQL数据库,WEB端通过实时读取数据库数据,更新地图上的显示。此时,数据库中该节点的状态数据更新为“非法打开”,地图上该节点的状态图标为对应的非法打开图样,如下图所示。
百度API简介
如今的主流的几种地图(谷歌地图、百度地图、高德地图等)都提供了API接口,考虑到谷歌这年对国内支持不太够,百度地图相对内容更为详尽,我选择了百度地图。百度地图为开发者提供了丰富的开发接口,为WEB端提供了JS API和Flash API(已停止更新),我选择的是JS API。特别注意,API版本在v1.5以上的,就必须申请密钥(ak)才能调用接口。密钥申请较为简单,此处就不再赘言。
如何调用API
作为一个技术小白,你的第一感觉应该是:这就是百度地图接口?那我怎么拿来用呢?
由于地图使用的是JS代码,故首先你得对javascript有大概认识(至少要能读懂)。新手建议先试试百度地图提供的地图生成器,通过手动生成一些地图代码,来了解地图的代码结构。生成器网址:http://api.map.baidu.com/lbsapi/createmap/index.html。看完示例代码,实际上可以看出来,百度地图是通过在网页上嵌入一个固定大小DIV容器,在容器内显示从百度地图官网中调用来的地图数据。
当然,仅仅看完示例代码,你也只能创建一个静态的地图,不能自定义地在地图上添加各种你想要的功能。下面我就说说我用到的功能的一些实现办法。当然,由于我也仅仅只是用到了百度地图的部分功能,所以本文章阐述的东西还是非常有限的。
实现需求
function iniMarkers()
{
var xmlhttp;
var user=document.getElementById("user").innerText; //获取当前登陆管理员id
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
if(xmlhttp.responseText!="")
{
wellArr=xmlhttp.responseText.split(";");//结点数据通过";"分割
addMarker();//将数据添加到地图图层
updateInfo();//定时刷新地图数据显示
}
else
{
window.alert("该用户暂未安排节点监控管理。");
}
}
}
xmlhttp.open("GET","mcmProcess.php?flag=fetchWelllist&user="+user,true);//获取结点数据
xmlhttp.send();
}
<pre name="code" class="javascript"> function addMarker(){
point= new BMap.Point(wellArr[0].split("|")[2],wellArr[0].split("|")[3]);
map.centerAndZoom(point,17);//设定地图的中心点和坐标并将地图显示在地图容器中
for(var i=0;i<window.wellArr.length-1;i++){
canet[i] = window.wellArr[i].split("|")[0];
canid[i] = window.wellArr[i].split("|")[1];
lng[i] = window.wellArr[i].split("|")[2];
lat[i] = window.wellArr[i].split("|")[3];
state[i] = window.wellArr[i].split("|")[4];
descr[i] = window.wellArr[i].split("|")[5];
var point = new BMap.Point(lng[i],lat[i]);
var sateImg;
switch(state[i])
{
case '1':
stateImg="img/authored.png";
break;
case '2':
stateImg="img/authoropened.png";
break;
case '3':
stateImg="img/warn.png";
break;
case '19':
stateImg="img/warnacked.png";
break;
case '255':
stateImg="img/lost.png";
break;
default:
stateImg="img/normal.png";
break;
}
var iconImg = new BMap.Icon(stateImg,new BMap.Size(20,20));
var marker = new BMap.Marker(point,{icon:iconImg});
var iw = createInfoWindow(i);
var label = new BMap.Label(canid[i],{"offset":new BMap.Size(17,0)});//右,下,
marker.setLabel(label);
map.addOverlay(marker);
markers[i]=marker;
label.setStyle({
borderColor:"#808080",
color:"#333",
cursor:"pointer"
});
(function(){
var index = i;
var _iw = createInfoWindow(i);
var _marker = marker;
_marker.addEventListener("click",function(){
this.openInfoWindow(_iw);
document.getElementById("canet").value=canet[index];
document.getElementById("canid").value=canid[index];
});
_iw.addEventListener("open",function(){
_marker.getLabel().hide();
})
_iw.addEventListener("close",function(){
_marker.getLabel().show();
})
label.addEventListener("click",function(){
})
label.show();
})()
}
}
当然,显示出来还是第一步,之后还要定时获取数据库更新的数据,更新地图显示。我将更新这一过程封装在updateinfo()函数中,具体实现代码如下:
function updateInfo()
{
if(timeCount!=0)
{
if(timeCount==5)
{
document.getElementById("report").value=
document.getElementById("report").value+"\n无法与数据库取得连接,通信异常!";
return;
}
++timeCount;
setTimeout("updateInfo()",2000);
return;
}
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
if(xmlhttp.responseText!="")
{
reportProcess(xmlhttp.responseText);
}
setTimeout("updateInfo()",2000);
timeCount=0;
}
}
user=document.getElementById('user').innerText;
xmlhttp.open("GET","mcmProcess.php?flag=fetchReport&user="+user,true);
xmlhttp.send();
++timeCount;
}
在updateinfo()函数中,我使用了Window的一个method,setTimeout()。该函数可以定时执行某一函数,我们用它定时执行updateinfo()函数。updateinfo函数通过AJAX技术,定时查询更新的数据,然后将更新数据显示出来。上述函数中嵌套了reportProcess子函数,就是用于更新显示。具体实现代码如下:
function reportProcess(responseStr)
{
var reportStr=document.getElementById("report").value;
reportArr=responseStr.split(";");
var iconImg;
var stateStr;
for(var i=0;i!=reportArr.length;++i)
{
switch(reportArr[i].split("|")[3])
{
case "255":
iconImg=new BMap.Icon("img/lost.png",new BMap.Size(20,20));
if(reportArr[i].split("|")[2]=="0")
{//canet失联了
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"+
reportArr[i].split("|")[1]+"失联!\n";
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1])
{
markers[j].setIcon(iconImg);
}
}
}
else
{//节点失联了
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1]&&
window.canid[j]==reportArr[i].split("|")[2])
{
markers[j].setIcon(iconImg);
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"
+reportArr[i].split("|")[1]+"节点:"+reportArr[i].split("|")[2]+"失联!\n";
}
}
}
break;
case "0"://正常关闭
var iconImg=new BMap.Icon("img/normal.png",new BMap.Size(20,20));
if(reportArr[i].split("|")[2]=="0")
{//canet建立连接了
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"
+reportArr[i].split("|")[1]+"状态正常!\n";
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1])
{
markers[j].setIcon(iconImg);
}
}
}
else
{//节点查询到了
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1]&&
window.canid[j]==reportArr[i].split("|")[2])
{
markers[j].setIcon(iconImg);
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"
+reportArr[i].split("|")[1]+"节点:"+reportArr[i].split("|")[2]+"正常!\n";
}
}
}
break;
case "1"://授权关闭
case "2"://授权打开
case "3"://非法打开
case "9"://确认完成授权
case "11"://取消授权成功
if(reportArr[i].split("|")[3]=="1")
{
iconImg=new BMap.Icon("img/authored.png",new BMap.Size(20,20));
stateStr="处于授权关闭!\n";
}else if(reportArr[i].split("|")[3]=="2")
{
iconImg=new BMap.Icon("img/authoropened.png",new BMap.Size(20,20));
stateStr="处于授权打开!\n";
}else if(reportArr[i].split("|")[3]=="3")
{
iconImg=new BMap.Icon("img/warn.png",new BMap.Size(20,20));
stateStr="处于非法打开!\n";
}else if(reportArr[i].split("|")[3]=="9")
{
iconImg=new BMap.Icon("img/authored.png",new BMap.Size(20,20));
stateStr="授权成功!\n";
}else if(reportArr[i].split("|")[3]=="11")
{
iconImg=new BMap.Icon("img/normal.png",new BMap.Size(20,20));
stateStr="取消授权成功!\n";
}
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1]&&
window.canid[j]==reportArr[i].split("|")[2])
{
markers[j].setIcon(iconImg);
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"
+reportArr[i].split("|")[1]+"节点:"+reportArr[i].split("|")[2]+stateStr;
}
}
break;
case "10"://授权失败
case "12"://取消授权失败
case "13"://已处于授权状态
case "14"://已处于非授权状态
case "19"://非法打开,已响应
if(reportArr[i].split("|")[3]=="10")
{
stateStr="授权失败!\n";
}else if(reportArr[i].split("|")[3]=="12")
{
stateStr="取消授权失败!\n";
}else if(reportArr[i].split("|")[3]=="13")
{
stateStr="已处于授权状态!\n";
}else if(reportArr[i].split("|")[3]=="14")
{
stateStr="已处于非授权状!\n";
}else if(reportArr[i].split("|")[3]=="19")
{
iconImg=new BMap.Icon("img/warnacked.png",new BMap.Size(20,20));
stateStr="非法打开,已响应报警!\n";
}
for(var j=0;j!=window.canet.length;++j)
{
if(window.canet[j]==reportArr[i].split("|")[1]&&
window.canid[j]==reportArr[i].split("|")[2])
{
markers[j].setIcon(iconImg);
reportStr=reportStr+reportArr[i].split("|")[0]+" CANET:"
+reportArr[i].split("|")[1]+"节点:"+reportArr[i].split("|")[2]+stateStr;
}
}
break;
}
}
document.getElementById("report" ).value=reportStr;
}
我这里使用的代码方法比较笨,就是用遍历的办法逐个更新节点。但其实,对于本项目性能上是能够满足的。为什么这么说呢?首先,在本项目中,虽然监控的总的井盖节点数据众多,但实际上每个区域的井盖节点是不多的。在实际情况中,各个系统用户应该只是管理他所在辖区内的井盖节点,这些节点是有限数目的。因而,这样的代码从性能上是能够保证顺畅运行的。但,这并不代表这些代码是高效率的。算是本人的惰性吧,在完成功能实现后,没有花太多心思用在提高代码执行效率上,没有重构代码。所以,本文仅希望能给初学者稍稍启发,读者就别吐槽俺的糟糕代码了。
结束语
从开发难度上来说,百度地图的二次开发难度还是不大的。个人认为,在官网上已经提供了足够详细的开发文档,所以开发人员只要熟读开发指南,就足以能够进行快速的二次开发。地图的开放接口为广大商家大大节约了自己制作和维护地图的成本,给各大地图提供商点个赞!