一、可視化海量點應用場景
在正文開始之前我先說說我為啥會使用這個技術來實現(xiàn)數(shù)據(jù)的可視化。
事情是這樣的,我接手了一個項目,里面有個需求是在地圖上標記出他們公司的產品的使用分布。我接手的時候呢,我前面的那位大哥是使用marker點覆蓋物,加上for循環(huán)來渲染實現(xiàn)的,可能他在維護這個項目的時候,公司的產品上線的比較少,最多的時候也不超過2000
個,所以通過for循環(huán)marker也沒出現(xiàn)什么卡頓現(xiàn)象。
可到我這里,好家伙,一下子數(shù)據(jù)飆到1w
多,進那個頁面之后直接卡死,瀏覽器直接崩潰了。所以說通過for循環(huán)marker的方式在數(shù)據(jù)量小的時候還可以,在大數(shù)據(jù)面前顯然是不可取的。
在高德官方呢也給出了解決方案,
- 一個是通過MassMarks海量點來解決
- 一個是通過layer加上 Loca.GeoJSONSource的方式處理
二、示例代碼
我這里采用的是第二種方式,這是我根據(jù)官方示例自己在vue項目中實現(xiàn)的demo效果
官網示例:https://lbs.amap.com/demo/loca-v2/demos/cat-icon/icon_traffic
- 可以顯示和影藏點
- 鼠標移動到點上時樣式改為手的樣式
-
點擊點覆蓋物時彈出信息窗體
好了 先不說別的直接上代碼吧,整體代碼給了之后,再分析一下一些細節(jié)注意點。
1. 地圖初始化mixin抽取
- mapinit.vue
import AMapLoader from "@amap/amap-jsapi-loader";
window._AMapSecurityConfig = {
securityJsCode: "xxxxxxxxxxx", // '「申請的安全密鑰」',
};
const mapInit = {
data() {
return {
map: null,
AMap: null,
Loca: null,
msg: "hello",
};
},
methods: {
async initAMap() {
this.AMap = await AMapLoader.load({
key: "xxxxxxxxxxxxxxxxxxxxx", // 申請好的Web端開發(fā)者Key,首次調用 load 時必填
version: "2.0", // 指定要加載的 JSAPI 的版本,缺省時默認為 1.4.15
plugins: [
"AMap.Scale",
"AMap.ToolBar",
"AMap.ControlBar",
"AMap.Geocoder",
"AMap.Marker",
"AMap.CitySearch",
"AMap.Geolocation",
"AMap.AutoComplete",
"AMap.InfoWindow",
], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
Loca: {
// 是否加載 Loca, 缺省不加載
version: "2.0.0", // Loca 版本,缺省 1.3.2
},
});
this.map = new AMap.Map("amapcontainer", {
willReadFrequently: true,
//設置地圖容器id
resizeEnable: true,
zoom: this.zoom, // 地圖顯示的縮放級別
viewMode: "3D", // 使用3D視圖
zoomEnable: true, // 地圖是否可縮放,默認值為true
dragEnable: true, // 地圖是否可通過鼠標拖拽平移,默認為true
doubleClickZoom: true, // 地圖是否可通過雙擊鼠標放大地圖,默認為true
zoom: 11, //初始化地圖級別
center: [116.397428, 39.90923], // 初始化中心點坐標 北京
// mapStyle: "amap://styles/darkblue", // 設置顏色底層
});
},
// 修改鼠標樣式
changeCursor(layer) {
//監(jiān)聽鼠標移動事件,如果移動到圖層上的元素,就改變鼠標樣式為手的樣式
this.map.on("mousemove", (e) => {
let features = layer.queryFeature(e.pixel.toArray());
if (features) {
this.map.setDefaultCursor("pointer");
} else {
this.map.setDefaultCursor("default");
}
});
}
},
mounted() {
//DOM初始化完成進行地圖初始化
// this.initAMap();
},
created() {
}
};
export default mapInit;
2. 地圖點的數(shù)據(jù)準備
高德官方是的數(shù)據(jù):https://a.amap.com/Loca/static/loca-v2/demos/mock_data/events.js
我是把它搞下來,在本地新建了個文件,然后導入使用的
3. demo組件
<template>
<div class="local-container">
<div id="amapcontainer" style="width: 100%; height: 880px"></div>
<div class="button-list">
<el-button type="primary" @click="layer.show()">顯示圖層</el-button>
<el-button type="primary" @click="layer.hide()">隱藏圖層</el-button>
</div>
</div>
</template>
<script>
import mapinit from "@/mixin/mapinit.js";
import events from "@/count/event.js";
console.log('events', events)
export default {
mixins: [mapinit],
data() {
return {
layer: null,
};
},
created() {
//可以直接使用mixin里面定義變量
console.log("create--one", this.msg);
},
methods: {
async mapExtend() {
let loca = new Loca.Container({ //創(chuàng)建一個容器
map: this.map,
});
// let data = this.getTestData()
let data = this.getEventsCollection();
console.log('data', data)
let geo = new Loca.GeoJSONSource({ //數(shù)據(jù)源
data: data,
});
let layer = new Loca.IconLayer({ //圖層
zIndex: 10, //圖層層級
opacity: 1, //透明度
visible: false, //是否顯示
});
let trafficIcons = {
1: 'https://a.amap.com/Loca/static/loca-v2/demos/images/traffic-control.png',
2: 'https://a.amap.com/Loca/static/loca-v2/demos/images/jam.png',
3: 'https://a.amap.com/Loca/static/loca-v2/demos/images/construction.png',
4: 'https://a.amap.com/Loca/static/loca-v2/demos/images/close.png',
5: 'https://a.amap.com/Loca/static/loca-v2/demos/images/fog.png',
0: 'https://a.amap.com/Loca/static/loca-v2/demos/images/accident.png',
};
layer.setSource(geo); //設置數(shù)據(jù)源
layer.setStyle({
unit: 'px',
icon: (index, feature) => {
let data = feature.properties.rawData;
let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
return url;
},
// icon: 'https://a.amap.com/Loca/static/loca-v2/demos/images/traffic-control.png',
iconSize: [40, 40],
offset: [0, -40],
rotation: 0,
})
loca.add(layer);
this.map.on('complete', function () {
setTimeout(function () {
layer.show();
layer.addAnimate({
key: 'offset',
value: [0, 1],
easing: 'Linear',
transform: 500,
random: true,
delay: 9000,
});
layer.addAnimate({
key: 'iconSize',
value: [0, 1],
easing: 'Linear',
transform: 500,
random: true,
delay: 9000,
});
}, 800);
});
loca.animate.start();
layer.show(); //顯示圖層
// 拾取測試
this.map.on('click', (e) => {
const feat = layer.queryFeature(e.pixel.toArray());
console.log('feat', feat);
if (feat) {
layer.setStyle({
unit: 'px',
icon: (index, feature) => {
let data = feature.properties.rawData;
let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
return url;
},
iconSize: (i, feature) => {
if (feature === feat) {
return [60, 60];
}
return [40, 40];
},
});
// 為當前的feat信息,添加一個彈窗
const infoWindow = new this.AMap.InfoWindow({
content: `<div style="border: 1px solid black; padding: 10px; width: 200px; border-radius: 5px;">
<h2 style="margin-bottom: 10px;">個人信息</h2>
<p><strong>姓名:</strong> 張三</p>
<p><strong>年齡:</strong> 25歲</p>
<p><strong>性別:</strong> 男</p>
<p><strong>地址:</strong> 北京市朝陽區(qū)</p>
</div>`,
offset: new this.AMap.Pixel(0, -30),
});
infoWindow.open(this.map, feat.coordinates);
}
});
this.changeCursor(layer);
this.layer = layer;
},
getEventsCollection() {
let _events = events[0].events;
let list = _events.map(e => {
let ll = e.lngLat.split(',');
let arr = [parseFloat(ll[0]), parseFloat(ll[1])] //lng,lat
return {
"type": "Feature",
"properties": {
rawData: e
},
"geometry": {
"type": "Point",
"coordinates": arr
}
}
})
// console.log('list', list)
let data = {
"type": "FeatureCollection",
"features": list,
};
return data;
},
getTestData() {
let list = [];
for (let i = 0; i < 200; i++) {
let fList = [];
for (let j = 0; j < 200; j++) {
fList.push([
Number((115.601 + i * 0.002).toFixed(5)), Number((40.32 + j * 0.002).toFixed(5))
]);
}
list.push({
"type": "Feature",
"properties": {
"name": i,
},
"geometry": {
"type": "MultiPoint",
"coordinates": fList
}
});
}
list.push({
"type": "Feature",
"properties": {
"name": 'HIIII',
},
"geometry": {
"type": "Point",
"coordinates": [115.201, 40.302]
}
})
let data = {
"type": "FeatureCollection",
"features": list,
};
return data;
}
},
async mounted() {
await this.initAMap();
await this.mapExtend();
// console.log('this.map', this.map)
// console.log('this.AMap', this.AMap)
// console.log('this.Loca', this.Loca)
// this.map.setMapStyle('amap://styles/darkblue')
// 設置地圖中心點為北京
this.map.setCenter([116.397428, 39.90923]);
},
};
</script>
<style lang="less" scoped>
.local-container {
position: relative;
width: 100%;
.button-list {
position: absolute;
top: 20px;
left: 20px;
z-index: 100;
}
}
</style>
4. 在頁面中使用
<template>
<div>
<!-- 海量點 -->
<local-container></local-container>
</div>
</template>
<script>
import LocalContainer from "@/components/LocalContainer";
export default {
name: "homeView",
components: { LocalContainer },
}
</script>
<style lang="less" scoped>
</style>
三、核心功能分析
1. 海量點的數(shù)據(jù)處理和圖層的添加。
let loca = new Loca.Container({ //創(chuàng)建一個容器
map: this.map,
});
// let data = this.getTestData()
let data = this.getEventsCollection();
console.log('data', data)
let geo = new Loca.GeoJSONSource({ //數(shù)據(jù)源
data: data,
});
let layer = new Loca.IconLayer({ //圖層
zIndex: 10, //圖層層級
opacity: 1, //透明度
visible: false, //是否顯示
});
layer.setSource(geo); //設置數(shù)據(jù)源
loca.add(layer); //將圖層添加到容器
layer.show(); //顯示圖層
// 處理數(shù)據(jù)的方法
getEventsCollection() {
let _events = events[0].events;
let list = _events.map(e => {
let ll = e.lngLat.split(',');
let arr = [parseFloat(ll[0]), parseFloat(ll[1])] //lng,lat
return {
"type": "Feature",
"properties": {
rawData: e
},
"geometry": {
"type": "Point",
"coordinates": arr
}
}
})
// console.log('list', list)
let data = {
"type": "FeatureCollection",
"features": list,
};
return data;
},
我們從上面的地址拿到的數(shù)據(jù)是這樣的結構
我們需要處理成這樣的結構
這是因為Loca.GeoJSONSource類需要這種的數(shù)據(jù)結構。
這里介紹一下Loca.GeoJSONSource
高德地圖中的Loca.GeoJSONSource
是一個數(shù)據(jù)源類型對象,用于處理和處理GeoJSON數(shù)據(jù)。
GeoJSON是一種面向地理數(shù)據(jù)的開放標準格式,基于JSON(JavaScript對象表示法)。其編碼各種地理數(shù)據(jù)結構,如:點(Point),線(LineString),面(Polygon)等地理數(shù)據(jù)結構。
Loca.GeoJSONSource
的主要用途是處理地圖上的數(shù)據(jù)信息,可以用它來展示各種地理信息數(shù)據(jù),如道路、建筑物、行政區(qū)域等。
使用Loca.GeoJSONSource
首先需要初始化一個Loca.Visual
的對象,然后調用其source
函數(shù),指定數(shù)據(jù)源類型為 GeoJSON數(shù)據(jù)源。如果需要獲取遠程的GeoJSON數(shù)據(jù)則需要在參數(shù)中指定URL,如果需要處理本地的GeoJSON數(shù)據(jù)則需傳入本地GeoJSON對象。
舉例說明:
var layer = new Loca.LineLayer({
map: map,
zIndex: 120,
});
layer.source({
type: 'GeoJSON',
data: 'https://example.com/data.geojson'
});
這里首先創(chuàng)建了一個Loca.LineLayer
對象,并指定了地圖展示對象和圖層層級。然后調用source
方法設置了數(shù)據(jù)源,數(shù)據(jù)源類型為GeoJSON,數(shù)據(jù)路徑為指定的網址。
如果你要處理本地對象的數(shù)據(jù),代碼如下:
var geojson = {
"type":"FeatureCollection",
"features":[{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[123,45]
},
"properties":{}
}]
};
layer.source({
type: 'GeoJSON',
data: geojson
});
這里首先定義了一個符合GeoJSON標準的本地對象geojson
,然后調用source
方法設置了數(shù)據(jù)源,數(shù)據(jù)源類型為GeoJSON,數(shù)據(jù)則直接傳入初始化的geojson
對象。
2. 改變鼠標樣式
// 修改鼠標樣式
changeCursor(layer) {
//監(jiān)聽鼠標移動事件,如果移動到圖層上的元素,就改變鼠標樣式為手的樣式
this.map.on("mousemove", (e) => {
let features = layer.queryFeature(e.pixel.toArray());
if (features) {
this.map.setDefaultCursor("pointer");
} else {
this.map.setDefaultCursor("default");
}
});
},
3. 點擊圖標放大并展示信息
this.map.on('click', (e) => {
const feat = layer.queryFeature(e.pixel.toArray());
console.log('feat', feat);
if (feat) {
layer.setStyle({
unit: 'px',
icon: (index, feature) => {
let data = feature.properties.rawData;
let url = trafficIcons[data.type % Object.keys(trafficIcons).length];
return url;
},
iconSize: (i, feature) => {
if (feature === feat) {
return [60, 60];
}
return [40, 40];
},
});
// 為當前的feat信息,添加一個彈窗
const infoWindow = new this.AMap.InfoWindow({
content: `<div style="border: 1px solid black; padding: 10px; width: 200px; border-radius: 5px;">
<h2 style="margin-bottom: 10px;">個人信息</h2>
<p><strong>姓名:</strong> 張三</p>
<p><strong>年齡:</strong> 25歲</p>
<p><strong>性別:</strong> 男</p>
<p><strong>地址:</strong> 北京市朝陽區(qū)</p>
</div>`,
offset: new this.AMap.Pixel(0, -30),
});
infoWindow.open(this.map, feat.coordinates);
}
});
通過文章來源:http://www.zghlxwxcb.cn/news/detail-552081.html
const feat = layer.queryFeature(e.pixel.toArray());
console.log('feat', feat);
可以獲取到當前點擊的圖層中的點的位置信息文章來源地址http://www.zghlxwxcb.cn/news/detail-552081.html
到了這里,關于高德地圖通過圖層layer實現(xiàn)對海量點的可視化渲染的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!