項目需求需要在openlayers中繪制canvas矩形,矩形頂點是openlayers中的地理坐標。
繪制的關鍵是解決ImageCanvas與map中canvas的相對位置關系,以及坐標點與屏幕像素的關系。
openlayers中提供了ol.source.ImageCanvas類,當地圖縮放改變時,通過canvasFunction回調函數實現同步試試繪制。
最初代碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>添加Canvas圖層</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/openlayers/4.6.5/ol.js"></script>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/openlayers/4.6.5/ol.css" />
</head>
<body>
<div id="map"></div>
<script>
//創建imageCanvasLayer圖層
var imageCanvasLayer = new ol.layer.Image();
imageCanvasLayer.setSource(new ol.source.ImageCanvas({
//ImageCanvas有一個canvasFunction屬性,該屬性其實是一個回調函數,需要對該函數進行實現,從而創建一個canvas
canvasFunction: (extent, resolution, pixelRatio, size, projection) => {
var canvas = document.createElement('canvas');
//size是ImageCanvas的size,單位是px
canvas.width = size[0];
canvas.height = size[1];
var context = canvas.getContext('2d');
context.fillStyle = "blue";
context.fillRect(0, 0, 1000, 1000);
return canvas;
},
}));
var layers = [
new ol.layer.Tile({
source: new ol.source.OSM()//這里使用ol自帶的一個地圖圖層作為底圖
}),
imageCanvasLayer
];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([78, 12]),
projection: 'EPSG:3857',
zoom: 5
})
});
</script>
</body>
</html>
運行代碼,雖然繪制context.fillRect(0, 0, 1000, 1000),但發現ImageCanvas的起點在窗口外,說明ImageCanvas與map中的canvas起點不一致。
可以看出,openlayers地圖中默認的map canvas原點與ImageCanvas canvas原點不一致!
通過我本人的不斷嘗試,通過map.getSize()
獲取map canvas的長寬(單位px),與ImageCanvas canvas(canvasFunction里的size參數),獲得固定比例2:3
,在canvasFunction添加以下代碼,
//判斷兩個canvas的長寬的比例關系
var mapsize = map.getSize();
console.log('ImageCanvas size:' + size);
console.log('map size:' + size);
console.log('X_ratio:'+mapsize[0]/size[0]);
console.log('Y_ratio:'+mapsize[1]/size[1]);
在控制台可以看到:
然后可以發現兩個canvas比例固定,中心點也固定(可以通過context.fillRect(size[0]/2, size[1]/2, 100, 100);
驗證,圖形左上角頂點始終在map canvas中間)。
淚目,終於搞清楚兩個canvas究竟什么關系了!!!
重頭戲來了 ,下面將openlayer的地理坐標映射到ImageCanvas中,計算兩個canvas在x與y的偏移delt即可。
手畫的圖湊合看:
var delt = new Array(2);
delt[0] = (size[0] - mapsize[0]) / 2;
delt[1] = (size[1] - mapsize[1]) / 2;
所以:
X(ImageCanvas) = X(map canvas) + delt[0];
Y(ImageCanvas) = Y(map canvas) + delt[1];
下面實現一個右上頂點為[120.3803, 36.0704]的矩形
//先定義一個4326坐標系坐標,為青島市坐標
var cor_4326 = [120.3803, 36.0704];
//將4326坐標系坐標轉換成3857坐標系,3857坐標系就是那種坐標數值特別長的,ol默認是這種坐標,各種計算也是基於3857坐標系
var cor_3857 = ol.proj.transform(cor_4326, 'EPSG:4326', 'EPSG:3857');
//獲得以map canvas為參考,獲得當前坐標的像素值
var pix = map.getPixelFromCoordinate(cor_3857);
//利用delt,將坐標在map canvas中的像素位置轉化為ImageCanvas的像素位置,矩形長寬隨便設的
context.fillRect(pix[0] + delt[0], pix[1] + delt[1], 100, 100);
結果如下:
運行效果如圖:
完整代碼如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>添加Canvas圖層</title>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/openlayers/4.6.5/ol.js"></script>
<link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/openlayers/4.6.5/ol.css" />
</head>
<body>
<div id="map"></div>
<script>
var imageCanvasLayer = new ol.layer.Image();
imageCanvasLayer.setSource(new ol.source.ImageCanvas({
canvasFunction: (extent, resolution, pixelRatio, size, projection) => {
var canvas = document.createElement('canvas');
canvas.width = size[0];
canvas.height = size[1];
var context = canvas.getContext('2d');
context.fillStyle = "blue";
var mapsize = map.getSize();
var delt = new Array(2);
delt[0] = (size[0] - mapsize[0]) / 2;
delt[1] = (size[1] - mapsize[1]) / 2;
var cor_4326 = [120.3803, 36.0704];
var cor_3857 = ol.proj.transform(cor_4326, 'EPSG:4326', 'EPSG:3857');
var pix = map.getPixelFromCoordinate(cor_3857);
console.log("pix:" + pix);
context.fillRect(pix[0] + delt[0], pix[1] + delt[1], 100, 100);
return canvas;
},
}));
var layers = [
new ol.layer.Tile({
source: new ol.source.OSM()//這里使用ol自帶的一個地圖圖層作為底圖
}),
imageCanvasLayer
];
var map = new ol.Map({
layers: layers,
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([120.3803, 36.0704]),
projection: 'EPSG:3857',
zoom: 5,
})
});
</script>
</body>
</html>
接下來是根據多個點繪制矩形,解決點與點之間的像素距離是關鍵,很簡單,等我有空再寫吧