openlayers的popup实现

功能需求如下:

在一张地图上,我们希望点击相应的点之后,能够弹框显示该点的具体信息。

最终效果如下:(由于某些原因,有关信息不予显示)

首先,要完成该图的效果,要首先保证你了解了下面的相关知识:

1.如果你的openlayers工程和Geoserver部署在不同的服务器上,则,你需要解决跨域的问题。

无论你是通过WMS的GetFeatureInfo或者是通过WFS的GetFeature来实现,最终都是以Ajax的方式来发起请求。

下图是用firebug来捕捉点击事件,可以看出,X-Requested-With:XMLHttpRequest

所以该请求通过Ajax方式发起。

讲到这个地方,有必要温习一下几个知识点:同源策略,跨域访问。

注:参考
袭烽-《同源策略和跨域访问 》
该文章写的很好很全,建议读者抽空细读。

(1)何谓同源策略:(Same origin policy)
URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。
浏览器的同源策略,限制了来自不同源的”document”或脚本,对当前”document”读取或设置某些属性。 (白帽子讲web安全[1])
从一个域上加载的脚本不允许访问另外一个域的文档属性。

(2)跨域访问:
顾名思义,就是一个站点中的资源去访问另外一个不同域名站点上的资源。
这种情况很常见,比如说通过 style 标签加载外部样式表文件、通过 img 标签加载外部图片、
通过 script 标签加载外部脚本文件、通过 Webfont 加载字体文件等等。
默认情况下,脚本访问文档属性等数据采用的是同源策略。

2.具体如何实现跨域访问
注:参考
袭烽-《同源策略和跨域访问 》

(1) JSONP
JSONP技术实际和Ajax没有关系。我们知道<script>标签可以加载跨域的javascript脚本,并且被加载的脚本和当前文档属于同一个域。因此在文档中可以调用/访问脚本中的数据和函数。如果javascript脚本中的数据是动态生成的,那么只要在文档中动态创建<script>标签就可以实现和服务端的数据交互。
JSONP就是利用<script>标签的跨域能力实现跨域数据的访问,请求动态生成的JavaScript脚本同时带一个callback函数名作为参数。其中callback函数本地文档的JavaScript函数,服务器端动态生成的脚本会产生数据,并在代码中以产生的数据为参数调用callback函数。当这段脚本加载到本地文档时,callback函数就被调用。

(2) Proxy
使用代理方式跨域更加直接,因为SOP的限制是浏览器实现的。如果请求不是从浏览器发起的,就不存在跨域问题了。
使用本方法跨域步骤如下:
1. 把访问其它域的请求替换为本域的请求
2. 本域的请求是服务器端的动态脚本负责转发实际的请求
各种服务器的Reverse Proxy功能都可以非常方便的实现请求的转发。

(3) CORS
通过在HTTP Header中加入扩展字段,服务器在相应网页头部加入字段表示允许访问的domain和HTTP method,客户端检查自己的域是否在允许列表中,决定是否处理响应。
实现的基础是JavaScript不能够操作HTTP Header。某些浏览器插件实际上是具有这个能力的。
服务器端在HTTP的响应头中加入(页面层次的控制模式):
Access-Control-Allow-Origin: example.com
Access-Control-Request-Method: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization, Accept, Range, Origin
Access-Control-Expose-Headers: Content-Range
Access-Control-Max-Age: 3600
多个域名之间用逗号分隔,表示对所示域名提供跨域访问权限。”*”表示允许所有域名的跨域访问。
客户端可以有两种行为:
1. 发送OPTIONS请求,请求Access-Control信息。如果自己的域名在允许的访问列表中,则发送真正的请求,否则放弃请求发送。
2. 直接发送请求,然后检查response的Access-Control信息,如果自己的域名在允许的访问列表中,则读取response body,否则放弃。
本质上服务端的response内容已经到达本地,JavaScript决定是否要去读取。

我是通过设置Proxy的方式来实现跨域访问的,具体做法,请看我上一篇的文章:Tomcat6 配置cgi openlayers跨域访问

下面贴上我程序中重要部分的代码:

