头图

Vue AutoNavi Map API Loca How to use connection line layer and pulse connection line layer

What you need to know and master to read this article:

  1. The normal map generation method is already used
  2. Already know how to load Loca plugin
    If you don't understand, you can check my previous article:
to load data visualization Loca in Vue of AutoNavi map
to use the AutoNavi map API to make a route planning application, showing custom
Description of High German Map API Loca 3D Animation

The final effect achieved:

"Thank you bro for the rocket"

在这里插入图片描述
请添加图片描述

1. Basic knowledge

An example of an official connection line is a connection diagram of a country that has established diplomatic relations with my country
The two data sources used are:

Connection line data for diplomatic relations: https://a.amap.com/Loca/static/static/diplomacy-line.json
Point data of diplomatic relations: https://a.amap.com/Loca/static/static/diplomacy-point.json

1. This view contains four layers:

  1. A text layer containing the names of the provinces AMap.LabelsLayer
  2. A layer Loca.ScatterLayer that displays the animation of the coordinates of each province
  3. A layer containing the coordinates of the target point Loca.ScatterLayer
  4. A layer showing the impulse connectors Loca.PulseLinkLayer

2. What steps are required to make such a view:

  1. Create a new map and load Loca plugin.
  2. Traverse all province data to generate province name layer AMap.LabelsLayer
  3. Traverse all province data and generate province geographic coordinate identification layer Loca.ScatterLayer
  4. Generate the logo layer of the target point Loca.ScatterLayer
  5. Traverse all provincial data, each data contains the data of two coordinate points [including target point] and [current province coordinate point], and generate a pulse connection line according to the two location data. Then generate connecting lines for all provinces
  6. Finally, the animation of Loca can be moved

3. What you need to know to build a pulse line

The process of establishing the pulse line is as follows:

// 建立图层
let pulseLayer = new Loca.PulseLinkLayer(图层参数)
// 设置数据源
pulseLayer.setSource(数据参数)
// 设置样式
pulseLayer.setStyle(样式参数)

4. How to generate the data used

The data received by the Loca layer is in Loca.GeoJSONSource format, and the content format received by this object is like this

let locaLayerData = new Loca.GeoJSONSource({
    data: {
        'type': 'FeatureCollection', // 固定
        'features': [ // 这里就是点的数组
            {
                'type': 'Feature',
                
                // 本例中我们用到的 geometry 有两个格式,一种是 `Point` 一种是 `LineString`,
                'geometry': { 
                    'type': 'Point',
                    'coordinates': [121.504673, 25.046711], // 地理经纬度
                },
            },
        ],
    },
})

This is the geometry structure in LineString format,

'geometry': { 
    'type': 'LineString ',
    'coordinates': [ // 地理经纬度,这里有两个经纬度值
        [121.504673, 25.046711], // 连接线的起点
        [121.504673, 25.046711]  // 连接线的终点
    ], 
},

After understanding the above structure, you can modify and generate data according to your needs.
For example, we get the official province data as follows:

[
  { "name": "北京市", "center": "116.407394,39.904211" },
  { "name": "天津市", "center": "117.200983,39.084158" },
]

We can generate points based on this [coordinate layer layer data]:

computed: {
    // 根据省份地址,生成展示地图需要的格式化数据
    dataPoints(){
        let tempData = GEO_PROVINCE_DATA.map(item => {
            let co = item.center.split(',').map(item => Number(item)) // 将字符串拆分成坐标数组数据
            return {
                "type": "Feature",
                "properties": {"province": item.name},
                "geometry": {
                    "type": "Point", // 点位
                    "coordinates": co
                }
            }
        })
        return {
            "type": "FeatureCollection",
            "features": tempData
        }
    },
    
    // 连接线图层数据
    dataLines(){
        let tempData = GEO_PROVINCE_DATA.map(item => {
            let co = item.center.split(',').map(item => Number(item)) // 将字符串拆分成坐标数组数据
            return {
                "type": "Feature",
                "properties": {"province": item.name},
                "geometry": {
                    "type": "LineString", // 线段
                    "coordinates": [
                        TARGET_POINT, // target location
                        co
                    ]
                }
            }
        })
        return {
            "type": "FeatureCollection",
            "features": tempData
        }
    },
}

