后面的课程会以
mapboxGL
的canvas-source
为入口开展,通过一个canvas
画布,实现webgis的基础功能,包括:1、展示矢量数据(点、线、面,格式为geojson
);2、展示x-y-z
切片;3、展示wms
服务。webgis基础功能部分,有一个比较核心的是前面的文章里面提到的屏幕坐标和地图坐标的相互转换,在mapboxGL
中,可以通过map.project()
实现地图坐标转换为屏幕坐标,通过map.unproject()
实现屏幕坐标转换为地图坐标。
为方便后面使用,先将公用的部分拿出来,代码如下:
var CanvasLayer = function(map, data, style = {}) {
this._data = data;
this._map = map;
this._canvas = null;
this._ctx = null
this._init(data);
return this;
}
/**
* 初始化
* @param data
* @private
*/
CanvasLayer.prototype._init = function (data) {
const that = this;
// 初始化canvas图层
that._canvas = document.createElement('canvas');
that._canvas.setAttribute('id', 'mapcanvas');
var w = that._map.getCanvas().width,
h = that._map.getCanvas().height;
that._canvas.setAttribute('width', w);
that._canvas.setAttribute('height', h);
that._map.getCanvasContainer().appendChild(that._canvas);
that._canvas.style.display = 'none';
that._ctx = that._canvas.getContext('2d');
that._map.addSource('canvas-source', {
type: 'canvas',
canvas: 'mapcanvas',
coordinates: that._getMapExtent(),
animate: true
});
that._map.addLayer({
id: 'canvas-layer',
type: 'raster',
source: 'canvas-source'
});
that._map.on('movestart', function () {
that._ctx.clearRect(0, 0, that._canvas.width, that._canvas.height);
});
that._map.on('zoomstart', function () {
that._ctx.clearRect(0, 0, that._canvas.width, that._canvas.height);
});
that._map.on('moveend', function () {
that._map.getSource('canvas-source').setCoordinates(that._getMapExtent());
that._showData2Map();
});
// 如果为url,获取数据
if(typeof data === 'string') {
$.ajax({
type:"get",
url : data,
async: false,
success: function (res) {
that._data = res;
that._showData2Map();
}
})
}
}
/**
* 16位转换为rgba
* @param color
* @param opacity
* @returns {string}
* @private
*/
CanvasLayer.prototype._convertHexToRGB = function (color, opacity = 1) {
if (color.length === 4) {
let extendedColor = "#"
for (let i = 1; i < color.length; i++) {
extendedColor += color.charAt(i) + color.charAt(i)
}
color = extendedColor
}
const values = {
r: parseInt(color.substr(1, 2), 16),
g: parseInt(color.substr(3, 2), 16),
b: parseInt(color.substr(5, 2), 16),
}
return `rgba(${values.r}, ${values.g}, ${values.b}, ${opacity})`;
}
/**
* 获取地图的四至
* @returns {[[*, *], [*, *], [*, *], [*, *]]}
* @private
*/
CanvasLayer.prototype._getMapExtent = function () {
var xmin = this._map.getBounds().getWest(),
xmax = this._map.getBounds().getEast(),
ymin = this._map.getBounds().getSouth(),
ymax = this._map.getBounds().getNorth();
return [
[xmin, ymax],
[xmax, ymax],
[xmax, ymin],
[xmin, ymin]
];
}
/**
* 将地理坐标转换为屏幕坐标
* @param coords
* @returns {*}
* @private
*/
CanvasLayer.prototype._map2pixel = function (coords) {
const that = this;
return that._map.project(coords);
}
/**
* 将屏幕坐标转换为地理坐标
* @param pixel
* @returns {*}
* @private
*/
CanvasLayer.prototype._pixel2map = function (pixel) {
const that = this;
return that._map.unproject(pixel);
}
/**
* 将数据展示到地图上
* @private
*/
CanvasLayer.prototype._showData2Map = function () {
const that = this;
const features = that._data.features;
const geomType = features[0].geometry.type;
switch (geomType) {
case "Point": {
that._showPoint2Map();
break;
}
case "MultiPoint": {
that._showMultiPoint2Map();
break;
}
case "LineString": {
that._showLine2Map();
break;
}
case "MultiLineString": {
that._showMultiLine2Map();
break;
}
case "Polygon": {
that._showPolygon2Map();
break;
}
case "MultiPolygon": {
that._showMultiPolygon2Map();
break;
}
}
}
简单点的绘制方式我们分两种:圆形和方形,包括填充和边框。实现代码如下:
/**
* 在地图上打点
* @private
*/
CanvasLayer.prototype._showPoint2Map = function () {
const that = this;
const features = that._data.features;
for (let i = 0; i < features.length; i++) {
const coords = features[i].geometry.coordinates;
that._drawPoint(coords);
}
}
/**
* 在地图上打多点
* @private
*/
CanvasLayer.prototype._showMultiPoint2Map = function () {
const that = this;
const features = that._data.features;
for (let i = 0; i < features.length; i++) {
const coordinates = features[i].geometry.coordinates;
for (let j = 0; j < coordinates.length; j++) {
const coords = coordinates[j];
that._drawPoint(coords);
}
}
}
/**
* 绘制点
* @param coords
* @private
*/
CanvasLayer.prototype._drawPoint = function (coords) {
const that = this;
const pixel = that._map2pixel(coords);
const x = pixel.x, y = pixel.y;
const pointStyle = {
type: 'circle', // circle, rect
size: 5,
color: '#00f',
opacity: 1,
borderWidth: 2,
borderColor: '#ff0000',
borderOpacity: 0.5
};
const size = pointStyle.size;
const type = pointStyle.type;
that._ctx.beginPath();
type === 'rect'
? that._ctx.rect(x - size, y - size, size * 2, size * 2)
: that._ctx.arc(x, y, size, 0, Math.PI * 2, true);
that._ctx.closePath();
// 边框
if(pointStyle.borderWidth) {
that._ctx.strokeStyle = that._convertHexToRGB(pointStyle.borderColor, pointStyle.borderOpacity);
that._ctx.lineWidth = pointStyle.borderWidth + size;
that._ctx.stroke();
}
// 填充
if(pointStyle.color) {
that._ctx.fillStyle = that._convertHexToRGB(pointStyle.color, pointStyle.opacity);
that._ctx.fill();
}
}
实现后效果如下:
为了更加灵活,本文讲述sprite
图标的绘制方式。sprite
图标参考了mapboxGL的实现方式,分为两个文件:.png
和.json
,示例图标如下:
/**
* 在地图上展示图标
* @private
*/
CanvasLayer.prototype._showIcon2Map = function () {
const that = this;
const img = new Image();
img.src = "/mapbox-lecture/lecture/data/sprite.png";
img.onload = function () {
$.ajax({
type: 'get',
url: '/mapbox-lecture/lecture/data/sprite.json',
success: function (res){
that._iconStyle.img = img;
that._iconStyle.data = res;
const features = that._data.features;
for (let i = 0; i < features.length; i++) {
const coords = features[i].geometry.coordinates;
that._drawIcon(coords);
}
}
})
}
}
CanvasLayer.prototype._drawIcon = function (coords) {
const that = this;
const pixel = that._map2pixel(coords);
const x = pixel.x, y = pixel.y;
const iconStyle = that._iconStyle;
const iconData = iconStyle.data[iconStyle.icon];
const size = [iconData.width * iconStyle.size, iconData.height * iconStyle.size]
that._ctx.drawImage(iconStyle.img,
iconData.x, iconData.y,
iconData.width, iconData.height,
x - size[0] / 2, y - size[1] / 2,
size[0], size[1]
);
}