关于百度地图在web端二次开发经验分享

时间 : 15-02-08 栏目 : 技术笔记 作者 : eekuang 评论 : 0 点击 : 4,110 次

前言

研一跟着导师做了一个关于城市沙井盖监控的项目,其中的沙井盖管理中心(网站形式)就采用了百度地图的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容器,在容器内显示从百度地图官网中调用来的地图数据。

当然,仅仅看完示例代码,你也只能创建一个静态的地图,不能自定义地在地图上添加各种你想要的功能。下面我就说说我用到的功能的一些实现办法。当然,由于我也仅仅只是用到了百度地图的部分功能,所以本文章阐述的东西还是非常有限的。

 

实现需求

在百度地图接口中,实际上,是将整个地图封装成一个类——map类,而地图的所有内容都封装成了类里的成员和函数。例如,用户自定义的覆盖物(marker),就是以数组的形式存在map类中,地图的缩放功能通过类中的enableDragging()和disableDragging()来启用和禁用。至于具体某个功能通过类里的什么成员、成员函数来实现,就需要在开发指南中通过查找相关信息了。这是JavaScript API大众版的地址:点击打开链接
在我的项目中,首先我需要从数据库加载预设的井盖节点数据,显示在地图上,接着定时查询更新的数据信息,更新地图上的节点显示。我使用了简单的AJAX代码实现了异步获取节点数据,然后显示在地图上。所谓异步就是,我先加载地图,然后异步获取数据库的节点信息,将节点的图层添加在地图上。注意,在js代码中,我们将井盖节点(网络编号、子网编号、经度、纬度、状态、描述信息)存入一个全局一维数组中,每个数组成员中包含该节点的所有信息。例如数组中某元素存储字符串为"218.192.171.79:5001|90|113.35255|23.159962|0|一教旁",则表示该节点处于218.192.171.79:5001这个网络中,子网内的编号为90,经度113.35255,纬度23.159962,状态为0(正常),备注信息“一教旁”。贴出添加节点的javascript函数代码:

 

    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();    	
    }
    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;
    }

我这里使用的代码方法比较笨,就是用遍历的办法逐个更新节点。但其实,对于本项目性能上是能够满足的。为什么这么说呢?首先,在本项目中,虽然监控的总的井盖节点数据众多,但实际上每个区域的井盖节点是不多的。在实际情况中,各个系统用户应该只是管理他所在辖区内的井盖节点,这些节点是有限数目的。因而,这样的代码从性能上是能够保证顺畅运行的。但,这并不代表这些代码是高效率的。算是本人的惰性吧,在完成功能实现后,没有花太多心思用在提高代码执行效率上,没有重构代码。所以,本文仅希望能给初学者稍稍启发,读者就别吐槽俺的糟糕代码了。害羞害羞害羞

 

 

结束语

从开发难度上来说,百度地图的二次开发难度还是不大的。个人认为,在官网上已经提供了足够详细的开发文档,所以开发人员只要熟读开发指南,就足以能够进行快速的二次开发。地图的开放接口为广大商家大大节约了自己制作和维护地图的成本,给各大地图提供商点个赞!

 

anyShare分享到:

本文标签 , ,

除非注明,文章均为( eekuang )原创,转载请保留链接: http://www.14en.com/?p=122

关于百度地图在web端二次开发经验分享:等您坐沙发呢!

发表评论


-----===== 博主信息 =====-----
腾讯后台开发工程师
介绍:目前从事容器云相关开发工作,主要使用C++/go语言。


0