<!DOCTYPE html>
<html>
<head>
<title>ChinaMap1</title>
<meta name="keywords" content="keyword1,keyword2,keyword3">
<meta name="description" content="this is my page">
<meta name="content-type" content="text/html; charset=GBK">
<meta name="language" content="java">
<meta name="import" content="java.util.*,java.io.*">
<style type="text/css">
#map {
	width: 1200px;
	height: 900px;
	border: 0.5px solid black;
}
</style>
<script src="lib/OpenLayers.js"></script>
<script src="lib/proj4js/lib/proj4js-combined.js"></script>
<script src="lib/transform.js"></script>
<script type="text/javascript">
	OpenLayers.ProxyHost = "cgi/proxy.cgi?url=";
	var map;
	var untiled;
	var tiled;
	var layer_teleport;
	var tpoint;
	var xmlHttp;
	var popup;//全局的变量,popup
	//var result;
	function init() {
		// if this is just a coverage or a group of them, disable a few items,
		// and default to jpeg format
		format = 'image/png';
		var bounds = new OpenLayers.Bounds(-2578821.947218156,
				2367106.345797384, 2092054.120327893, 6385320.290080428);
		var options = {
			controls : [],
			maxExtent : bounds,
			maxResolution : 22933.484092629773,
			projection : "EPSG:111111",
			units : 'm'
		};
		map = new OpenLayers.Map('map', options);

		// setup tiled layer
		tiled = new OpenLayers.Layer.WMS("Geoserver layers - Tiled",
				"http://localhost:8090/geoserver/ChinaMap/wms", {
					LAYERS : 'MySqlChinaMap',
					STYLES : '',
					format : format,
					tiled : true,
					tilesOrigin : map.maxExtent.left + ','
							+ map.maxExtent.bottom
				}, {
					buffer : 0,
					displayOutsideMaxExtent : true,
					isBaseLayer : true,
					yx : {
						'EPSG:111111' : false
					}
				});

		// setup single tiled layer
		untiled = new OpenLayers.Layer.WMS("Geoserver layers - Untiled",
				"http://localhost:8090/geoserver/ChinaMap/wms", {
					LAYERS : 'MySqlChinaMap',
					STYLES : '',
					format : format
				}, {
					singleTile : true,
					ratio : 1,
					isBaseLayer : true,
					yx : {
						'EPSG:111111' : false
					}
				});
		map.addLayers([ untiled, tiled ]);

		layer_teleport = new OpenLayers.Layer.WMS("Teleport",
				"http://localhost:8090/geoserver/ChinaMap/wms", {
					layers : 'ChinaMap:viewteleportgeo',
					INFO_FORMAT : 'application/vnd.ogc.gml',
					transparent : true
				}, {
					isBaseLayer : false
				});
		map.addLayer(layer_teleport);
		//添加弹出框,popup
		popup = new OpenLayers.Control.WMSGetFeatureInfo(
				{
					url : 'http://localhost:8090/geoserver/ChinaMap/wms',
					title : 'Identify features by clicking',
					queryVisible : true,
					layers : [ layer_teleport ],
					eventListeners : {
						getfeatureinfo : function(event) {
							var attributes;
							var teleportName = null;
							var lat = 0;
							var long = 0;
							var latDegrees = 0;
							var latMinutes = 0;
							var latSeconds = 0;
							var longDegrees = 0;
							var longMinutes = 0;
							var longSeconds = 0;
							var picture;
							var result = null;
							var feature = event.features[0];
							if (feature) {
								attributes = feature.attributes;
								teleportName = attributes.TeleportName;
								latDegrees = attributes.LatDegrees;
								latMinutes = attributes.LatMinutes;
								latSeconds = attributes.LatSeconds;
								lat = parseFloat(latDegrees)
										+ parseFloat(latMinutes / 60)
										+ parseFloat(latSeconds / 3600);
								lat = Math.round(lat * 100) / 100;//保留小数点后两位
								longDegrees = attributes.LongDegrees;
								longMinutes = attributes.longMinutes;
								longSeconds = attributes.longSeconds;
								long = parseFloat(longDegrees)
										+ parseFloat(longMinutes / 60)
										+ parseFloat(longSeconds / 3600);
								long = Math.round(long * 100) / 100;//保留小数点后两位
								picture = attributes.picture;
								console.log(picture);
								result = "<div> <table> <tr> <td> <strong> 名称:</strong></td> <td>"
										+ teleportName
										+ "</td></tr> <tr> <td> <strong> 纬度:</strong></td><td>"
										+ lat
										+ "</td></tr> <tr><td><strong>经度:</strong></td><td>"
										+ long
										+ "</td></tr><tr><td colspan=\"2\"> <img src=\""+picture+"\" alt=\""+teleportName
                                                                                +"\"/></td></tr></table></div>";
							}
							if (result) {
								map.addPopup(new OpenLayers.Popup.FramedCloud(
										"chicken",
										map.getLonLatFromViewPortPx(event.xy),
										null, result, null, true), true);
							}
						}
					}
				});
		map.addControl(popup);
		popup.activate();
		//页面增加切换,勾选图层的按钮
		map.addControl(new OpenLayers.Control.LayerSwitcher());
		// build up all controls
		map.addControl(new OpenLayers.Control.PanZoomBar({
			position : new OpenLayers.Pixel(2, 15)
		}));
		map.addControl(new OpenLayers.Control.Navigation());
		map.addControl(new OpenLayers.Control.Scale($('scale')));
		map.addControl(new OpenLayers.Control.MousePosition({
			element : $('location')
		}));
		console.log("Hello");
		map.zoomToExtent(bounds);
		//注册map的鼠标单击监听事件,关键字“click”代表单击鼠标
		map.events.register("click", map, onMapClick);

	}

	function onMapClick(e) {
		var lonlat = map.getLonLatFromViewPortPx(e.xy);
		// Lamber 转为 经纬度
		var p = ChinaMap.transformToLonLat(lonlat.lon, lonlat.lat);
		document.getElementById("lon").value = p.x;
		document.getElementById("lat").value = p.y;
	}