Generate geo data when used

const geoDataPoints = new Loca.GeoJSONSource({
    data: this.dataPoints,
})
const geoDataLines = new Loca.GeoJSONSource({
    data: this.dataLines,
})

Load layers based on this data:

// 图层点坐标
let loadLocation = () => {
    setLabelsLayer(this.dataPoints)
    scatterLayer2.setSource(geoDataPoints)
    scatterLayer2.setStyle({
        size: [250000, 250000], // 点的大小
        unit: 'miter',
        animate: true,
        duration: 1000,
        texture: 'https://a.amap.com/Loca/static/static/orange.png',
        // texture: 'https://a.amap.com/Loca/static/static/green.png',
    })
    this.loca.add(scatterLayer2)

    // this.loca.animate.start() // 开始动画
}
loadLocation()
// 连接线图层
let linkLayer = new Loca.LinkLayer({
    zIndex: 20,
    opacity: 1,
    visible: true,
    zooms: [2, 22],
})
let loadLine = () => {
    linkLayer.setSource(geoDataLines)
    linkLayer.setStyle({
        lineColors: ['#ff7514', '#ff0008'],
        height: (index, item) => {
            return item.distance / 2
        },
        smoothSteps: 300
    })
    this.loca.add(linkLayer)
}
loadLine()
// pulse layer
let pulseLayer = new Loca.PulseLinkLayer({
    zIndex: 20,
    opacity: 1,
    visible: true,
    zooms: [2, 22],
})
let loadPulse = () => {
    pulseLayer.setSource(geoDataLinesReverse)
    pulseLayer.setStyle({
        height: (index, item) => {
            return item.distance / 2
        },
        unit: 'meter',
        dash: [40000, 0, 40000, 0],
        lineWidth: function () {
            return [20000, 2000]; // 始末 节点的线段宽度
        },
        // altitude: 1000,
        smoothSteps: 100, // 曲线圆滑度
        speed: function (index, prop) {
            return 1000 + Math.random() * 200000;
        },
        flowLength: 100000,
        lineColors: function (index, feat) {
            return ['rgb(255,221,0)', 'rgb(255,141,27)', 'rgb(65,0,255)'];
        },
        maxHeightScale: 0.3, // 弧顶位置比例
        headColor: 'rgba(255, 255, 0, 1)', // 线段中流动的
        trailColor: 'rgb(255,84,84)',
    })
    this.loca.add(pulseLayer)
}
loadPulse()

After these are over, let the animation move

this.map.on('complete', ()=> {
    this.loca.animate.start()
})

At this point the top view looks like this:
在这里插入图片描述

Add some Loca animations and it looks like this

在这里插入图片描述

Note: Official data and resources used

Provincial capital data: https://a.amap.com/Loca/static/mock/districts.js
请添加图片描述

Center point icon: https://a.amap.com/Loca/static/static/center-point.png 请添加图片描述
Animation point (green): https://a.amap.com/Loca/static/static/green.png
请添加图片描述
Animation point (orange): https://a.amap.com/Loca/static/static/green.png
请添加图片描述

Complete code: replace appID with your own

province.json

