前端绘制地图中的地理坐标到屏幕坐标转换的简单思路–放大、缩小、移动。目的是解决前端绘制地图中存在的问题。

前言

为了让自己熟悉地图的绘制,我想手绘一个地图如何绘制的工具。使用了zrender库,这是一个canvas库,Echart基于此做。数据的来源则是一个geojson文件。下面就要解决一下如何进行绘制的问题包括:
1、地理坐标如何与屏幕坐标转换。
2、如何放大、缩小。
3、如何移动。

问题1 地理坐标系与屏幕坐标系如何进行转换

这里面我们是读取一个矢量数据在地图上绘制出来,思路如下:
0、明确浏览器坐标系是屏幕坐标系原点在左上角,地理坐标系原点是坐标系的原点。
1、找到屏幕坐标系的方框和地理坐标系的方框,找到屏幕坐标系的高度和地理坐标系的高度计算缩放比。

注:这里的缩放比,我是按照简单的思路来的,其实应该是选择最小的那个,然后将坐标挪到中间。

2、有了缩放比之后就明确了,地理坐标系和屏幕坐标系之间的缩放关系,之后开始计算地理坐标系在屏幕上的坐标。如图根据此点到左边的距离和到顶部的距离之后乘以缩放参数就会得到此点的屏幕坐标。
如何从地理坐标计算到屏幕坐标
3、转换完屏幕坐标之后就可以绘制了。如果初始时有默认的缩放级别参照下面的缩放算一下就行。

问题2 如何放到缩小

1 如何确定放大缩小系数

这里使用了mousewheel事件直接绑定在了zrender对象上。这里要处理的是缩放系数缩放系数也就是放大和缩小的倍数。 使用evt.wheelDelta参数大于0代表放大小于0代表缩小。通过这样计算出缩放系数。

1
2
3
4
5
6
7
8
9
var x = evt.offsetX;
var y = evt.offsetY;//距离容器
var scalefactor = 1.2;
evt.wheelDelta = evt.wheelDelta ? evt.wheelDelta : (evt.deltalY * (-40)); //获取当前鼠标的滚动情况
if (evt.wheelDelta > 0) {
scalefactor *= 2;
} else {
scalefactor /= 2;
}

参考 http://www.zhangxinxu.com/wordpress/2013/04/js-mousewheel-dommousescroll-event/

2 如何放大缩小

这里面的思路是:
1、直接将地理信息数据每个坐标乘以放大系数得到新的坐标但是这个坐标是向外扩展的,所以还要纠正回去、
2、计算鼠标缩放的原点的坐标与缩放之后的原点坐标的差值,作为纠正值,向原点纠正纠正之后放大缩小完成。

问题3 如何移动

主要思路 :
1、 使用事件mouseup和mousedown。
2、 计算两个事件的坐标差然后将数据坐标计算即可。

效果

如何从地理坐标计算到屏幕坐标

总结

