Newer
Older
dcms_front / src / components / Map / leafletMap.vue
<template>
  <div style="margin-top: -30px; margin-bottom: -10px;">
    <el-row :gutter="20">
      <el-col v-show="showpoi" :span="8">
        <el-input v-model="keywords" size="small" type="text" placeholder="兴趣点关键字" clearable style="width: 50%" />
        <el-button type="primary" size="small" @click="queryOnMap">查询</el-button>
        <el-button type="info" size="small" @click="clearMap"> 清除地图 </el-button>
      </el-col>

      <el-col :span="8" style="text-align: right">
        <el-radio-group v-model="queryEventSwitch" size="small" @change="switchEventOrComponent">
          <el-radio-button label="1">事件定位</el-radio-button>
          <el-radio-button label="0">部件定位</el-radio-button>
          <!--          <el-radio-button v-show="showpoi" label="2">兴趣点属性</el-radio-button>-->
        </el-radio-group>
      </el-col>

      <el-col v-if="queryEventSwitch === '0'" :span="8" style="text-align: right;">
        <el-select v-model="form.componentId" placeholder="选择部件" clearable value="" size="small">
          <el-option
            v-for="item in compListOpts"
            :key="item.value"
            :label="item.name"
            :value="item.value"/>
        </el-select>
        <el-button type="primary" size="small" @click="checkComponentPoint"> 部件提交 </el-button>
      </el-col>

      <el-col v-if="queryEventSwitch === '1'" :span="8" :offset="0" style="text-align: right;">
        <el-button type="primary" size="small" @click="checkEventPoint"> 事件提交 </el-button>
      </el-col>
    </el-row>

    <div id="map" ref="mapDiv" class="baseMap"/>
  </div>
</template>

<script>
import L from 'leaflet'
import 'leaflet/dist/leaflet.css'
import { mapGetters } from 'vuex'
import { merchantListPage } from '@/api/geoCoding/geoCoding'
import {getPartsTypeName} from "../../utils/constant";

const esri = require('esri-leaflet')

const icon = L.icon({
  iconUrl: require('@/assets/icons/icon-position.png'),
  iconSize: [32, 32],
  iconAnchor: [16, 32],
  popupAnchor: [0, -32]
})

const poiIcon = L.icon({
  iconUrl: require('@/assets/icons/icon-poi.png'),
  iconSize: [16, 16],
  iconAnchor: [8, 16],
  popupAnchor: [0, -16]
})

const deltaLng10 = 0.00002536 // 10米范围经度值偏移量(大约)
const deltaLat10 = 0.00002256 // 10米范围纬度值偏移量(大约)

