我们在做GIS空间分析的时候常常需要计算某个点的缓冲区,这时需要以这个点为中心,画一个圆。怎么来实现呢?
缓冲区的做法在arcims里有相应的API,但是我们现在的做法是抛开IMS的API,只在前台画圆。这里利用了VML来画圆。
用VML技术来画圆还是比较简单的,这里较困难的是需要解决每像素代表实地多少距离的问题,知道了这个值就可以利用需要画的缓冲区的半径得到到底用VML要画多少像素。
概念中我们最重要的是要弄清楚“像素”的含义。我们常常大概明白它的意思,但是想想:为什么我们写程序时都是用像素呢?而不用米之类的长度单位,而如果分辨率不同的话,像素值是不变的,对结果会产生什么影响?等等。还有,我们说的“每像素代表实地多少距离”并不是我们在学地图学里说的“比例尺”。比例尺是实地距离除以图上距离(1厘米),而对电子地图来说,比例尺没什么意义,因为在电子地图里, “每像素代表实地多少距离”的分母是像素值,并不是长度值。我们在IMS返回的是MBR的四个值XMin,YMin,XMax,YMax和Width,Height,其中width和height是像素值,这点需要明确。
我们建立一个Map对象,用于存放IMS里返回的地图状态:MBR的四个值XMin,YMin,XMax,YMax和地图的长度width(像素值)和高度height(像素值)。通过它们就可以得到一些我们很常用且重要的函数:屏幕坐标与地理坐标的相互转化函数,输入屏幕坐标得到距离函数(测距时用),判断输入的经纬度点是否在地图范围内(如果不在的话就不加载进来)函数。如下:
Js代码
function Map()
{
this.XMin = 0;
this.YMin = 0;
this.XMax = 0;
this.YMax = 0;
this.Width = 0;
this.Height = 0;
this.ImageURL = '';
this.getXDistance = function(){
return this.XMax - this.XMin;
}
this.getYDistance = function(){
return this.YMax - this.YMin;
}
this.getPixelX = function(){
return this.getXDistance()/this.Width;
}
this.getPixelY = function(){
return this.getYDistance()/this.Height;
}
this.toScreenX = function(x){
return (x-this.XMin)/this.getPixelX();
}
this.toScreenY = function(y){
return (this.YMax-y)/this.getPixelY();
}
this.toGeoX = function(x){
return this.XMin + x*this.getPixelX();
}
this.toGeoY = function(y){
return this.YMax - y*this.getPixelY();
}
this.distance = function(x1, y1, x2, y2){
var num1 = Math.PI/180;
var num2 = 1 / num1;
x1 = this.toGeoX(x1) * num1;
y1 = this.toGeoY(y1) * num1;
x2 = this.toGeoX(x2) * num1;
y2 = this.toGeoY(y2) * num1;
return ((111120 * num2) * Math.acos((Math.sin(y1) * Math.sin(y2)) + ((Math.cos(y1) * Math.cos(y2)) * Math.cos(x2 - x1))));
}
this.include = function(x, y){
return (x>this.XMin && x<this.XMax && y>this.YMin && y<this.YMax);
}
}
然后我们得到像素与实地距离比,如下:
Js代码
//像素与实地距离比
getScale: function(){
var map = webmap.getMap();
var x1 = map.toScreenX(map.XMin);
var x2 = map.toScreenX(map.XMax);
var y1 = map.toScreenY(map.YMin);
var y2 = map.toScreenY(map.YMax);
geoDis = map.distance(x1, y1, x2, y1);
scepx = map.Width;
return parseFloat(scepx)/(parseFloat(geoDis)*100);
}
接着我们写出画圆接口,调用这个接口就可以把一个圆对象放到webmap的全局数组displayed_circle里,如下:
Js代码
//画圆接口(缓冲区时用)
setDisplayCircle: function(args){
function setcircle(left,top,width,height,showColor,showTitle,clickFun,circleProp){
var o = new Object();
o.left = left;
o.top = top;
o.width = width;
o.height = height;
o.showColor = showColor;
if(showTitle)
o.showTitle = function(){Tip(showTitle)};
if(clickFun)
o.clickFun = clickFun;
if(circleProp)
o.circleProp = circleProp;
webmap.displayed_circle[webmap.displayed_circle.length] = o;
}
setcircle(args.left,args.top,args.width,args.height,args.showColor||'red',args.showTitle,args.clickFun,args.circleProp);
}
以上代码需要明确以下方面:
1.setcircle函数传入的参数是一个对象。为什么要传入对象而不直接传入字符串参数呢?因为传入对象可以允许调用这个方法的人只写要传入的参数即可,不需要每个参数都传。
2.传入的对象参数里的属性又包含一个对象。args.circleProp就是一个对象,它主要是传入一些将要画的圆的一些额外的属性,如你可以传入它的KEY,这样如果你画了若干个圆,就可以根据这个值来得到它更详尽的属性了。 3.displayed_circle是webmap的全局数组,用于保存每个圆对象。画线接口完成后,就可以在地图刷新完返回地图的状态后调用如下函数来画线,用到了VML技术, 如下:
Js代码
//地图刷新完画圆(缓冲区时用)
_RefreshCircle: function(){
document.getElementById('frontMap').innerHTML = '';
for(var i=0;i<this.displayed_circle.length;i++){
var o = this.displayed_circle[i];
var objCircle = document.createElement("v:oval");
var scale = this.getScale();
objCircle.style.width = o.width*100000*scale;
objCircle.style.height = o.height*100000*scale;
objCircle.style.left = (webmap.getMap().toScreenX(o.left)-o.width*100000*scale/2)+'px';//定位以左上角来定
objCircle.style.top = (webmap.getMap().toScreenY(o.top)-o.height*100000*scale/2)+'px';
objCircle.style.opacity = 0.8;
objCircle.style.filter = 'alpha(opacity=80)';
objCircle.style.fillcolor = '#99cccc';
objCircle.id = "objCircle";
objCircle.strokecolor = o.showColor;
if(o.showTitle)
objCircle.onmouseover = o.showTitle;
if(o.clickFun)
objCircle.onmousedown = o.clickFun;
if(o.circleProp) {
for(var key in o.circleProp){
circleProp[key] = o.circleProp[key];
}
}
document.getElementById("frontMap").appendChild(objCircle);
}
},
以上代码需要明确以下方面:
1.frontMap是地图div上的内的一个div,首先先把它清空,用innerHMTL=''来实现。
2.然后循环遍历displayed_circle数组,得到每一个圆对象的属性,放到VML建立好的圆元素里。
3.最后通过appendChild()把每个圆元素加到DIV进来。
4.这里要注意的是:objCircle.style.width,objCircle.style.height,objCircle.style.left,objCircle.style.top这四个值需要做相应地转化。
5.用如下方法把圆的属性增加进来: for(var key in o.circleProp){ circleProp[key] = o.circleProp[key]; }
注:这里不能用circleProp.key