uniapp 微信小程序 绘制地图(秋云uCharts图表组件)

470 阅读3分钟

在用uniapp做项目时,遇到做地图的需求,但是又不想用echarts,于是找到了一个兼容性很高的图表插件uCharts(秋云uCharts图表组件),下面说一下怎么使用:

基本用法可以参考一下官网,说得非常的详细ucharts官网

安装

1.下载插件ext.dcloud.net.cn/plugin?id=2…,用HBuilderx编译器可以直接导入

image.png

2.导入之后会发现uni_modules目录发生了变化,这就是导入的图表组件及其配置

image.png

代码演示

<template>
    <view>
        <view style="height: 400px;">
            <qiun-data-charts type="map" @getIndex="getIndex" :opts="{ extra: { map: { mercator: true } } }" :chartData="chartsDataMap1" />
        </view>
    </view>
</template>
<script>
    import mapdata from '../mockdata/mapdata.json';
    export default {
        data() {
            return {
                chartsDataMap1: {},
            };
	},
        onReady() {
            this.getServerData();
	},
        methods: {
            // 点击事件
            getIndex(e) {
                console.log(e)
                console.log(mapdata.features[e.currentIndex])
            },
            // 地图渲染
            getServerData() {
                this.chartsDataMap1 = {
                    series: mapdata.features
                };
            },
        }
    };
</script>

标点:

参考的这位博主的文章 juejin.cn/post/711281…

找对应ucharts的源码

  1. 打开文件/uni_modules/qiun-data-charts/js_sdk/u-charts/u-charts.jsdrawMapDataPoints函数即绘制地图的函数,直接替换即可。