</script>
</head>
<body onload="init()">
	<div id="map"></div>
	经度:
	<input id="lon" type="text" disabled="disabled">    
	    纬度:
	<input id="lat" type="text" disabled="disabled">

</body>
</html>

注:代码中的地图坐标EPSG:111111是自定义的,因为Geoserver默认无此坐标系。
题外话:前三周刚刚接触GIS开发,发现由于地球的不规则椭球形,导致各个地方将椭球形表面的地形转化成平面地图绘制 的时候各家的标准都不一样。
中国早年采用的是Beijing-1954大地坐标系,貌似采用的是苏联“克拉索夫斯基”测绘标准,而后采用1980-Xian大地坐标系。但是由于早年测绘的数据迁移转化
的工作量大等一系列原因,某些地图仍旧采用的是1954-Beijing的大地坐标。普及一下,谷歌地图用的是ESPG:900913,而默认的国际标准是ESPG:4326。
而本文作者恰好工作需要,用到的是Beijing-1954标准,需要自定义ESPG:111111,这样才好把坐标转化为国际的经纬度来方便查看。
另,附上ESPG:111111的定义:
该定义可以在Geoserver 2.4.8版本的D:\Program Files\Coding Software\GeoServer 2.4.8\data_dir\user_projections下的epsg.properties文件中添加下面一行:
111111=PROJCS[“China_Lambert_Conformal_Conic”,GEOGCS[“GCS_Beijing_1954”,DATUM[“D_Beijing_1954”,SPHEROID[“Krasovsky_1940”,6378245.0,298.3]],PRIMEM[“Greenwich”,0.0],UNIT[“Degree”,0.0174532925199433]],PROJECTION[“Lambert_Conformal_Conic”],PARAMETER[“False_Easting”,0.0],PARAMETER[“False_Northing”,0.0],PARAMETER[“Central_Meridian”,105.0],PARAMETER[“Standard_Parallel_1”,30.0],PARAMETER[“Standard_Parallel_2”,62.0],PARAMETER[“Latitude_Of_Origin”,0.0],UNIT[“Meter”,1.0],AUTHORITY[“EPSG”,”111111″]]
则在Geoserver中添加数据图层的使用,使用该标准,就能把Beijing-1954大地坐标较好地转化成ESPG:4326经纬坐标

至此完成。

由于刚刚接触javascript,所以代码写的特别丑,目前程序还在改动,有新功能还会更新推进。

GIS开发的新手,有误之处请各位不吝赐教。

转载自:https://blog.csdn.net/Lin00Kun11/article/details/38926279

You may also like...