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