高德地图可视化
amap-jsapi-loader
基本使用
ts
npm install @amap/amap-jsapi-loader --save
准备容器
ts
准备一个容器 必须要有id
<div id="map"></div>
引入高德地图
ts
import AMapLoader from "@amap/amap-jsapi-loader";
加载地图
ts
const initMap = () => {
AMapLoader.load({
key: "申请到的key", // 申请好的Web端开发者Key,首次调用 load 时必填
version: "2.0", // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
// 需要使用的的插件列表
plugins: [
"AMap.Geocoder", // 逆向地理解码插件
"AMap.Marker", // 点标记插件
"AMap.MapType" // 地图类型
],
AMapUI: {
version: "1.1",
plugins: ["overlay/SimpleMarker"]
}
}).then(AMap => {
const map = new AMap.Map("map", {
resizeEnable: true,
zoom: 10
});
// 地图类型
map.addControl(
new AMap.MapType({
defaultType: 0 //0代表默认,1代表卫星
})
);
// 地图样式
map.setMapStyle("amap://styles/darkblue");
// 绘制标记点点
var markerPoint = new AMap.Marker({
position: [] // 经纬度 自行添加
});
// 点位标注
markerPoint.setLabel({
direction: "bottom",
offset: new AMap.Pixel(0, 5), //设置文本标注偏移量
content: `<div class='info'>${position.name}</div>` //设置文本标注内容
});
map.add(markerPoint);
// 缩放地图到合适的视野级别
map.setFitView();
});
};
初始化
ts
onMounted(async () => {
// DOM初始化完成进行地图初始化
initMap()
});
地图容器样式(增加定位 设置z-index 将地图放在最底部)
ts
#map {
height: 100vh;
width: 100%;
z-index: -99;
position: fixed;
bottom: 0;
}
vue-amap插件
高德官网
ts
https://lbs.amap.com/api/javascript-api/summary/
vue-amap
ts
https://elemefe.github.io/vue-amap/#/zh-cn/introduction/install
基本使用
安装
ts
npm install vue-amap --save
项目导入
ts
需要提前申请高德key,步骤附在最后了
// 高德地图配置
import VueAMap from 'vue-amap' // 引入插件
Vue.use(VueAMap) // 使用插件
VueAMap.initAMapApiLoader({ // 初始化插件
key: '高德申请的key', // 高德key
plugin: ['AMap.Geocoder', 'AMap.PlaceSearch', 'AMap.Geolocation', 'AMap.CitySearch'], // 插件集合,根据自己的需求添加
uiVersion: '1.0.11', // 不加会报错,加上吧
v: '1.4.15' // 默认高德 sdk 版本为 1.4.4
})
// 申请的Web端(JS API)的需要写上下面这段话
window._AMapSecurityConfig = {
securityJsCode: '高德申请的kedy对应的安全密钥' // 高德Web端安全密钥
}
新建一个Map.vue文件
vue
<template>
<div class="Map">
<!-- 地图组件 -->
<el-amap
vid="amapContainer"
class="amapClass"
:zoom="zoom"
:plugin="plugin"
:center="center"
:events="events"
>
<!-- 这里用于点击时的标点 -->
<el-amap-marker
v-for="(marker, index) in markers"
:key="index + '1'"
:position="marker"
/>
<!-- 这里用于特殊位置的标点 -->
<el-amap-marker
v-for="(marker, index) in markerList"
:key="index + '2'"
:position="marker.lnglats"
:icon="getIcon(marker)"
:events="marker.events"
:offset="[-24, -24]"
/>
<!-- 信息窗体 -->
<el-amap-info-window
v-if="currentWindow.visible"
:position="currentWindow.position"
:visible.sync="currentWindow.visible"
:content="currentWindow.content"
:close-when-click-map="false"
:events="currentWindow.events"
:offset="[0, -24]"
/>
</el-amap>
</div>
</template>
<script>
import LED0 from '@/assets/imgs/LED-0.png'
import LED1 from '@/assets/imgs/LED-1.png'
import lamppost0 from '@/assets/imgs/lamppost-0.png'
import lamppost1 from '@/assets/imgs/lamppost-1.png'
import tourGuide0 from '@/assets/imgs/tourGuide-0.png'
import tourGuide1 from '@/assets/imgs/tourGuide-1.png'
export default {
name: 'Map',
props: {
zoom: { // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
type: Number,
default: 14
},
center: {
type: Array,
default: () => [120.147076, 30.245426] // 西湖风景区的经纬度
},
markers: {
type: Array,
default: () => []
},
markerList: {
type: Array,
default: () => []
},
currentWindow: {
type: Object,
default: () => ({
position: [],
visible: false,
content: '测试'
})
}
},
data() {
const self = this
return {
LED0,
LED1,
lamppost0,
lamppost1,
tourGuide0,
tourGuide1,
address: null,
loaded: false,
events: {
init(o) {
console.log('地图初始化', o)
// ... 初始地图的一些操作
},
complete(){
// ... 地图渲染完成后的一些操作
}
// 点击地图
click: self.click
},
// 一些工具插件
plugin: [
{
pName: 'Geocoder',
events: {
init(o) {
console.log('Geocoder', o.getAddress())
}
}
},
{
// 定位
pName: 'Geolocation',
events: {
init(o) {
// o是高德地图定位插件实例
o.getCurrentPosition((status, result) => {
// 会存在失败的时候,官网回答:https://lbs.amap.com/faq/js-api/map-js-api/position-related/43361/
console.log('查询成功定位信息', status, result)
if (result && result.position) {
// 根据经纬度设置坐标 --不想替换掉默认的center的可以先注释
self.$emit('update:center', [result.position.lng, result.position.lat])
// self.$emit('update:markers', [[result.position.lng, result.position.lat]])
// load
self.loaded = true
// 页面渲染好后
self.$nextTick()
}
})
}
}
},
{
// 搜索 --我这里没用到
pName: 'PlaceSearch',
events: {
init(instance) {
console.log('搜索', instance)
}
}
},
{
// 定位城市
pName: 'CitySearch',
events: {
init(o) {
// o是高德地图定位插件实例
o.getLocalCity(function(status, result) {
if (status === 'complete' && result.info === 'OK') {
// 查询成功,result即为当前所在城市信息
console.log('查询成功,当前所在城市信息', result)
}
})
}
}
}
]
}
},
methods: {
// 点击地图获取经纬度和具体位置
click(e) {
const { lng, lat } = e.lnglat
this.$emit('update:center', [lng, lat])
this.$emit('update:markers', [[lng, lat]])
console.log('[lng, lat]', [lng, lat])
// 这里通过高德 SDK 完成。获取具体地址的
const geocoder = new AMap.Geocoder({
radius: 1000,
extensions: 'all'
})
const that = this
geocoder.getAddress([lng, lat], function(status, result) {
if (status === 'complete' && result.info === 'OK') {
if (result && result.regeocode) {
that.address = result.regeocode.formattedAddress
console.log('具体位置:', result.regeocode.formattedAddress)
that.$nextTick()
}
}
that.$emit('getLocation', { lng, lat, address: that.address })
})
},
getIcon(item) {
if (item.mediaTypeName === '灯杆屏') {
return item.status ? this.lamppost0 : this.lamppost1
} else if (item.mediaTypeName === '导览屏') {
return item.status ? this.tourGuide0 : this.tourGuide1
} else return item.status ? this.LED0 : this.LED1
}
}
}
</script>
<style lang="scss" scoped>
/* 指定地图的宽高 */
.amapClass, .Map {
width: 100%;
height: 100%;
}
</style>
关于el-amap-marker的属性
ts
1、position:为marker点的坐标(经纬度),接收数组
2、icon:自定义图标地址
3、events:事件集合对象,click、dblclick、rightclick、mouseover、mouseout等
4、offset:偏移量
其他我没用到的还有:
5、clickable:true允许用户可点击marker点(默认也是true)
6、animation:marker点的动画效果
marker点弹跳效果:设置值为 AMAP_ANIMATION_BOUNCE
7、label:点显示的简略信息
8、content可以是文字字符串也可以是html
ts
events: {
click: (e) => {
// ...点击操作
},
dblclick: (e) => {
// ...双击操作
},
...
}
关于el-amap-info-window信息窗体的属性
静态属性
动态属性
ref 可用方法
ts
提供无副作用的同步帮助方法
函数 返回 说明
$$getInstance() AMap.InfoWindow 获取infoWindow实例
详细见 高德API https://lbs.amap.com/api/javascript-api/reference/infowindow
事件(也是events里的)
使用地图组件
我把使用的页面也全贴上了,但其实search-box里面的东西大家可以忽略,只是针对markerList的查询过滤
vue
<template>
<div class="box">
<div class="search-box">
<el-form ref="queryForm" label-width="auto" class="searchForm" :model="searchForm" style="padding: 16px 0 0;">
<el-form-item label="类型" prop="mediaType">
<el-select v-model="searchForm.mediaType">
<el-option v-for="item in digitalTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="区域" prop="region">
<el-select v-model="searchForm.region" clearable>
<el-option v-for="item in regionOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label-width="20px">
<el-button v-permission="['ResourceDistribution:query']" class="search-btn" icon="el-icon-search" type="primary" @click="getDetail">
查询
</el-button>
<el-button class="reset-btn" icon="el-icon-refresh-left" @click="resetForm('queryForm')">
重置
</el-button>
</el-form-item>
<el-form-item label="设备名称" prop="">
<el-select v-model="searchKey" filterable clearable placeholder="请输入关键字" @change="quickOrientation">
<el-option v-for="item in markerList" :key="item.mediaCode" :label="item.mediaName" :value="item.mediaCode" />
</el-select>
<!-- <el-input id="search" v-model="searchKey" placeholder="请输入设备名称" /> -->
</el-form-item>
<el-form-item label-width="20px" style="width: 100px;">
<el-button v-permission="['ResourceDistribution:query']" class="search-btn" type="primary" @click="quickOrientation">
快速定位
</el-button>
</el-form-item>
</el-form>
<div class="rightType">
<div v-for="item in digitalTypeOptions" :key="item.value" class="iconItem">
<svg-icon v-if="item.label === '灯杆屏'" icon-class="lamppost" />
<svg-icon v-else-if="item.label === '导览屏'" icon-class="tourGuide" />
<svg-icon v-else icon-class="LED" />
<span>{{ item.label }}</span>
</div>
</div>
</div>
<div class="rightBottom">
<span class="successStatus">已启用</span>
<span class="errorStatus">已禁用</span>
</div>
<!-- 地图组件 -->
<Map
:center.sync="center"
:markers.sync="markers"
:zoom.sync="zoom"
:marker-list="markerList"
:current-window="currentWindow"
@getLocation="getLocation"
/>
</div>
</template>
<script>
import Map from '@/components/Map'
import { dictList, deviceFilter } from '@/api/resource'
export default {
name: 'GaodeDituAmap',
components: { Map },
data() {
return {
searchForm: {
mediaType: '',
region: ''
},
searchKey: '',
digitalTypeOptions: [],
regionOptions: [],
currentWindow: {
position: [],
visible: false,
content: '',
events: {}
},
markerList: [],
markers: [],
center: [121.133858, 30.606042],
zoom: 14 // 地图缩放比例。zoom值越小,越宏观、越大,越微观。
}
},
mounted() {
this.getDict()
this.getDetail()
},
methods: {
// 获取标点信息
async getDetail() {
this.currentWindow.visible = false
const that = this
const { data } = await deviceFilter(this.searchForm)
data.forEach(item => {
item.lnglats = [item.longitude, item.latitude]
item.events = {
click(e) {
console.log('点击标点', e)
that.center = item.lnglats
that.currentWindow.content = that.createWinContent(item)
that.currentWindow.position = item.lnglats
that.currentWindow.events = {
close(e) {
console.log('窗体关闭', e)
that.currentWindow.visible = false
}
}
that.currentWindow.visible = true
}
}
})
this.markerList = data
},
/**
* 基础配置信息
*/
async getDict() {
const { data: { area, digitalType }} = await dictList('dictNames=area&dictNames=digitalType')
this.regionOptions = area
this.digitalTypeOptions = digitalType
},
// 重置
resetForm(formName) {
this.$refs[formName].resetFields()
this.zoom = this.$options.data.call(this).zoom
this.getDetail()
},
// 快速定位
quickOrientation() {
if (!this.searchKey) {
this.markers = []
this.currentWindow.visible = false
return
}
const cur = this.markerList.find(f => f.mediaCode === this.searchKey)
if (cur) {
this.center = [cur.longitude, cur.latitude]
// this.markers = [this.center]
this.currentWindow.content = this.createWinContent(cur)
this.currentWindow.position = [cur.longitude, cur.latitude]
this.currentWindow.visible = true
this.zoom = 15
}
},
// 获取经纬度及地理位置
getLocation({ lng, lat, address }) {
console.log('获取到经纬度和地址啦:', lng, lat, address)
},
// 画信息窗
createWinContent(item) {
return `
<div>
<div style="display: flex;margin-bottom: 12px">
${item.src ? `<img src="${item.src}" style="width: 120px;margin-right: 16px;">` : ''}
<div style="display: flex;flex-direction: column;justify-content: space-around;">
<strong style="font-size: 18px">${item.mediaName}</strong>
<div style="margin-top: 6px">
<span style="font-size: 14px;color: #0082FF;padding: 0px 6px;background: rgba(0,130,255,0.15);border-radius: 4px;">${item.mediaTypeName}</span>
<span style="font-size: 14px;color: ${!item.status ? '#46A77B' : '#E30000'};padding: 0px 6px;background: ${!item.status ? '#46A77B26' : '#E3000026'};border-radius: 4px;">${!item.status ? '已启用' : '已禁用'}</span>
</div>
<div style="margin-top: 8px">
<i class="el-icon-location" style="color: #0082FF"></i>
<span style="font-size: 14px;">${item.regionName}</span>
</div>
</div>
</div>
<div style="font-size: 14px;color: #666666;line-height: 20px;">
<span>${item.specification},</span>
<span>${item.supplier},</span>
<span>${item.supplierContact},</span>
<span>${item.supplierPhone}</span>
</div>
</div>`
}
}
}
</script>
<style lang="scss" scoped>
.box {
width: 100%;
height: 100%;
box-sizing: border-box;
padding: 0px;
position: relative;
.search-box {
position: absolute;
background-color: #FFFFFF;
width: calc(100% - 32px);
top: 16px;
left: 16px;
z-index: 999;
border-radius: 4px;
.searchForm .el-form-item {
margin: 0 0px 16px;
}
.tip-box {
width: 100%;
max-height: 260px;
position: absolute;
top: 30px;
overflow-y: auto;
background-color: #fff;
}
.rightType{
position: absolute;
right: 0px;
bottom: -24px;
transform: translateY(100%);
z-index: 999;
background: #FFFFFF;
border-radius: 4px;
padding: 16px;
display: flex;
align-items: center;
flex-direction: column;
.iconItem{
font-size: 14px;
color: #4A4A4A;
display: flex;
align-items: center;
flex-direction: column;
.svg-icon{
width: 24px;
height: 24px;
}
}
.iconItem+.iconItem{
margin-top: 24px;
}
}
}
.rightBottom{
position: absolute;
right: 16px;
bottom: 16px;
z-index: 999;
padding: 16px;
font-size: 14px;
color: #4A4A4A;
background-color: #FFFFFF;
border-radius: 4px;
.successStatus{
margin-right: 24px;
}
.successStatus::before{
content: '';
display: inline-block;
height: 8px;
width: 8px;
background-color: #46A77B;
border-radius: 50%;
margin-right: 5px;
}
.errorStatus::before{
content: '';
display: inline-block;
height: 8px;
width: 8px;
background-color: #FF4747;
border-radius: 50%;
margin-right: 5px;
}
}
}
/* 指定地图的宽高 */
.amapClass {
width: 100%;
height: 100%;
}
</style>
vuemap/vue-amap插件
https://www.npmjs.com/package/@vuemap/vue-amap
ts
// 安装核心库
npm i -S @vuemap/vue-amap
// 安装loca相关库
npm i -S @vuemap/vue-amap-loca
// 安装其他扩展库,主要为threejs相关
npm i -S @vuemap/vue-amap-extra
基本使用
引入@vuemap/vue-amap
ts
// 引入vue-amap
import VueAMap, {initAMapApiLoader} from '@vuemap/vue-amap';
import '@vuemap/vue-amap/dist/style.css';
// 初始化vue-amap
initAMapApiLoader({
// 高德的key
key: 'YOUR_KEY',
securityJsCode: 'securityJsCode', // 新版key需要配合安全密钥使用
//Loca:{
// version: '2.0.0'
//} // 如果需要使用loca组件库,需要加载Loca
});
createApp(App).use(VueAMap)
引入类型 忽略
ts
shims-vue-amap.d.ts
declare module 'vue-amap' {
import { App } from 'vue';
const VueAMap: {
install: (app: App) => void;
initAMapApiLoader: (config: {
key: string;
plugin?: string[];
v?: string;
uiVersion?: string;
}) => void;
};
export default VueAMap;
}