[
  { "name": "北京市", "center": "116.407394,39.904211" },
  { "name": "天津市", "center": "117.200983,39.084158" },
  { "name": "河北省", "center": "114.530235,38.037433" },
  { "name": "山西省", "center": "112.562678,37.873499" },
  { "name": "内蒙古自治区", "center": "111.76629,40.81739" },
  { "name": "辽宁省", "center": "123.431382,41.836175" },
  { "name": "吉林省", "center": "125.32568,43.897016" },
  { "name": "黑龙江省", "center": "126.661665,45.742366" },
  { "name": "上海市", "center": "121.473662,31.230372" },
  { "name": "江苏省", "center": "118.762765,32.060875" },
  { "name": "浙江省", "center": "120.152585,30.266597" },
  { "name": "安徽省", "center": "117.329949,31.733806" },
  { "name": "福建省", "center": "119.295143,26.100779" },
  { "name": "江西省", "center": "115.81635,28.63666" },
  { "name": "山东省", "center": "117.019915,36.671156" },
  { "name": "河南省", "center": "113.753394,34.765869" },
  { "name": "湖北省", "center": "114.341745,30.546557" },
  { "name": "湖南省", "center": "112.9836,28.112743" },
  { "name": "广东省", "center": "113.26641,23.132324" },
  { "name": "广西壮族自治区", "center": "108.327546,22.815478" },
  { "name": "海南省", "center": "110.349228,20.017377" },
  { "name": "重庆市", "center": "106.551643,29.562849" },
  { "name": "四川省", "center": "104.075809,30.651239" },
  { "name": "贵州省", "center": "106.70546,26.600055" },
  { "name": "云南省", "center": "102.710002,25.045806" },
  { "name": "西藏自治区", "center": "91.117525,29.647535" },
  { "name": "陕西省", "center": "108.954347,34.265502" },
  { "name": "甘肃省", "center": "103.826447,36.05956" },
  { "name": "青海省", "center": "101.780268,36.620939" },
  { "name": "宁夏回族自治区", "center": "106.259126,38.472641" },
  { "name": "新疆维吾尔自治区", "center": "87.627704,43.793026" },
  { "name": "香港特别行政区", "center": "114.171203,22.277468" },
  { "name": "澳门特别行政区", "center": "113.543028,22.186835" }
]

MapLoca.vue

<template>
    <div class="map-container">
        <div id="container" :style="`height: ${insets.height}px`"></div>
    </div>
</template>

<script>

import AMapLoader from '@amap/amap-jsapi-loader'
import {mapState} from "vuex"
import GEO_PROVINCE_DATA from './province.json'
import LaunchButton from "@/components/LaunnchButton";

let AMap = null

const TARGET_POINT = [121.504673, 25.046711] // 目标坐标 台湾
const DESTINATION_POINT = [110.504673, 28.046711] // 目标坐标

