1 基本思路
1) 利用 HTML5 提供的 File API读取<input>
标签中的内容,使用FileReader对象的readAsDataURL方法对文件进行base-64编码,然后将图片的src属性设置为编码后的Data URL。
2) 创建一个与地图同宽高的<canvas>
标签,将图片的内容通过Canvas API中的drawImage方法绘制到canvas中,之后对图片的缩放、平移、旋转等操作都是在canvas上进行操作。
3) 当处于配准状态,即canvas的z-index属性设置为最高时,绑定mousedown事件,记录下相对于原始图片的坐标,以及屏幕坐标,并将屏幕坐标改为地理坐标。在地图及<canvas>
元素上分别标注出该点的位置及标注的序号。
4) 判断控制点个数,大于等于4个点则调用geoserver服务器的仿射变换服务,返回变换参数和误差估计,若计算出的误差较大可删除误差较大的控制点调整标准差。
5) 上传文件及配准信息:将图片保存问tif格式保存到后台服务器;将控制点信息保存为cpt格式的文件,cpts为json格式数组[{cpt},{cpt},{cpt},…],其中cpt:{ “ptNo”:”pt1”;
x0:double;y0: double;x1:double;y1:double;}(其中:x0,y0:屏幕坐标,x1,y1:地理坐标);将仿射变换服务返回的信息保存为twf格式的文件,tfw辅助文件是一个包含六行内容的ASCII文本文件,每行为一个数值,含义如下:
- X方向上的象素分辨素
- X方向的旋转系数
- Y方向的旋转系数
- Y方向上的象素分辨率
- 栅格地图左上角象素中心X坐标
- 栅格地图左上角象素中心Y坐标
6) 最后在数据库表中添加图层信息,具体操作为:向g_ftset表中添加一条记录,保存tfw
文件的路径;向g_layers添加一条记录,其中ftset_id为刚刚插入的主键id,yrtype:geoimg/tfw; path:tfw格式文件。
2 关键技术
2.1 实现与图片交互
创建一个对象存储图片的信息,包括图片的位置(top、left);图片大小(width、height)
缩放比例(scalex、scaley);倾斜角度(theta);透明度(opacity);标注点信息(markers)等。
利用以上信息计算图片的四个角的屏幕坐标,计算过程如下:
1)根据图片宽高及缩放比例计算图片目前的宽高值。
2)计算左上角屏幕坐标1
2
3
4
5
6
7
8
9
10
11
12//计算对角线长度、对角线与底边的夹角
this._hypotenuse = Math.sqrt(Math.pow(this.currentWidth / 2, 2) +
Math.pow(this.currentHeight / 2, 2));
this._angle = Math.atan(this.currentHeight / this.currentWidth;
//计算x、y方向的偏移量
var offsetX = Math.cos(this._angle + this.theta) * this._hypotenuse;
var offsetY = Math.sin(this._angle + this.theta) * this._hypotenuse;
//计算左上角坐标
var tl = {
x: this.left - offsetX,
y: this.top - offsetY
};
3)计算另外三个点屏幕坐标1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//根据倾斜角计算其sin值、cos值
var sinTh = Math.sin(theta);
var cosTh = Math.cos(theta);
//计算右上角坐标
var tr = {
x: tl.x + (this.currentWidth * cosTh),
y: tl.y + (this.currentWidth * sinTh)
};
//计算右下角坐标
var br = {
x: tr.x - (this.currentHeight * sinTh),
y: tr.y + (this.currentHeight * cosTh)
};
//计算右上角坐标
var bl = {
x: tl.x - (this.currentHeight * sinTh),
y: tl.y + (this.currentHeight * cosTh)
};
4)设置每个点的四个坐标
根据角落大小(cornersize)、倾斜角(theta)及之前计算出的四个角的屏幕坐标计算每个角的左上角、右上角、右下角及左下角屏幕坐标。
2.1.1 图片平移
1)绑定<canvas>
元素的鼠标按下mousedown事件,判断单击的点是否被图片包含,判断方法如下:
根据四个角的坐标点,组成上、右、下、左四条边:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
topline: {
o: oCoords.tl,
d: oCoords.tr
},
rightline: {
o: oCoords.tr,
d: oCoords.br
},
bottomline: {
o: oCoords.br,
d: oCoords.bl
},
leftline: {
o: oCoords.bl,
d: oCoords.tl
}
}
当鼠标的y轴值位于四个点的y轴最大值与最小值之间时,计算坐标点所在的水平线上
与四条边相交且x轴值大于鼠标点击的x值的点的个数(其实是向右做水平方向的射线),若相交的个数为奇数,则点击到了图片。
2)判断点击的坐标点是否被包含在四个角落的范围内,如果是则说明当前处于缩放状态,否的话则说明目前处于平移状态,判断方法同上。记录下offsetX值和offsetY值:1
2var offsetX = mp.ex - oImg.left;
var offsetY = mp.ey - oImg.top;
3)绑定<canvas>
元素的鼠标按下mousemove事件,当鼠标移动时触发事件,执行平移图片操作时动态修改图片的top、left值:1
2oImg.left = mp.ex - offsetX;
oImg.top = mp.ey - offsetY;
4)最后清空canvas画布中的内容,利用Canvas API的drawImage方法按照图片的原始大小将图片重新绘制到canvas元素中,并利用Canvas API的context.translate(oImg.left, oImg.top)平移canvas中的内容。
2.1.2 缩放、旋转图片
1)若鼠标点被包含在四个角落的范围内,说明当前处于缩放、旋转状态,记录下当前
的鼠标位置、缩放值(scalex)和倾斜角度(theta):1
2
3
4
5
6
7
8this._currentTransform = {
ex: mp.ex,
ey: mp.ey,
left: oImg.left,
top: oImg.top,
scalex: oImg.scalex,
theta: oImg.theta
};
2)移动鼠标时,动态计算当前图片的缩放大小scalex:1
2
3
4
5
6
7//计算鼠标点击时的坐标与左上角距离以及当前鼠标位置与左上角距离
var lastLen =Math.sqrt(Math.pow(this._currentTransform.ey - this._currentTransform.top, 2)
+Math.pow(this._currentTransform.ex - this._currentTransform.left, 2));
var curLen = Math.sqrt(Math.pow(mp.ey - this._currentTransform.top, 2)
+Math.pow(mp.ex - this._currentTransform.left, 2));
//根据之前的缩放值计算当前缩放值
var scale = this._currentTransform.scalex * (curLen / lastLen);
3)移动鼠标时,动态计算当前图片的倾斜角度theta:1
2
3
4
5
6
7
8
9
10
11
12//鼠标点击时,图片左上角垂线与左上角和鼠标点的连线构成的角度
var lastAngle = Math.atan2(
this._currentTransform.ey - this._currentTransform.top,
this._currentTransform.ex - this._currentTransform.left
);
//鼠标移动时,图片左上角垂线与左上角和鼠标点的连线构成的角度
var curAngle = Math.atan2(
mp.ey - this._currentTransform.top,
mp.ex - this._currentTransform.left
);
//动态计算当前图片的倾斜角度
var theta = (curAngle - lastAngle) + this._currentTransform.theta;
4)清空canvas画布中的内容,利用Canvas API的drawImage方法按照图片的原始大小将图片重新绘制到canvas元素中,并利用Canvas API的context.rotate(oImg.theta)和context.scale(oImg.scalex, oImg.scaley)旋转和缩放canvas中的内容。
2.2 计算控制点图片坐标
捕获鼠标点击事件,得到鼠标点坐标,计算坐标点与图片左上角点的x、y轴的差值:1
2var dltX = mp.ex - tl.x;
var dltY = mp.ey - tl.y;
计算坐标点与左上角点连线的长度以及左上角垂线与连线的角度:1
2var angle = Math.atan(dltX / dltY);
var dis = Math.sqrt(Math.pow( dltX , 2) + Math.pow( dltY , 2));
最后计算坐标点相对于图片左上角的坐标值:1
2var coordX = parseInt( Math.abs( Math.sin(angle+theta) * dis ) / oImg.scalex );
var coordY = parseInt( Math.abs( Math.cos(angle+theta) * dis ) / oImg.scalex );
3 改进计划
3.1 现实配准功能
目前只实现了将配准信息以及仿射变换的结果保存并上传到服务器中,但是还不能将tif文件作为一个图层加载到地图中,接下来进一步实现将其作为图层加载。
3.2 再次修改控制点
可利用已经保存的控制点信息,提供再次修改的功能,这样用户可以在上一次配准工作的基础上继续完成配准操作,保证配准结果的准确性。