最终效果
一、需求
最近公司有这样一个需求,指定一个区域根据一个距离测算需要开放多少个门店才能覆盖整个指定区域(暂不考虑人口密集、山区等因素——大概估算);因此稍微了解了一下,高德地图的API,记录一下常用高德地图进行定位、标点(自定义标点)、测距、绘制矢量图(圆、多边形等)、根据经纬度获取中文地址、根据地址获取经纬度等等功能。
二、前期准备,需申请key及安全秘钥
其步骤可自行百度
三、实际项目使用(适用于vue2与vue3项目)
以下实现的效果都是在index.html中引入的方式来实现的
也可以使用:@amap/amap-jsapi-loader 或 vue-amap
1、index.html页面的<head>标签中添加以下代码
<!-- 解决逆地理编码,根据经纬度获取地址信息 -->
<script type="text/javascript">
window._AMapSecurityConfig = {
securityJsCode: '所申请的安全密钥', //所申请的安全密钥 注意这是安全密钥而不是key
}
</script>
<!--同步加载AMap-->
<script
src='https://webapi.amap.com/maps?v=2.0&key=自己申请的key&&plugin=AMap.Scale,AMap.HawkEye,AMap.ToolBar,AMap.ControlBar,AMap.PlaceSearch,AMap.DistrictSearch,AMap.HeatMap,AMap.3DTilesLayer,AMap.IndoorMap,AMap.MoveAnimation,AMap.ElasticMarker,AMap.MarkerCluster,AMap.IndexCluster,AMap.MouseTool,AMap.BezierCurveEditor,AMap.RectangleEditor,AMap.CircleEditor,AMap.EllipseEditor,AMap.GeoJSON,AMap.PolylineEditor,AMap.PolygonEditor,AMap.AutoComplete,AMap.Driving,AMap.Walking,AMap.Riding,AMap.Transfer,AMap.Geocoder,AMap.GraspRoad,AMap.StationSearch,AMap.LineSearch,AMap.ArrivalRange,AMap.CitySearch,AMap.Geolocation,AMap.Weather,AMap.RangingTool'></script>
2、实现具体功能
1、左击获取经纬度,并根据经纬度回显地址
<template>
<t-layout-page class="map_localization">
<div id="container"></div>
<div class="input-item">
<div class="title">左击获取经纬度</div>
<el-input v-model="lnglat">
<template slot="prepend">经纬度</template>
</el-input>
<el-input style="margin-top:5px;" v-model="address">
<template slot="prepend">地址</template>
</el-input>
</div>
</t-layout-page>
</template>
<script>
export default {
name: 'MapLocalization',
data() {
return {
map: null,
lnglat: '113.276517,23.151382',
address: ''
}
},
mounted() {
//调用地图初始化方法
this.initAMap()
this.regeoCode()
},
methods: {
initAMap() {
this.map = new AMap.Map('container', {
resizeEnable: true,
zoom: 15,
center: [113.276517, 23.151382]
});
//使用CSS默认样式定义地图上的鼠标样式
this.map.setDefaultCursor("pointer");
//绑定地图点击事件
this.map.on('click', (e) => {
this.lnglat = e.lnglat.getLng() + ',' + e.lnglat.getLat();
this.regeoCode()
})
},
// 逆地理编码,根据经纬度获取地址信息
regeoCode() {
this.map.clearMap();
let lnglat = this.lnglat.split(',');
let marker = new AMap.Marker();
let geocoder = new AMap.Geocoder({
city: "020", //城市设为广州,默认:“全国”
radius: 1000, //范围,默认:500
// extensions: 'all'
});
this.map.add(marker);
marker.setPosition(lnglat);
geocoder.getAddress(lnglat, (status, result) => {
if (status === 'complete' && result.regeocode) {
this.address = result.regeocode.formattedAddress;
} else {
this.$message.error('根据地址查询位置失败,请重新选择!');
}
});
}
}
}
</script>
<style lang="scss" scoped>
.map_localization {
position: relative;
#container {
width: 100%;
height: 100%;
}
.input-item {
position: absolute;
background: white;
padding: 15px;
right: 30px;
bottom: 10px;
.title {
font-weight: bold;
font-size: 16px;
margin-bottom: 10px;
}
}
}
</style>
2、测距—默认样式测距、自定义样式测距
<template>
<t-layout-page class="map_localization">
<div id="container"></div>
<div class="cursor-item">
<div class="title">测距</div>
<el-button round size="mini" @click="defaultStyle"
>默认样式测距</el-button
>
<el-button round size="mini" @click="customStyle"
>自定义样式测距</el-button
>
</div>
</t-layout-page>
</template>
<script>
export default {
name: "MapLocalization",
data() {
return {
map: null,
ruler1: null,
ruler2: null
};
},
mounted() {
//调用地图初始化方法
this.initAMap();
},
methods: {
initAMap() {
this.map = new AMap.Map("container", {
resizeEnable: true,
zoom: 15,
center: [113.276517, 23.151382]
});
//默认样式
this.ruler1 = new AMap.RangingTool(this.map);
//自定义样式
let startMarkerOptions = {
icon: new AMap.Icon({
size: new AMap.Size(19, 31), //图标大小
imageSize: new AMap.Size(19, 31),
image: "//webapi.amap.com/theme/v1.3/markers/b/start.png"
}),
offset: new AMap.Pixel(-9, -31)
};
let endMarkerOptions = {
icon: new AMap.Icon({
size: new AMap.Size(19, 31), //图标大小
imageSize: new AMap.Size(19, 31),
image: "//webapi.amap.com/theme/v1.3/markers/b/end.png"
}),
offset: new AMap.Pixel(-9, -31)
};
let midMarkerOptions = {
icon: new AMap.Icon({
size: new AMap.Size(19, 31), //图标大小
imageSize: new AMap.Size(19, 31),
image: "//webapi.amap.com/theme/v1.3/markers/b/mid.png"
}),
offset: new AMap.Pixel(-9, -31)
};
let lineOptions = {
strokeStyle: "solid",
strokeColor: "#FF33FF",
strokeOpacity: 1,
strokeWeight: 2
};
let rulerOptions = {
startMarkerOptions: startMarkerOptions,
midMarkerOptions: midMarkerOptions,
endMarkerOptions: endMarkerOptions,
lineOptions: lineOptions
};
this.ruler2 = new AMap.RangingTool(this.map, rulerOptions);
//使用CSS默认样式定义地图上的鼠标样式
this.map.setDefaultCursor("pointer");
},
//默认样式测距
defaultStyle() {
this.ruler2.turnOff();
this.ruler1.turnOn();
},
//自定义样式测距
customStyle() {
this.ruler1.turnOff();
this.ruler2.turnOn();
}
}
};
</script>
<style lang="scss" scoped>
.map_localization {
position: relative;
#container {
width: 100%;
height: 100%;
}
.cursor-item {
position: absolute;
background: white;
padding: 5px 20px 10px;
right: 340px;
bottom: 170px;
.title {
font-weight: bold;
font-size: 16px;
margin-bottom: 10px;
}
.mouseStyle {
display: block;
padding: 5px 20px 10px;
}
}
}
</style>
3、绘制矢量图(圆、多边形等)
1、绘制多边形
1、效果
2、关键代码
selectedOptions是选择省市区的编码数组
let keyword;
if (this.selectedOptions.length > 0) {
keyword = this.selectedOptions[this.selectedOptions.length - 1];
}
// 创建行政区查询对象
let district = new AMap.DistrictSearch({
// 返回行政区边界坐标等具体信息
extensions: "all"
// subdistrict: 1
// 设置查询行政区级别为 区
level: 'district'
});
district.search(keyword, (status, result) => {
if (result.districtList) {
// 获取边界信息
let bounds = result.districtList[0].boundaries;
let polygons = [];
if (bounds) {
for (let i = 0, l = bounds.length; i < l; i++) {
//生成行政区划polygon
let polygon = new AMap.Polygon({
map: this.map,
strokeWeight: 1,
path: bounds[i],
fillOpacity: 0.7,
fillColor: "#CCF3FF",
strokeColor: "#CC66CC"
});
polygons.push(polygon);
}
// 地图自适应
this.map.setFitView();
}
} else {
this.map.setFitView();
}
});
2、绘制圆
1、效果
2、关键代码
this.circles是后台提供绘制圆形的经纬度数据
// 添加Circle
addCircle() {
this.circleList = [];
for (let i = 0; i < this.circles.length; i += 1) {
let center = this.circles[i].center;
let circle = new AMap.Circle({
center: center,
radius: ((this.radius - 0) * 1000) / 2, // 半径
strokeColor: "#F33", // 线颜色
// strokeColor: this.rgb(), // 线颜色
zIndex: 101,
strokeOpacity: 1, //线透明度
strokeWeight: 3, //线粗细度
bubble: true, // 是否将覆盖物的鼠标或touch等事件冒泡到地图上
fillColor: "#7c7cfb", //填充颜色
fillOpacity: 0.35 //填充透明度
});
circle.setMap(this.map);
this.circleList.push(circle);
}
},
// 动态显示和隐藏圆
changeShowCircle() {
if (this.isShowCircle) {
this.circleList.map(item => {
item.show();
});
} else {
this.circleList.map(item => {
item.hide();
});
}
},
四、vue3+vite+ts项目打包需要解决以下问题:
1、问题
因为是index.html页面的<head>标签中引入的高德地图会出现
1、new AMap在ts语法报错 且打包时也报错
2、需要用window定义AMap报错
2、解决办法
问题2解决办法在.d.ts文件中加上
declare interface Window {
AMap: any;
}
问题1的解决办法,在utils下新建一个map.ts文件
export function MapLoader() {
return new Promise(resolve => {
if (window.AMap) {
resolve(window.AMap);
}
});
}
页面使用
import { MapLoader } from "@/utils/map";
MapLoader().then((AMap: any) => {
state.map = new AMap.Map("container", {
// resizeEnable: true,
viewMode: "3D",
zoom: 12,
center: [113.265862, 23.126124]
});
})
解决vite打包没有webpack externals:{‘AMap’: ‘AMap’}配置
pnpm add -D vite-plugin-resolve-externals
vite.config.ts 文件中引入
import resolveExternalsPlugin from 'vite-plugin-resolve-externals'
plugins: [
...
resolveExternalsPlugin({
AMap: 'AMap'
})
],