跟牛老师一起学WEBGIS——WEBGIS实现(绘制点)

2020-12-07 10:35:49 浏览数 (1)

三、WEBGIS实现

后面的课程会以mapboxGLcanvas-source为入口开展,通过一个canvas画布,实现webgis的基础功能,包括:1、展示矢量数据(点、线、面,格式为geojson);2、展示x-y-z切片;3、展示wms服务。webgis基础功能部分,有一个比较核心的是前面的文章里面提到的屏幕坐标和地图坐标的相互转换,在mapboxGL中,可以通过map.project()实现地图坐标转换为屏幕坐标,通过map.unproject()实现屏幕坐标转换为地图坐标。

为方便后面使用,先将公用的部分拿出来,代码如下:

代码语言:javascript复制
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;
        }
    }
}

1、点数据绘制

1.1 绘制简单点

简单点的绘制方式我们分两种:圆形和方形,包括填充和边框。实现代码如下:

代码语言:javascript复制
/**
 * 在地图上打点
 * @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();
    }
}

实现后效果如下:

1.2 绘制图标

为了更加灵活,本文讲述sprite图标的绘制方式。sprite图标参考了mapboxGL的实现方式,分为两个文件:.png.json,示例图标如下:

代码语言:javascript复制
/**
 * 在地图上展示图标
 * @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]
    );
}

0 人点赞