function drawMapDataPoints(series, opts, config, context) {
    var mapOption = assign({}, {
        border: true,
        mercator: false,
        borderWidth: 1,
        active: true,
        borderColor: '#666666',
        fillOpacity: 0.6,
        activeBorderColor: '#f04864',
        activeFillColor: '#facc14',
        activeFillOpacity: 1
    }, opts.extra.map);
    var coords, point;
    var data = series;
    var bounds = getBoundingBox(data);
    if (mapOption.mercator) {
        var max = lonlat2mercator(bounds.xMax, bounds.yMax)
        var min = lonlat2mercator(bounds.xMin, bounds.yMin)
        bounds.xMax = max[0]
        bounds.yMax = max[1]
        bounds.xMin = min[0]
        bounds.yMin = min[1]
    }
    var xScale = opts.width / Math.abs(bounds.xMax - bounds.xMin);
    var yScale = opts.height / Math.abs(bounds.yMax - bounds.yMin);
    var scale = xScale < yScale ? xScale : yScale;
    var xoffset = opts.width / 2 - Math.abs(bounds.xMax - bounds.xMin) / 2 * scale;
    var yoffset = opts.height / 2 - Math.abs(bounds.yMax - bounds.yMin) / 2 * scale;
    for (var i = 0; i < data.length; i++) {
        context.beginPath();
        context.setLineWidth(mapOption.borderWidth * opts.pix);
        context.setStrokeStyle(mapOption.borderColor);
        context.setFillStyle(hexToRgb(series[i].color, series[i].fillOpacity || mapOption.fillOpacity));
        if (mapOption.active == true && opts.tooltip) {
            if (opts.tooltip.index == i) {
                context.setStrokeStyle(mapOption.activeBorderColor);
                context.setFillStyle(hexToRgb(mapOption.activeFillColor, mapOption.activeFillOpacity));
            }
        }
        var coorda = data[i].geometry.coordinates
        for (var k = 0; k < coorda.length; k++) {
            coords = coorda[k];
            if (coords.length == 1) {
                coords = coords[0]
            }
            for (var j = 0; j < coords.length; j++) {
                var gaosi = Array(2);
                if (mapOption.mercator) {
                    gaosi = lonlat2mercator(coords[j][0], coords[j][1])
                } else {
                    gaosi = coords[j]
                }
                point = coordinateToPoint(gaosi[1], gaosi[0], bounds, scale, xoffset, yoffset)
                if (j === 0) {
                    context.beginPath();
                    context.moveTo(point.x, point.y);
                } else {
                    context.lineTo(point.x, point.y);
                }
            }
            context.fill();
            if (mapOption.border == true) {
                context.stroke();
            }
        }
    }
    const location = opts.location
    const fontSize = 10
    location.forEach(item => {
        // 经纬度转墨卡托坐标
        const mercator = lonlat2mercator('经度', '纬度')
        // 转换为当前经纬对应canvas画布上的坐标
        const point = coordinateToPoint(mercator[1], mercator[0], bounds, scale, xoffset, yoffset);
        // 绘制白点
        context.beginPath();
        context.arc(point.x, point.y, 1, 0, Math.PI * 2, false)
        context.strokeStyle = 'transparent'
        context.fillStyle = 'white'
        context.fill()
        context.closePath();
        context.stroke();
        // 绘制标记icon
        context.beginPath();
        context.moveTo(point.x, point.y);
        context.arc(point.x, point.y - fontSize * 2, fontSize * 1, 45 * Math.PI / 180, 135 * Math.PI / 180,
        true);
        context.lineTo(point.x, point.y);
        context.fillStyle = '#B9AF57';
        context.fill();
        context.closePath();
    })
    if (opts.dataLabel == true) {
        for (var i = 0; i < data.length; i++) {
            var centerPoint = data[i].properties.centroid;
            if (centerPoint) {
                // 记录该省的数量
                let count = opts.data[data[i].properties.name]
                if (mapOption.mercator) {
                    centerPoint = lonlat2mercator(data[i].properties.centroid[0], data[i].properties.centroid[1])
                }
                point = coordinateToPoint(centerPoint[1], centerPoint[0], bounds, scale, xoffset, yoffset);
                let fontSize = data[i].textSize * opts.pix || config.fontSize;
                let fontColor = data[i].textColor || opts.fontColor;
                if (mapOption.active && mapOption.activeTextColor && opts.tooltip && opts.tooltip.index == i) {
                    fontColor = mapOption.activeTextColor;
                }
                let text = data[i].properties.name;
                context.beginPath();
                context.textAlign = 'left'
                context.setFontSize(fontSize)
                context.setFillStyle(fontColor)
                const x = point.x - measureText(text, fontSize, context) / 2

                context.fillText(text, point.x - measureText(text, fontSize, context) / 2, point.y + fontSize / 2);
                context.closePath();
                context.stroke();

                if (count) {
                    context.beginPath()
                    const centerx = point.x - measureText(text, fontSize, context) / 2
                    const centery = point.y + fontSize / 2
                    context.moveTo(centerx, centery)
                    context.arc(centerx, centery - fontSize * 2, fontSize * 1.5, 45 * Math.PI / 180, 135 * Math.PI /
                    180, true)
                    context.lineTo(centerx, centery)
                    context.fillStyle = '#b9af57'
                    context.fill()
                    context.closePath()
                    context.beginPath();
                    context.textAlign = 'center';
                    context.setFontSize(fontSize)
                    context.setFillStyle(data[i].textColor || opts.fontColor)
                    context.fillStyle = '#FFFFFF'
                    // 因为icon大小是固定的,数量太大样式会有问题,如果太多的话显示 99+
                    context.fillText(count < 100 ? count : '99+', centerx, centery - fontSize * 1.5)

                    context.arc(centerx, centery, 1, 0, Math.PI * 2, false)
                    context.strokeStyle = 'transparent'
                    context.fillStyle = 'white'
                    context.fill()
                    context.closePath();
                    context.stroke();
                }
            }
        }
    }
    opts.chartData.mapData = {
        bounds: bounds,
        scale: scale,
        xoffset: xoffset,
        yoffset: yoffset,
        mercator: mapOption.mercator
    }
    drawToolTipBridge(opts, config, context, 1);
    context.draw();
}
  1. 在绘制地图的组件里进行以下处理
<qiun-data-charts type="map" :opts="opts" />
opts: {
    location: [],
    data: {},
    ...
};
// 处理中心点
let points = mapData.features.map(item => item.properties.center)
let pointsCenter = points.map(item => item.join(','))
this.opts.location = pointsCenter
// 处理显示名字
let mapName = mapData.features.map(item => item.properties.name)
for (let key in mapName) {
        this.opts.data[mapName[key]] = 1;
}

image.png

注意:

  1. 秋云的用法非常之简单,以至于很多朋友不会使用echats,他没有echarts中的配置项option={},所以我还是建议少用
  2. 标点,只能标记一个地区一个点,而不能实现一个地区多个点(也许是我比较菜,没有搞出来,但是echarts可以) 3.地图的json文件一定是四层数组的,不能用三层数组

image.png

如有问题,欢迎留言!!