效果简单但是还有很大的提升空间,解决了主要的问题剩下的还可以精进。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
'手动实现的一个小地图'
function CanvasMap(render,mapdata){
this.render = render;
this.mapdata=mapdata;
this.bound = this.returnMinXY(mapdata);
console.log(mapdata,this.bound)
var _my = this;
var x1,y1,x2,y2;
this.render.on("mousedown",function(evt){
x1 = evt.offsetX;
y1 = evt.offsetY;
});
this.render.on("mouseup",function(evt){
x2 = evt.offsetX;
y2 = evt.offsetY;
var xfactor = x2-x1;
var yfactor = y2-y1;
_my.render.clear();
_my.move(_my.mapdata,xfactor,yfactor);
for (var i=0;i<_my.mapdata.features.length;i++){
_my.addgeometry(_my.mapdata.features[i],false);
}
});
this.render.on("mousewheel",function(evt){
// console.log(evt);
var x = evt.offsetX;
var y = evt.offsetY;//距离容器
var scalefactor = 1.2;
evt.wheelDelta = evt.wheelDelta ? evt.wheelDelta : (evt.deltalY * (-40)); //获取当前鼠标的滚动情况
if (evt.wheelDelta > 0) {
scalefactor *= 2;
} else {
scalefactor /= 2;
}
console.log(evt.wheelDelta)
var sx = scalefactor*x;
var sy = scalefactor*y;
var sdisx =sx-x;
var sdisy = sy-y;
_my.sacle(_my.mapdata,scalefactor,sdisx,sdisy);
_my.render.clear();
for (var i=0;i<_my.mapdata.features.length;i++){
_my.addgeometry(_my.mapdata.features[i],false);
}

});
}
/**
*
* @param {一个geojson文件的对象} obj
*/
CanvasMap.prototype.returnMinXY=function(obj){
var factor = 20;
var features = obj.features,feature = null,coor = null;
var t = features[0].geometry.coordinates;
var minx=t[0][0][0],miny=t[0][0][1],maxx=t[0][0][0],maxy=t[0][0][1];
for (var z=0;z<features.length;z++){
feature=features[z];
coor = feature.geometry.coordinates;
for (var i=0;i<coor.length;i++){
for (var j=0;j<coor[i].length;j++){
var coo = coor[i][j];
if (minx >coo[0] ){
minx = coo[0];
}
if (maxx <coo[0]){
maxx = coo[0];
}
if (miny >coo[1]){
miny = coo[1];
}
if (maxy <coo[1]){
maxy = coo[1];
}
}
}
}

return {
minx:minx,
miny:miny,
maxx:maxx,
maxy:maxy
}
}
/**
* 坐标转换
* 函数思路是这个样子的
* 首先找出这里面的最左上角的坐标
* 之后minx maxy
* 之后算出点坐标与 左上角点的距离也就是曼哈顿距离 dx dy
* 之后把距离扩大扩大的factor为宽度的比
* @param {*} geometry
* @param {*} bound
*/
CanvasMap.prototype.changecoordinate = function(geometry,bound){
// 获取外界矩形
var factor = 400/(bound.maxy-bound.miny);//获取外边界
var minx=bound.minx,miny=bound.miny,maxx=bound.maxx,maxy=bound.maxy;
// //获得了外接矩形

var coor = geometry.coordinates;
var disx = 0,disy = 0;
for (var i=0;i<coor.length;i++){
for (var j=0;j<coor[i].length;j++){
var coo = coor[i][j];
disx = coo[0]-minx;
disy = maxy - coo[1];
coor[i][j][0]=disx*factor;
coor[i][j][1]=disy*factor;
}
}
}

CanvasMap.prototype.addgeometry = function(data,change){
var bound = this.bound;
var geometry = [];
if (change!=false){
this.changecoordinate(data.geometry,bound);
}

var polygon = new zrender.Polygon({
shape: {
points:data.geometry.coordinates[0]
},
style: {
fill: 'none',
stroke: '#c93756'
}
});
this.render.add(polygon);
};

/**
*
* @param {*} data
* @param {*} xfactor
* @param {*} yfactor
*/
CanvasMap.prototype.move=function(data,xfactor,yfactor){
console.log("move !!!!!!")
for (var i=0;i<data.features.length;i++){
var feature = data.features[i];
movesingle(feature.geometry,xfactor,yfactor);
}
function movesingle (geometry,xfactor,yfactor){
var coor = geometry.coordinates;
var disx = 0,disy = 0;
for (var i=0;i<coor.length;i++){
for (var j=0;j<coor[i].length;j++){
coor[i][j][0]=coor[i][j][0]+xfactor;
coor[i][j][1]=coor[i][j][1]+yfactor;
}
}
}
};

/**
* 放大
*/
CanvasMap.prototype.sacle = function(data,scalefactor,sdisx,sdisy){
for (var i=0;i<data.features.length;i++){
var feature = data.features[i];
scalesingle(feature.geometry,scalefactor,sdisx,sdisy);
}
function scalesingle (geometry,scalefactor){
var coor = geometry.coordinates;
var disx = 0,disy = 0;
for (var i=0;i<coor.length;i++){
for (var j=0;j<coor[i].length;j++){
coor[i][j][0]=coor[i][j][0]*scalefactor-sdisx;
coor[i][j][1]=coor[i][j][1]*scalefactor-sdisy;
}
}
}
}