最近业务中遇到一个需求,要把一些小区的边界画在地图上,而客户是没有坐标提供的,那么就只能自己在地图上将小区边界画出来。这里使用的是高德地图,并没有找到相关的可以直接画边界的功能,所以这里采用 绘制覆盖物 + 覆盖物编辑
来实现效果。
效果可参考线上地址
这里大概解释一下步骤,查看详细代码可以直接跳过
JSAPI Loader
依赖鼠标工具-绘制覆盖物
多边形编辑器
右键菜单
关键字搜索
(非必须)<template>
<div id="app">
<div class="container">
<!-- 地图-底层 -->
<div id="amap" style="width: 100%; height: 100%;"></div>
<div class="search">
<el-input v-model="keywords" placeholder="请输入关键词"></el-input>
<el-button type="primary" style="margin-left: 10px;" @click="onSearch">搜索</el-button>
</div>
<div id="panel"></div>
<div class="input-card">
<div class="input-item" @change="drawOverlay(selectStatus)">
<input type="radio" name='func' value='polyline' v-model="selectStatus"><span class="input-text">画折线</span>
<input type="radio" name='func' value='polygon' v-model="selectStatus"><span class="input-text" style='width:5rem;'>画多边形</span>
</div>
</div>
</div>
<el-dialog title="保存区划" :visible.sync="dialogFormVisible" width="30%">
<el-form :model="form">
<el-form-item label="小区名称" label-width="120px">
<el-select v-model="form.xqbh" placeholder="请选择小区">
<el-option :label="item.name" :value="item.value" v-for="item in xqList" :key="item.value"></el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogFormVisible = false">取 消</el-button>
<el-button type="primary" @click="onConfirm">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getXq, saveLnglat } from '@/apis'
import AMapLoader from '@amap/amap-jsapi-loader'
export default {
name: 'App',
data() {
return {
xqList: [
{
name: '测试小区A',
value: 'A',
},
{
name: '测试小区B',
value: 'B',
},
],
_AMap: null,
amap: null,
placeSearch: null, // 地点查询类实例
keywords: '', // 关键词查询
mouseTool: null, // 鼠标工具插件
contextMenu: null, // 右键菜单
polyEditor: null, // 多边形编辑器
selectStatus: '', // 右下角选择模式
curPath: [], // 当前要保存的区划
dialogFormVisible: false,
form: {
xqbh: '',
},
}
},
mounted() {
this.initAMap()
},
methods: {
// 初始化高德地图
initAMap() {
AMapLoader.load({
key: '9714666324f9aa888a63e7719f0abdfb', // 申请好的Web端开发者Key,首次调用 load 时必填
version: '2.0', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
plugins: ['AMap.MouseTool', 'AMap.PolygonEditor', 'AMap.PlaceSearch'], // 需要使用的的插件列表,如比例尺'AMap.Scale'等
}).then((AMap) => {
this._AMap = AMap
this.amap = new AMap.Map('amap', {
center: [116.400274, 39.905812], //初始中心经纬度
zoom: 14, //地图显示的缩放级别,可以设置为浮点数;若center与level未赋值,地图初始化默认显示用户所在城市范围。
zooms: [5, 20], //地图显示的缩放级别范围, 默认为 [2, 20] ,取值范围 [2 ~ 30]
// rotation: -15, //地图顺时针旋转角度,取值范围 [0-360] ,默认值:0
// pitch: 42, //俯仰角度,默认 0,最大值根据地图当前 zoom 级别不断增大,2D地图下无效 。
// viewMode: '3D', //开启3D视图,默认为关闭
})
this.initArea(AMap)
this.initPlaceSearch(AMap)
this.initContextMenu(AMap)
this.initMouseTool(AMap)
})
},
// 初始化小区边界
initArea(AMap) {
let polygonArray = [
{
xqbh: 'A',
lnglat: [
[121.036125, 31.373269],
[121.038342, 31.373319],
[121.038399, 31.371219],
[121.037822, 31.371249],
[121.037487, 31.371239],
[121.037245, 31.371475],
[121.036183, 31.371574],
],
},
{
xqbh: 'B',
lnglat: [
[121.04289, 31.37323],
[121.044841, 31.373289],
[121.044021, 31.370046],
[121.042682, 31.370007],
[121.042786, 31.371446],
],
},
]
polygonArray.forEach((item) => {
let polygon = new AMap.Polygon({
path: item.lnglat,
fillColor: '#ccebc5',
strokeOpacity: 1,
fillOpacity: 0.5,
strokeColor: '#2b8cbe',
strokeWeight: 1,
strokeStyle: 'dashed',
strokeDasharray: [5, 5],
})
polygon.on('click', () => {
this.polyEditor && this.polyEditor.close()
this.polyEditor = new AMap.PolygonEditor(this.amap, polygon)
this.polyEditor.open()
})
polygon.on('rightclick', (e) => {
this.form.xqbh = item.xqbh
this.curPath = e.target._opts.path
this.contextMenu.open(this.amap, e.lnglat)
})
this.amap.add(polygon)
})
this.amap.setFitView()
},
// 初始化地点查询
initPlaceSearch(AMap) {
this.placeSearch = new AMap.PlaceSearch({
pageSize: 1, // 单页显示结果条数
pageIndex: 1, // 页码
map: this.amap, // 展现结果的地图实例
panel: 'panel', // 结果列表将在此容器中进行展示。
autoFitView: true, // 是否自动调整地图视野使绘制的 Marker点都处于视口的可见范围
})
},
// 初始化右键菜单
initContextMenu(AMap) {
// 创建一个右键菜单实例
this.contextMenu = new AMap.ContextMenu()
//右键放大
this.contextMenu.addItem(
'放大一级',
() => {
this.amap.zoomIn()
},
0
)
//右键放大
this.contextMenu.addItem(
'缩小一级',
() => {
this.amap.zoomOut()
},
0
)
this.contextMenu.addItem(
'保存',
() => {
this.contextMenu.close()
this.dialogFormVisible = true
},
0
)
},
// 初始化鼠标工具
initMouseTool(AMap) {
this.mouseTool = new AMap.MouseTool(this.amap)
this.mouseTool.on('draw', (overlay) => {
// 根据鼠标绘制的覆盖物 生成Polygon实例
let polygon = new AMap.Polygon({
path: overlay.obj._opts.path,
fillColor: '#ccebc5',
strokeOpacity: 1,
fillOpacity: 0.5,
strokeColor: '#2b8cbe',
strokeWeight: 1,
strokeStyle: 'dashed',
strokeDasharray: [5, 5],
})
// 多边形-左键操作
polygon.on('click', () => {
this.polyEditor && this.polyEditor.close()
this.polyEditor = new AMap.PolygonEditor(this.amap, polygon)
this.polyEditor.open()
})
// 多边形-右键操作
polygon.on('rightclick', (e) => {
this.form.xqbh = ''
this.curPath = e.target._opts.path
this.contextMenu.open(this.amap, e.lnglat)
})
this.amap.add(polygon)
this.mouseTool.close(true) //关闭,并清除覆盖物
this.clearRadio()
})
},
// 关键词搜索
onSearch() {
this.placeSearch.search(this.keywords)
},
/**
* 绘制覆盖物
* @param type 类型
* return void
*/
drawOverlay(type) {
switch (type) {
case 'polyline': {
this.mouseTool.polyline({
strokeColor: '#80d8ff',
})
break
}
case 'polygon': {
this.mouseTool.polygon({
fillColor: '#00b0ff',
strokeColor: '#80d8ff',
})
break
}
}
},
// 清空选中状态
clearRadio() {
let radios = document.querySelectorAll("input[name='func']")
radios.forEach((item) => {
if (item.checked) {
item.checked = false
}
})
},
// 确认提交
onConfirm() {
alert(this.curPath)
console.log(this.curPath)
this.dialogFormVisible = false
},
},
}
</script>
<style lang="less" scoped>
#app {
width: 100vw;
height: 100vh;
.container {
position: relative;
width: 100%;
height: 100%;
margin: 0 auto;
.search {
position: absolute;
top: 10px;
left: 10px;
display: flex;
align-items: center;
background-color: #fff;
}
#panel {
position: absolute;
background-color: white;
max-height: 90%;
overflow-y: auto;
top: 10px;
right: 10px;
width: 280px;
}
.input-card {
display: flex;
flex-direction: column;
word-wrap: break-word;
background-color: #fff;
border-radius: 0.25rem;
border-width: 0;
border-radius: 0.4rem;
box-shadow: 0 2px 6px 0 rgb(114 124 245 / 50%);
position: fixed;
bottom: 1rem;
right: 1rem;
-ms-flex: 1 1 auto;
flex: 1 1 auto;
padding: 0.75rem 1.25rem;
.btn {
width: 6rem;
margin: 0 1rem 0 2rem;
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border: 1px solid transparent;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
background-color: transparent;
background-image: none;
color: #25a5f7;
border-color: #25a5f7;
padding: 0.25rem 0.5rem;
line-height: 1.5;
border-radius: 1rem;
-webkit-appearance: button;
cursor: pointer;
}
.input-text {
width: 4rem;
margin-right: 1rem;
}
}
}
}
</style>
完整DEMO发布在Gitee仓库