Ant Design of Vue + leaflet 地图画点、线、面标绘控件
说明:使用Ant Design ofVue框架搭建leaflet 地图绘制点、线、面控件,直接上图看效果;功能描述:1、在地图上绘制点线面功能;2、可以为绘制符号添加备注;3、显示隐藏绘制的所有符号;4、可以根据条件查询绘制符号;5、可以编辑符号备注、删除和定位符号;一、搭建vue + leaflet项目1、使用vue-cli 3.0及以上版本的脚手架搭建...
·
说明:使用Ant Design of Vue框架搭建 leaflet 地图绘制点、线、面控件,直接上图看效果;
功能描述:
1、在地图上绘制点线面功能;
2、可以为绘制符号添加备注;
3、显示隐藏绘制的所有符号;
4、可以根据条件查询绘制符号;
5、可以编辑符号备注、删除和定位符号;
一、搭建vue + leaflet项目
1、使用vue-cli 3.0及以上版本的脚手架搭建项目,网上教程很多,这里就不细说了;
2、根据Ant Design of Vue官方文档引入UI框架(优化标签,使界面跟好看点);
3、安装Leaflet的Vue组件 — Vue2Leaflet,npm install vue2-leaflet leaflet --save
4、修改vue项目中的main.js文件,引入css文件和一些图标配置;
// leaflet地图样式
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
/* leaflet icon */
delete L.Icon.Default.prototype._getIconUrl
L.Icon.Default.mergeOptions({
iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'),
iconUrl: require('leaflet/dist/images/marker-icon.png'),
shadowUrl: require('leaflet/dist/images/marker-shadow.png')
})
5、新建vue页面,使用<l-map>标签创建地图,看页面是否显示地图成功;
<template>
<div style="height: 670px;">
<l-map
style="height: 100%; width: 100%"
:zoom="zoom"
:center="center"
ref="map"
>
<l-tile-layer :url="url"></l-tile-layer>
</l-map>
</div>
</template>
<script>
import { LMap, LTileLayer} from 'vue2-leaflet'
import L from 'leaflet'
export default {
components: {
L,
LMap,
LTileLayer,
},
data () {
return {
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
zoom: 3,
center: [47.413220, -1.219482]
}
}
}
</script>
<style>
</style>
二、点、线、面绘制控件和相关功能的实现
实现代码如下
主页面 HomeView.vue
<template>
<div style="height: 670px;">
<l-map
style="height: 100%; width: 100%"
:zoom="zoom"
:center="center"
ref="map"
>
<l-tile-layer :url="url"></l-tile-layer>
<l-control position="topright">
<control-marker :map="map" @perspective="perspective"></control-marker>
</l-control>
</l-map>
</div>
</template>
<script>
import { LMap, LTileLayer, LControl } from 'vue2-leaflet'
import L from 'leaflet'
import ControlMarker from '@/components/Map/ControlMarker'
export default {
components: {
L,
LMap,
LTileLayer,
LControl,
ControlMarker
},
data () {
return {
url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
zoom: 3,
center: [47.413220, -1.219482],
bounds: null,
map: null
}
},
mounted () {
this.map = this.$refs.map.mapObject
},
methods: {
perspective (center) {
this.center = center
}
}
}
</script>
<style>
.control-panel .search .ant-input-search-enter-button .ant-input-group-addon .ant-input-search-button{
font-size: 18px;
color: #5e5656;
background-color: #fff;
border-color: #d9d9d9;
}
.control-panel .ant-table-thead > tr > th, .ant-table-tbody > tr > td{
padding: 12px 14px;
word-break: break-all;
}
.leaflet-container .leaflet-control-attribution, .leaflet-container .leaflet-control-scale{
display: none;
}
.leaflet-marker-markerTooltip{
white-space: nowrap;
float: left;
margin-left: 15px;
margin-top: -5px;
background-color: #FFFFFF;
border: 1px solid #000000;
border-radius: 3px;
box-shadow: 2px 2px 5px #888888;
color: #000000;
padding: 2px 5px;
font-weight: bold;
font-size: 12px;
line-height: 16px;
cursor: default;
}
</style>
符号控件代码ControlMarker.vue
<template>
<div class="control-marker">
<a-button @click="pointMarker" :disabled="tipPanel">
绘制点
</a-button>
<a-button @click="lineMarker" :disabled="tipPanel">
绘制线
</a-button>
<a-button @click="polygonMarker" :disabled="tipPanel">
绘制面
</a-button>
<a-button @click="toggleControlPanel">
标会管理
</a-button>
<div class="control-panel" v-show="controlPanel">
<div class="panel-top">
<span>标绘管理</span>
<a href="javascript:void(0);"><a-icon type="swap" /></a>
<a href="javascript:void(0);" @click="showResultLayer"><a-icon :type="markerShow ? 'eye-invisible' : 'eye'" /></a>
</div>
<div class="select">
类型:
<a-select defaultValue="allTypes" style="width: 150px" @change="handleChange">
<a-select-option value="allTypes">全部</a-select-option>
<a-select-option value="Point">点</a-select-option>
<a-select-option value="LineString">线</a-select-option>
<a-select-option value="Polygon">面</a-select-option>
</a-select>
</div>
<div class="search">
<a-input-search placeholder="" @search="onSearch" enterButton />
</div>
<div class="marker-table">
<a-table
rowKey="projectNo"
:columns="columns"
:dataSource="showMarkerData"
:pagination="ipagination"
@change="handleTableChange"
>
<span slot="operation" slot-scope="text, record" class="operation">
<a title="编辑" href="javascript:;" @click="handleEdit(record)"><a-icon type="form" /></a>
<a-divider type="vertical" />
<a-popconfirm title="确定删除吗?" @confirm="handleDelete(record)">
<a title="删除"><a-icon type="close-circle" /></a>
</a-popconfirm>
<a-divider type="vertical" />
<a title="定位" href="javascript:;" @click="handleLocation(record)"><a-icon type="environment" /></a>
</span>
</a-table>
</div>
</div>
<div class="add-tip" ref="tipModal" v-show="tipPanel">
<div class="title">备注</div>
<a-input placeholder="请输入备注" allowClear v-model="tip" />
<a-button class="ok" @click="tipOk">确定</a-button>
<a-button @click="tipCancel">取消</a-button>
</div>
</div>
</template>
<script>
import L from 'leaflet'
export default {
components: {
L
},
props: {
map: {
type: Object,
required: true
}
},
data () {
return {
tip: '暂无备注',
controlPanel: true,
tipPanel: false,
markerShow: true,
record: null,
selectChange: 'allTypes',
searchContent: null,
markerAdd: null,
tipLocation: null,
allMarkerData: [],
showMarkerData: [],
columns: [{
title: '备注',
dataIndex: 'remark',
width: 160
},
{
title: '操作',
dataIndex: 'operation',
align: 'center',
width: 120,
scopedSlots: { customRender: 'operation' }
}],
ipagination: {
current: 1,
pageSize: 5,
size: 'small'
}
}
},
created () {
// 初始化一些符号属性
this.markersLayer = L.geoJSON([], {
style: function (feature) {
return {
color: '#237CC9',
weight: 2,
fillColor: '#237CC9',
fillOpacity: 0.2
}
}
}).addTo(this.map)
this.currentMarkerGroup = L.layerGroup([]).addTo(this.map)
this.marker_zindex = 2000
this.moveMarker = L.marker([0, 0], {
zIndexOffset: this.marker_zindex
})
this.moveIcon = L.marker([0, 0], {
zIndexOffset: this.marker_zindex
})
},
methods: {
// 绘制点
pointMarker (e) {
this.cancleMarkerListener()
this.clickFunction = this.addClickMarker
this.map.on('click', this.clickFunction, this)
this.moveFunction = this.addMoveMarker
this.map.on('mousemove', this.moveFunction, this)
this.rightClickFunction = this.cancleMarkerListener
this.map.on('contextmenu', this.rightClickFunction, this)
e.stopPropagation()
},
// 绘制点符号添加到图层中
addClickMarker: function (e) {
const clickLocation = [e.latlng.lat, e.latlng.lng]
this.cancleMarkerListener()
const pointMarker = L.marker(clickLocation).addTo(this.markersLayer)
this.addTip(pointMarker)
},
// 添加移动符号事件
addMoveMarker: function (e) {
this.map.getContainer().style.cursor = 'pointer'
this.moveMarker.addTo(this.currentMarkerGroup)
this.moveMarker.setLatLng(e.latlng)
this.moveMarker.setZIndexOffset(this.marker_zindex)
},
// 添加线符号
lineMarker (e) {
this.cancleMarkerListener()
this.poly_points = []
this.poly_line = new L.Polyline([], {
color: '#237CC9',
// opacity:0.6,
weight: 2
})// 折线
this.dashLine = new L.Polyline([], {
color: '#237CC9',
// opacity:0.6,
dashArray: [10, 10],
weight: 2
})
this.clickFunction = this.addLineLatlng
this.map.on('click', this.clickFunction, this)
this.dblclickFunction = this.addLineMarker
this.map.on('dblclick', this.dblclickFunction, this)
this.moveFunction = this.addDashLine
this.map.on('mousemove', this.moveFunction, this)
this.rightClickFunction = this.cancleMarkerListener
this.map.on('contextmenu', this.rightClickFunction, this)
e.stopPropagation()
},
// 添加线绘制点
addLineLatlng (e) {
this.poly_points.push([e.latlng.lat, e.latlng.lng])
},
// 添加线绘制结束点
addLineMarker (e) {
this.cancleMarkerListener()
this.map.removeLayer(this.dashLine)
if (this.poly_points.length > 2) {
this.poly_line.setLatLngs(this.poly_points).addTo(this.markersLayer)
return this.addTip(this.poly_line, this.poly_points[this.poly_points.length - 1])
}
},
// 在绘制线鼠标移动时显示虚线效果
addDashLine (e) {
this.map.getContainer().style.cursor = 'pointer'
this.moveIcon.addTo(this.currentMarkerGroup)
if (this.poly_points.length > 0) {
this.dashLine.setLatLngs(this.poly_points)
this.dashLine.addLatLng([e.latlng.lat, e.latlng.lng]).addTo(this.currentMarkerGroup)
this.moveIcon.setIcon(L.divIcon({ html: "<div class='leaflet-marker-markerTooltip'>双击添加备注</div>", className: 'leaflet-marker-noDefaultDivIcon' }))
} else {
this.moveIcon.setIcon(L.divIcon({ html: "<div class='leaflet-marker-markerTooltip'>点击开始绘制,双击结束</div>", className: 'leaflet-marker-noDefaultDivIcon' }))
}
this.moveIcon.setLatLng(e.latlng)
this.moveIcon.setZIndexOffset(this.marker_zindex + 20)
},
// 为绘制区域添加绘制事件
polygonMarker (e) {
this.cancleMarkerListener()
this.poly_points = []// 区域点
this.poly_area = new L.Polygon([], {
color: '#237CC9',
// opacity:0.6,
weight: 2,
fillColor: '#237CC9',
fillOpacity: 0.2
})
this.poly_now = new L.Polygon([], {
color: '#237CC9',
// opacity:0.6,
weight: 2,
dashArray: [10, 10],
fillColor: '#237CC9',
fillOpacity: 0.3
})
this.clickFunction = this.addPolyLatlng
this.map.on('click', this.clickFunction, this)
this.dblclickFunction = this.addPloygonMarker
this.map.on('dblclick', this.dblclickFunction, this)
this.moveFunction = this.addPolyNow
this.map.on('mousemove', this.moveFunction, this)
this.rightClickFunction = this.cancleMarkerListener
this.map.on('contextmenu', this.rightClickFunction, this)
e.stopPropagation()
},
// 添加区域绘制点
addPolyLatlng (e) {
this.poly_points.push([e.latlng.lat, e.latlng.lng])
},
// 添加区域绘制结束点
addPloygonMarker (e) {
this.cancleMarkerListener()
this.map.removeLayer(this.poly_now)
if (this.poly_points.length > 2) {
this.poly_area.setLatLngs(this.poly_points).addTo(this.markersLayer)
var tipLocation = this.poly_points[this.poly_points.length - 1]
return this.addTip(this.poly_area, tipLocation)
}
},
// 在绘制区域鼠标移动时显示虚线效果
addPolyNow (e) {
this.map.getContainer().style.cursor = 'pointer'
this.moveIcon.addTo(this.currentMarkerGroup)
if (this.poly_points.length > 0) {
this.poly_now.setLatLngs(this.poly_points)
this.poly_now.addLatLng([e.latlng.lat, e.latlng.lng]).addTo(this.currentMarkerGroup)
this.moveIcon.setIcon(L.divIcon({ html: "<div class='leaflet-marker-markerTooltip'>双击添加备注</div>", className: 'leaflet-marker-noDefaultDivIcon' }))
} else {
this.moveIcon.setIcon(L.divIcon({ html: "<div class='leaflet-marker-markerTooltip'>点击开始绘制,双击结束</div>", className: 'leaflet-marker-noDefaultDivIcon' }))
}
this.moveIcon.setLatLng(e.latlng)
this.moveIcon.setZIndexOffset(this.marker_zindex + 20)
},
// 添加绘制符号的备注信息
addTip (markerAdd, tipLocation) {
this.tipPanel = true
this.markerAdd = markerAdd
this.tipLocation = tipLocation
return this.$refs.tipModal
},
// 添加备注信息时备注框确定按钮
tipOk () {
this.tipPanel = false
// 判读是新建符号,还是修改符号
if (this.markerAdd._popup) {
this.bindPopupToMarker(this.markerAdd, this.tip, this.tipLocation)
this.record.remark = this.tip
this.record.marker.openPopup()
} else {
this.bindPopupToMarker(this.markerAdd, this.tip, this.tipLocation)
var newMarkerData = {}
newMarkerData.geometry = JSON.stringify(this.markerAdd.toGeoJSON().geometry)
newMarkerData.geometryObject = this.markerAdd.toGeoJSON().geometry
newMarkerData.marker = this.markerAdd
newMarkerData.projectNo = this.uuid()
newMarkerData.remark = this.tip
if (this.tipLocation) {
// 把点对象转化为字符串---存数据库
// newMarkerData.tipLocation = this.tipLocation.toString()
newMarkerData.tipLocation = this.tipLocation
}
this.allMarkerData.push(newMarkerData)
this.showMarkerData.push(newMarkerData)
}
},
// 备注框取消按钮
tipCancel () {
// 判断是新增取消还是新增取消
if (!this.markerAdd._popup) {
this.markerAdd.removeFrom(this.markersLayer)
}
this.tipPanel = false
},
// 绑定弹出标记---弹出框
bindPopupToMarker (theMarker, theTip, tipLocation) {
if (tipLocation != null) {
theMarker.bindPopup(theTip, {
autoPan: false,
autoClose: false,
className: 'leaflet-marker-markerTip',
keepInView: false
}).openPopup(L.latLng(tipLocation[0], tipLocation[1]))
// 使备注显示在线或面的结束点位置
// theMarker.off('click')
// theMarker.on('click', function () {
// theMarker.openPopup(L.latLng(tipLocation[0], tipLocation[1]))
// })
} else {
theMarker.bindPopup(theTip, {
autoClose: false,
className: 'leaflet-marker-markerTip'
}).openPopup()
}
},
// 取消标记侦听器
cancleMarkerListener () {
if (this.clickFunction) {
this.map.off('click', this.clickFunction, this)
}
if (this.dblclickFunction) {
this.map.off('dblclick', this.dblclickFunction, this)
}
if (this.moveFunction) {
this.map.off('mousemove', this.moveFunction, this)
}
if (this.rightClickFunction) {
this.map.off('contextmenu', this.rightClickFunction, this)
}
if (this.map.hasLayer(this.currentMarkerGroup)) {
this.currentMarkerGroup.clearLayers()
}
},
// 隐藏所有标绘符号按钮
showResultLayer () {
this.markerShow = !this.markerShow
this.allMarkerData.forEach((markerData) => {
if (this.markerShow) {
// 显示
markerData.marker.addTo(this.markersLayer)
this.bindPopupToMarker(markerData.marker, markerData.remark, markerData.tipLocation)
} else {
// 隐藏
markerData.marker.removeFrom(this.markersLayer)
}
})
},
// 页码设置
handleTableChange (pagination) {
this.queryParam.pageNo = this.ipagination.current = pagination.current
},
// 产生16位随机ID
uuid () {
var s = []
var hexDigits = '0123456789abcdef'
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = '4'
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1)
s[8] = s[13] = s[18] = s[23] = '-'
var uuid = s.join('')
return uuid
},
// 下拉选择框方法
handleChange (value) {
this.selectChange = value
this.onQuery()
},
// 符号面板搜索框搜索事件
onSearch (value) {
// eslint-disable-next-line no-useless-escape
this.searchContent = value.replace(/\ +/g, '')
this.onQuery()
},
// 条件查询绘制符号列表
onQuery () {
this.showMarkerData = []
this.allMarkerData.forEach((markerData) => {
if (this.searchContent) {
if (markerData.geometryObject.type === this.selectChange && markerData.remark.indexOf(this.searchContent) !== -1) {
this.showMarkerData.push(markerData)
} else if (this.selectChange === 'allTypes' && markerData.remark.indexOf(this.searchContent) !== -1) {
this.showMarkerData.push(markerData)
}
} else {
if (markerData.geometryObject.type === this.selectChange) {
this.showMarkerData.push(markerData)
} else if (this.selectChange === 'allTypes') {
this.showMarkerData.push(markerData)
}
}
})
},
// 切换符号控制面板的显示隐藏
toggleControlPanel () {
this.controlPanel = !this.controlPanel
},
// 表格中删除按钮
handleDelete (record) {
const index = this.allMarkerData.indexOf(record)
this.allMarkerData.splice(index, 1)
this.showMarkerData.splice(this.showMarkerData.indexOf(record), 1)
record.marker.removeFrom(this.markersLayer)
},
// 表格中定位按钮
handleLocation (record) {
this.perspective(record)
record.marker.openPopup()
},
// 表格中编辑按钮
handleEdit (record) {
this.perspective(record)
this.tipPanel = true
this.tip = record.remark
this.record = record
this.markerAdd = record.marker
record.marker.openPopup()
},
// 在地图中定位符号视角方法
perspective (record) {
if (record.tipLocation) {
this.$emit('perspective', record.tipLocation)
} else {
this.$emit('perspective', record.marker._latlng)
}
}
}
}
</script>
<style lang="less" scoped>
.add-tip{
width: 160px;
height: 108px;
padding: 5px 10px;
background-color: #fff;
border-radius: 4px;
position: absolute;
top: 0px;
right: 320px;
& .title{
width: 100%;
color: #000;
font-size: 18px;
text-align: center;
}
& .ok{
margin: 5px 12px 0 0;
}
}
.control-panel{
width: 312px;
height: 460px;
background-color: #fff;
border-radius: 4px;
overflow: auto;
position: absolute;
top: 84px;
left: 0px;
& .panel-top{
margin: 10px;
color: rgb(47,192,226);
font-size: 16px;
}
& .panel-top a{
float: right;
color: #000;
margin-right: 20px;
}
& .select{
margin: 10px 0 0 30px;
font-size: 16px;
}
& .search{
margin: 10px;
}
& .search .ant-input-search-enter-button .ant-input-group-addon .ant-input-search-button{
font-size: 16px;
color: #d9d9d9;
background-color: #fff;
border-color: #d9d9d9;
}
& .search .ant-btn-primary{
background-color: #fff;
border-color: #d9d9d9;
}
}
</style>
每个方法的注释已经非常清除了,应该基本都能读懂。
更多推荐
已为社区贡献2条内容
所有评论(0)