export default {
    name: "MapLoca",
    components: {LaunchButton},
    data() {
        return {
            isLoading: false,
            contentHeight: 400,
            map: null,
            loca: null,
        }
    },
    mounted() {
        this.contentHeight = window.innerHeight

        AMapLoader.load({
            key: "替换成自己申请的高德web app id", // 开发应用的 ID
            version: "2.0",   // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
            plugins: [],
            Loca:{
                version: '2.0.0',
            },
            AMapUI: {             // 是否加载 AMapUI,缺省不加载
                version: '1.1',   // AMapUI 缺省 1.1
                plugins: [],       // 需要加载的 AMapUI ui插件
            },

        }).then(map => {
            AMap = map
            this.map = new AMap.Map('container', {
                viewMode: '3D',
                zoom: 6,
                pitch: 32,
                center: TARGET_POINT,
                mapStyle: 'amap://styles/grey',
                showBuildingBlock: true, // 显示建筑物
                showLabel: false, // 不显示地名什么的
            })


            // 文字图层
            let labelLayer = new AMap.LabelsLayer({
                rejectMapMask: true,
                collision: true,
                animation: true,
            })
            this.map.add(labelLayer)

            this.loca = new Loca.Container({
                map: this.map,
            })



            let scatterLayer2 = new Loca.ScatterLayer({
                zIndex: 10,
                opacity: 0.8,
                visible: true,
                zooms: [2, 22],
            })
            let scatterLayer3 = new Loca.ScatterLayer({
                zIndex: 10,
                opacity: 0.8,
                visible: true,
                zooms: [2, 22],
            })

            let centerPoint = new Loca.GeoJSONSource({
                data: {
                    'type': 'FeatureCollection',
                    'features': [
                        {
                            'type': 'Feature',
                            'geometry': {
                                'type': 'Point',
                                'coordinates': TARGET_POINT,
                            },
                        },
                    ],
                },
            })
            scatterLayer3.setSource(centerPoint)
            scatterLayer3.setStyle({
                size: [300000, 300000],
                unit: 'meter',
                texture: 'https://a.amap.com/Loca/static/static/center-point.png',
            })
            this.loca.add(scatterLayer3)

            let lineGeoMap
            let scatterGeoMap


            let setLabelsLayer = (data) => {
                labelLayer.clear()
                data.features.forEach((item) => {
                    let labelsMarker = new AMap.LabelMarker({
                        name: item.properties.province,
                        position: item.geometry.coordinates,
                        zooms: [2, 22],
                        opacity: 1,
                        zIndex: 10,
                        text: {
                            content: item.properties.province,
                            direction: 'bottom',
                            offset: [0, -5],
                            style: {
                                fontSize: 13,
                                fontWeight: 'normal',
                                fillColor: '#fff',
                            },
                        },
                    })
                    labelLayer.add(labelsMarker)
                })
                labelLayer.add(
                    new AMap.LabelMarker({
                        name: '台湾',
                        position: TARGET_POINT,
                        zooms: [2, 22],
                        opacity: 1,
                        zIndex: 10,
                        rank: 100,
                        text: {
                            content: '台湾',
                            direction: 'bottom',
                            offset: [0, -5],
                            style: {
                                fontSize: 13,
                                fontWeight: 'normal',
                                fillColor: '#fff',
                            },
                        },
                    }),
                )
            }

            const geoDataPoints = new Loca.GeoJSONSource({
                data: this.dataPoints,
            });
            const geoDataLines = new Loca.GeoJSONSource({
                data: this.dataLines,
            });
            const geoDataLinesReverse = new Loca.GeoJSONSource({
                data: this.dataLinesReverse,
            });


            let loadLocation = () => {
                setLabelsLayer(this.dataPoints)
                scatterLayer2.setSource(geoDataPoints)
                scatterLayer2.setStyle({
                    size: [250000, 250000],
                    unit: 'miter',
                    animate: true,
                    duration: 1000,
                    texture: 'https://a.amap.com/Loca/static/static/orange.png',
                    // texture: 'https://a.amap.com/Loca/static/static/green.png',
                })
                this.loca.add(scatterLayer2)

                // this.loca.animate.start() // 开始动画
            }
            loadLocation()

            let linkLayer = new Loca.LinkLayer({
                zIndex: 20,
                opacity: 1,
                visible: true,
                zooms: [2, 22],
            })
            let loadLine = () => {
                linkLayer.setSource(geoDataLines)
                linkLayer.setStyle({
                    lineColors: ['#ff7514', '#ff0008'],
                    height: (index, item) => {
                        return item.distance / 2
                    },
                    smoothSteps: 300
                })
                this.loca.add(linkLayer)
            }
            // loadLine()

            // pulse layer
            let pulseLayer = new Loca.PulseLinkLayer({
                zIndex: 20,
                opacity: 1,
                visible: true,
                zooms: [2, 22],
            })
            let loadPulse = () => {
                pulseLayer.setSource(geoDataLinesReverse)
                pulseLayer.setStyle({
                    height: (index, item) => {
                        return item.distance / 2
                    },
                    unit: 'meter',
                    dash: [40000, 0, 40000, 0],
                    lineWidth: function () {
                        return [20000, 2000]; // 始末 节点的线段宽度
                    },
                    // altitude: 1000,
                    smoothSteps: 100, // 曲线圆滑度
                    speed: function (index, prop) {
                        return 1000 + Math.random() * 200000;
                    },
                    flowLength: 100000,
                    lineColors: function (index, feat) {
                        return ['rgb(255,221,0)', 'rgb(255,141,27)', 'rgb(65,0,255)'];
                    },
                    maxHeightScale: 0.3, // 弧顶位置比例
                    headColor: 'rgba(255, 255, 0, 1)',
                    trailColor: 'rgb(255,84,84)',
                })
                this.loca.add(pulseLayer)
            }
            loadPulse()

            this.animateStart()

            this.map.on('complete', ()=> {
                this.loca.animate.start()
            })

        }).catch(e => {
            console.log(e)
        })
    },

    computed: {
        ...mapState(['insets']),
        // 根据省份地址,生成展示地图需要的格式化数据
        dataPoints(){
            let tempData = GEO_PROVINCE_DATA.map(item => {
                let co = item.center.split(',').map(item => Number(item))
                return {
                    "type": "Feature",
                    "properties": {"province": item.name},
                    "geometry": {
                        "type": "Point", // 点位
                        "coordinates": co
                    }
                }
            })
            return {
                "type": "FeatureCollection",
                "features": tempData
            }
        },
        dataLines(){
            let tempData = GEO_PROVINCE_DATA.map(item => {
                let co = item.center.split(',').map(item => Number(item))
                return {
                    "type": "Feature",
                    "properties": {"province": item.name},
                    "geometry": {
                        "type": "LineString", // 线段
                        "coordinates": [
                            TARGET_POINT, // target location
                            co
                        ]
                    }
                }
            })
            return {
                "type": "FeatureCollection",
                "features": tempData
            }
        },
        dataLinesReverse(){
            let tempData = GEO_PROVINCE_DATA.map(item => {
                let co = item.center.split(',').map(item => Number(item))
                return {
                    "type": "Feature",
                    "properties": {"province": item.name},
                    "geometry": {
                        "type": "LineString", // 线段
                        "coordinates": [
                            co,
                            TARGET_POINT // target location
                        ]
                    }
                }
            })
            return {
                "type": "FeatureCollection",
                "features": tempData
            }
        },
    },
    methods: {
        animateStart(){
            this.loca.viewControl.addAnimates([{
                    center: {
                        value: DESTINATION_POINT, // 动画终点的经纬度
                        control: [TARGET_POINT, DESTINATION_POINT], // 过渡中的轨迹控制点,地图上的经纬度
                        timing: [0.42, 0, 0.4, 1], // 动画时间控制点
                        duration: 5000, // 过渡时间,毫秒(ms)
                    },
                    // 俯仰角动画
                    pitch: {
                        value: 60, // 动画终点的俯仰角度
                        control: [[0, 0], [1, 60]], // 控制器,x是0~1的起始区间,y是pitch值
                        timing: [0, 0, 1, 1], // 这个值是线性过渡
                        duration: 5000,
                    },
                    // 缩放等级动画
                    zoom: {
                        value: 5, // 动画终点的地图缩放等级
                        control: [[0, 8], [1, 5]], // 控制器,x是0~1的起始区间,y是zoom值
                        timing: [0, 0, 1, 1],
                        duration: 8000,
                    },
                    // 旋转动画
                    rotation: {
                        value: -30, // 动画终点的地图旋转角度
                        control: [[0, 0], [1, -30]], // 控制器,x是0~1的起始区间,y是rotation值
                        timing: [0, 0, 1, 1],
                        duration: 8000,
                    }
                }],
                () => {})
        },
        resizeMap() {
            let mapContainer = document.getElementById('container')
            mapContainer.style.height = window.innerHeight + "px"
            mapContainer.style.width = window.innerWidth + "px"
        },
     },
    beforeUnmount() {
        this.loca.destroy() // 需要先销毁 Loca 再销毁 Map
        this.map.destroy() // 销毁地图,释放内存
        this.map = null
    }
}
</script>

<style lang="scss" scoped>
.map-container {
    position: relative;
}

</style>

KyleBing
659 声望18 粉丝

前端,喜欢 Javascript scss,喜欢做一些实用的小工具