export default {
  name: 'LeafletMap',
  data() {
    return {
      map: null,
      baseLayer: [],
      markers: [], // 选点
      pois: [], // 兴趣点结果
      layers: {
        bjPoint: '', // 部件点图层
        grid: '', // 单元网格图层
        shopPoint: '' // 商铺图层
      },
      form: {
        longitude: '', // 定位点经度
        latitude: '', // 定位点纬度
        communityId: '', // 社区ID
        communityName: '', // 社区名称
        gridId: '', // 单元网格ID
        componentId: '', // 部件ID
        componentName: '' // 部件名称
      }, // 主页面返回值
      showpoi: true,
      keywords: '', // 查询关键字
      limit: 100, // 兴趣点查询的最大结果数
      showEventPointPopup: true, // 是否显示事部件的popup
      queryEventSwitch: '1', // 1==查询事件;0==查询部件
      compListOpts: [] // 部件选择下拉框option
    }
  },
  computed: {
    ...mapGetters([
      'baseUrl',
      'partsUrl',
      'mapUrl',
      'gridUrl',
      'partsAllUrl'
    ])
  },
  mounted() {
    this.initMap()
  },
  methods: {
    initMap() {
      const map = L.map(this.$refs.mapDiv, {
        minZoom: 14,
        maxZoom: 25,
        center: this.baseConfig.center,
        zoom: this.baseConfig.zoom,
        zoomControl: false,
        attributionControl: false,
        crs: L.CRS.EPSG3857
      })
      map.doubleClickZoom.disable()
      this.map = map // data上需要挂载
      window.map = map

      // 加载图层
      this.addLayers()

      const that = this
      map.on('click', function(e) {
        that.clearMarkers() // 清除定位点,不清除查询出来的兴趣点

        // 加载定位点
        if (that.showEventPointPopup) {
          const pos = L.marker(e.latlng, {
            icon: icon
          })
          that.markers.push(pos)
          that.map.addLayer(pos)

          // 飞到定位点
          let zoom = that.map.getZoom()
          if (zoom < 18) {
            zoom = 18
          }
          that.map.flyTo(e.latlng, zoom)

          // 查询所点的网格
          const query = that.layers.grid.query()
          query.nearby(e.latlng, 10)
          query.run(function(error, featureCollection, response) {
            if (error) {
              console.log(error)
              return
            }
            const result = featureCollection.features[0].properties
            // 赋值
            that.form.longitude = e.latlng.lng
            that.form.latitude = e.latlng.lat
            that.form.gridId = result.GBID
            that.form.communityId = result.GBID.substring(0, 12)
            that.form.communityName = result.Note

            if (that.queryEventSwitch === '1') {
              // 事件定位
              // 弹出事件点位信息
              const popupStr = '<div class="popup-div">' +
                '<div class="popup-title">事件点</div>' +
                '<div class="dashed-line"></div>' +
                '<div class="popup-item"><label>坐标</label>' + that.form.longitude.toFixed(4) + ', ' + that.form.latitude.toFixed(4) + '</div>' +
                '<div class="popup-item"><label>所在社区</label>' + that.form.communityName + '</div>' +
                '<div class="popup-item"><label>网格编号</label>' + that.form.gridId + '</div>' +
                '</div>'

              pos.bindPopup(popupStr).openPopup()
            } else if (that.queryEventSwitch === '0') {
              // 部件定位
              // 1构建部件查询多边形,上下左右四个点
              const latlngs = []
              const p1 = [e.latlng.lat + deltaLat10, e.latlng.lng] // 上方点
              const p2 = [e.latlng.lat, e.latlng.lng + deltaLng10] // 右侧点
              const p3 = [e.latlng.lat - deltaLat10, e.latlng.lng] // 下方点
              const p4 = [e.latlng.lat, e.latlng.lng - deltaLng10] // 左侧点
              latlngs.push(p1)
              latlngs.push(p2)
              latlngs.push(p3)
              latlngs.push(p4)

              const polyline = L.polygon(latlngs)
              const bjQuery = that.layers.bjPoint.query()
              bjQuery.within(polyline)
              bjQuery.run(function(error, featureCollection, response) {
                if (error) {
                  console.log(error)
                  return
                }

                // 查询到结果
                if (featureCollection.features.length > 0) {
                  const result = featureCollection.features
                  // 赋值
                  that.compListOpts = []

                  // 构建弹出窗口信息
                  let popupStr = '<div class="popup-div">' +
                    '<div class="popup-title">部件</div>' +
                    '<div class="dashed-line"></div>' +
                    '<div class="popup-item">坐标[' + e.latlng.lng.toFixed(4) + ', ' + e.latlng.lat.toFixed(4) + ']附近的部件有</div>' +
                    '<div class="popup-item"><label>所在社区</label>' + that.form.communityName + '</div>' +
                    '<div class="popup-item"><label>网格编号</label>' + that.form.gridId + '</div>'

                  result.forEach((item, index) => {
                    const dlCode = item.properties.objid.substring(6, 8)
                    const xlCode = item.properties.objid.substring(8, 10)

                    popupStr += '<div class="popup-subitem"><label style="padding-right: 5px;">部件' + (index + 1) + '</label>' + getPartsTypeName(dlCode, xlCode) + '[' + item.properties.objid + ']</div>'
                    const comp = {}
                    comp.name = '部件' + (index + 1) + ':' + getPartsTypeName(dlCode, xlCode) + '[' + item.properties.objid + ']'
                    comp.value = item.properties.objid
                    that.compListOpts.push(comp)
                  })

                  popupStr += '</div>'
                  pos.bindPopup(popupStr).openPopup()
                } else {
                  that.$message({ message: '没有查询到部件,请重新定位', type: 'warning' })
                }
              })
            }
          })
        }
      })
    },
    addLayers() {
      // 加载天地图底图和标注
      this.baseLayer.push(L.tileLayer(this.baseConfig.mapUrl).addTo(this.map))
      this.baseLayer.push(L.tileLayer(this.baseConfig.labelUrl).addTo(this.map))

      // 加载底图
      esri.dynamicMapLayer({
        url: `${this.baseUrl}${this.mapUrl}`,
        zIndex: 1
      }).addTo(this.map)

      // 初始化部件图层(不分层),待切换到部件定位时加载
      this.layers.bjPoint = esri.featureLayer({
        url: `${this.baseUrl}${this.partsAllUrl}`,
        minZoom: 19
      })

      // 加载网格图层,用于选择网格
      this.layers.grid = esri.featureLayer({
        url: `${this.baseUrl}${this.gridUrl}`
      })
    },
    switchEventOrComponent: function() {
      // 如果选中的是事件定位(1)或部件定位(0),点击地图时弹出事件或者部件的popup;否则(2以上)显示兴趣点结果的属性popup
      this.showEventPointPopup = this.queryEventSwitch < 2
      this.clearPoints()

      if (this.queryEventSwitch === '0') {
        // 加载和显示部件图层
        this.map.addLayer(this.layers.bjPoint)
        // 清空部件选择的列表
        this.compListOpts = []
      } else {
        this.map.removeLayer(this.layers.bjPoint)
      }
    },
    clearPoints() {
      this.clearMarkers()
      this.clearPois()
    },
    clearPois: function() {
      const that = this
      this.pois.forEach(marker => {
        that.map.removeLayer(marker)
      })
    },
    clearMarkers: function() {
      const that = this
      this.markers.forEach(marker => {
        that.map.removeLayer(marker)
      })
    },
    queryOnMap: function() {
      const that = this
      merchantListPage({
        keyword: this.keywords,
        limit: this.limit
      }).then(response => {
        if (response.code === 200) {
          console.log(response)
          this.clearPois() // 清除查询的兴趣点结果,不清除地图选点
          const data = response.data
          if (data.total === 0) {
            this.$message.warning('未找到 "' + that.keywords + '" 的兴趣点,请重新查询')
          } else if (data.total > that.limit) {
            this.$message.warning('"' + that.keywords + '" 的兴趣点结果过多,请重新查询')
          } else {
            let minLng = data.rows[0].pointX
            let minLat = data.rows[0].pointY
            let maxLng = data.rows[0].pointX
            let maxLat = data.rows[0].pointY
            data.rows.forEach(item => {
              const popupStr = '<div class="popup-div">' +
                '<div class="popup-title">兴趣点</div>' +
                '<div class="dashed-line"></div>' +
                '<div class="popup-item"><label>名称</label>' + item.merchantName + '</div>' +
                '<div class="popup-item"><label>详细地址</label>' + item.address + '</div>' +
                '</div>'
              const poi = L.marker([item.pointY, item.pointX], { icon: poiIcon })
              poi.bindPopup(popupStr).openPopup()
              that.pois.push(poi)
              that.map.addLayer(poi)

              // 查找左上角和右下角的坐标
              if (minLng > item.pointX) minLng = item.pointX
              if (minLat > item.pointY) minLat = item.pointY
              if (maxLng < item.pointX) maxLng = item.pointX
              if (maxLat < item.pointY) maxLat = item.pointY
            })

            const topLeft = L.latLng(maxLat, minLng) // 左上角经纬度
            const bottomRight = L.latLng(minLat, maxLng) // 右下角经纬度
            const bounds = L.latLngBounds(topLeft, bottomRight)

            // 定位到矩形
            that.map.fitBounds(bounds)
          }
        }
      })
    },
    // 返回事件定位的结果
    checkEventPoint: function() {
      this.$emit(
        'closeMapQueryDialogByEvent',
        this.form.longitude,
        this.form.latitude,
        this.form.communityId,
        this.form.communityName,
        this.form.gridId)

      this.clearPoints()
    },
    // 返回部件定位的结果
    checkComponentPoint: function() {
      this.$emit(
        'closeMapQueryDialogByComp',
        this.form.longitude,
        this.form.latitude,
        this.form.communityId,
        this.form.communityName,
        this.form.gridId,
        this.form.componentId)

      this.clearPoints()
    },
    clearMap() {
      this.clearPoints()
      // 清除data属性值
      this.keywords = ''
      this.showEventPointPopup = true // 是否显示事部件的popup
      this.queryEventSwitch = '1' // 1==查询事件;0==查询部件
      this.compListOpts = [] // 部件选择下拉框option
      this.form.longitude = '' // 定位点经度
      this.form.latitude = '' // 定位点纬度
      this.form.communityId = '' // 社区ID
      this.form.communityName = '' // 社区名称
      this.form.gridId = '' // 单元网格ID
      this.form.componentId = '' // 部件ID
      this.form.componentName = '' // 部件名称
    }
  }
}
</script>

<style scoped>
  .baseMap {
    height: 65vh;
    width: 100%;
    margin-top: 10px;
    border: 1px solid #DCDCDC;
    border-radius: 4px;
  }
</style>

<style>
  .leaflet-container a.leaflet-popup-close-button {
    top: 5px; /* 修改弹窗关闭按钮样式 */
    right: 10px; /* 修改弹窗关闭按钮样式 */
  }

  .leaflet-popup-content .popup-div {
    font-size: 15px;
    padding: 6px;
  }

  .leaflet-popup-content .popup-title {
    font-weight: bold;
  }

  .leaflet-popup-content .dashed-line {
    border-bottom: 1px dashed #dddddd;
    margin: 10px -10px;
  }

  .leaflet-popup-content .popup-item {
    padding-bottom: 6px;
  }

  .leaflet-popup-content .popup-item label {
    font-weight: bold;
    padding-right: 15px;
  }

  .leaflet-popup-content .popup-subitem {
    padding-bottom: 6px;
    font-size: 12px;
  }
</style>