Bläddra i källkod

添加 OpenLayersMap.vue 页面展示 open layers 地图

reghao 7 månader sedan
förälder
incheckning
f1495d8eb5
6 ändrade filer med 789 tillägg och 350 borttagningar
  1. 1 0
      package.json
  2. 4 7
      src/router/map.js
  3. 205 44
      src/views/map/AMap.vue
  4. 2 2
      src/views/map/MapIndex.vue
  5. 577 0
      src/views/map/OpenLayersMap.vue
  6. 0 297
      src/views/map/TrailMap.vue

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "js-cookie": "2.2.0",
     "jsencrypt": "^3.2.1",
     "nprogress": "^0.2.0",
+    "ol": "^6.13.0",
     "prismjs": "^1.25.0",
     "svg-sprite-loader": "^5.0.0",
     "tinymce": "^5.1.0",

+ 4 - 7
src/router/map.js

@@ -1,10 +1,7 @@
-// ********************************************************************************************************************
-// 网盘主页
-// ********************************************************************************************************************
 const MapIndex = () => import('views/map/MapIndex')
 const AMap = () => import('views/map/AMap')
 const PhotoMap = () => import('views/map/PhotoMap')
-const TrailMap = () => import('views/map/TrailMap')
+const OpenLayersMap = () => import('views/map/OpenLayersMap')
 
 export default {
   path: '/map',
@@ -25,9 +22,9 @@ export default {
       meta: { needAuth: false }
     },
     {
-      path: '/map/trail',
-      name: '轨迹地图',
-      component: TrailMap,
+      path: '/map/ol',
+      name: 'OpenLayers地图',
+      component: OpenLayersMap,
       meta: { needAuth: false }
     }
   ]

+ 205 - 44
src/views/map/AMap.vue

@@ -1,38 +1,76 @@
 <template>
-  <div id="container" class="amap-wrapper">
+  <el-container>
+    <el-header height="60">
+      <el-button style="float: right; padding: 5px" type="text" @click="clearCircle">清除圆形</el-button>
+      <el-button style="float: right; padding: 5px" type="text" @click="onPathNavigator">路径巡航</el-button>
+    </el-header>
+    <el-main>
+      <div id="container" class="text item amap-wrapper" />
+    </el-main>
+
     <el-dialog :visible.sync="showPositionDialog" width="30%" center>
       <el-card class="box-card" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
         <div slot="header" class="clearfix">
-          <span>选中的坐标</span>
-          <el-button style="float: right; padding: 10px" type="text" @click="onSavePosition1">保存坐标</el-button>
+          <span>操作</span>
+          <el-button style="float: right; padding: 5px" type="text" @click="onSavePosition">保存坐标</el-button>
+          <el-button v-if="!showInput" style="float: right; padding: 5px" type="text" @click="onDrawCircle">绘制圆形</el-button>
+          <el-button style="float: right; padding: 5px" type="text" @click="onSavePosition">保存坐标2</el-button>
         </div>
         <div class="text item">
-          <span style="color: red">({{ positionForm.lng }}, {{ positionForm.lat }})</span>
+          <el-row>
+            <span style="color: #0a84ff">
+              选中的坐标: <span style="color: red">({{ positionForm.lng }}, {{ positionForm.lat }})</span>
+            </span>
+          </el-row>
+          <el-row>
+            <span v-if="showInput">
+              选择半径(单位米):
+              <el-input-number v-model="radius" :min="0" :max="10000" style="margin-left: 5px" />
+              <el-button style="float: right; padding: 5px" type="text" @click="handleNumChange">确定半径</el-button>
+            </span>
+          </el-row>
         </div>
       </el-card>
     </el-dialog>
-  </div>
+  </el-container>
 </template>
 
 <script>
 import AMapLoader from '@amap/amap-jsapi-loader'
-import { addGeoPosition, getMapMarks } from '@/api/map'
+import { addGeoPosition, getGeoPoint } from '@/api/map'
 
 export default {
-  name: 'Map',
+  name: 'AMap',
   data() {
     return {
       amap: null,
       plugins: ['Scale'],
+      mapCenter: [114.0000, 30.0000],
+      // zoom=16 的比例尺为 100m
+      zoom: 16,
       // zoom=6 的比例尺为 100km
-      zoom: 5,
-      mapCenter: [108.337237, 33.881985],
-      markers: [],
+      zoom1: 6,
+      province: '',
+      provinces: [],
+      city: '',
+      citys: [],
+      district: '',
+      districts: [],
+      path1: [{
+        path: [
+          [116.361904, 39.913423],
+          [116.367904, 39.913423]
+        ]
+      }],
+      path: [],
       showPositionDialog: false,
+      showInput: false,
+      radius: 0,
       positionForm: {
         lng: null,
         lat: null
       },
+      pointArr: [],
       mapKeys: {
         securityJsCode: '983d6ee43bab3edf3693e91508f94aa9',
         key: '7b75ab2839ce68b884c7a682501ea774'
@@ -68,53 +106,160 @@ export default {
         this.map = new AMap.Map('container', {
           viewMode: '2D', // 是否为3D地图模式
           zoom: this.zoom, // 初始化地图级别
-          center: this.mapCenter // 初始化地图中心点位置
+          center: [104.065753, 30.657462] // 初始化地图中心点位置
         })
 
-        // 点击地图事件(获取经纬度)
         this.map.on('click', (e) => {
-          this.positionForm.lng = e.lnglat.getLng()
-          this.positionForm.lat = e.lnglat.getLat()
+          // 获取经纬度
+          var lng = e.lnglat.getLng()
+          var lat = e.lnglat.getLat()
+          this.pointArr = [lng, lat]
+
+          this.positionForm.lng = lng
+          this.positionForm.lat = lat
           this.showPositionDialog = true
         })
 
-        getMapMarks().then(res => {
-          if (res.code === 0) {
-            this.$notify({
-              message: '加载了 ' + res.data.length + ' 条数据',
-              type: 'warning',
-              duration: 1000
+        this.map.on('zoomchange', (e) => {
+          console.log('当前缩放级别: ' + this.map.getZoom())
+        })
+      }).catch((e) => {
+        console.log(e)
+      })
+    },
+    // 轨迹巡航
+    loadPathSimplifier() {
+      AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
+        if (!PathSimplifier.supportCanvas) {
+          alert('当前环境不支持 Canvas!')
+          return
+        }
+        // 创建组件实例
+        var pathSimplifierIns = new PathSimplifier({
+          map: this.map,
+          zIndex: 100, // 图层叠加顺序
+          data: this.path, // 巡航路径
+          // 获取巡航路径中的路径坐标数组
+          getPath: (pathData, pathIndex) => {
+            return pathData.path
+          }
+        })
+        // 创建巡航器
+        var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
+          loop: false, // 是否循环
+          speed: 2000 // 速度(km/h)
+        })
+        pathNavigator.start()
+      })
+    },
+    getDistrict(map) {
+      // 创建行政区查询对象
+      var district = new AMap.DistrictSearch({
+        // 返回行政区边界坐标等具体信息
+        extensions: 'all',
+        // 设置查询行政区级别为 区
+        level: 'street',
+        subdistrict: 3
+      })
+
+      this.zoom = 11
+      const area = '双流'
+      district.search(area, function(status, result) {
+        var bounds = result.districtList[0].boundaries
+        var polygons = []
+        if (bounds) {
+          for (var i = 0, l = bounds.length; i < l; i++) {
+            // 生成行政区划polygon
+            var polygon = new AMap.Polygon({
+              map: map,
+              strokeWeight: 1,
+              path: bounds[i],
+              fillOpacity: 0.7,
+              fillColor: '#CCF3FF',
+              strokeColor: '#CC66CC'
             })
+            polygons.push(polygon)
+          }
 
-            for (const item of res.data) {
-              this.markers.push(new AMap.Marker({
-                position: new AMap.LngLat(item.position.lng, item.position.lat),
-                label: { content: item.title, offset: [0, 0] },
-                extData: { id: item.id }
-              }))
-            }
+          // 地图自适应
+          map.setFitView()
+        }
+      })
+    },
+    drawCircle(pointArr, radius) {
+      // 设置圆形位置
+      // var center = new AMap.LngLat(104.065753, 30.657462)
+      var center = new AMap.LngLat(pointArr[0], pointArr[1])
+      // 设置圆的半径大小
+      // var radius = 1000
+      // 创建圆形 Circle 实例
+      var circle = new AMap.Circle({
+        center: center, // 圆心
+        radius: radius, // 半径
+        borderWeight: 3, // 描边的宽度
+        strokeColor: '#ff3333', // 轮廓线颜色
+        strokeOpacity: 1, // 轮廓线透明度
+        strokeWeight: 1, // 轮廓线宽度
+        fillOpacity: 0.4, // 圆形填充透明度
+        strokeStyle: 'line', // 轮廓线样式
+        strokeDasharray: [10, 10],
+        fillColor: '#1791fc', // 圆形填充颜色
+        zIndex: 50 // 圆形的叠加顺序
+      })
 
-            this.map.add(this.markers)
-          } else {
-            this.$notify({
-              message: res.msg,
-              type: 'warning',
-              duration: 3000
-            })
+      // 圆形 Circle 对象添加到 Map
+      this.map.add(circle)
+      // 根据覆盖物范围调整视野
+      this.map.setFitView([circle])
+
+      // 鼠标移入事件
+      circle.on('mouseover', function() {
+        console.log('鼠标移入')
+      })
+
+      var that = this.map
+      // 引入圆形编辑器插件
+      this.map.plugin(['AMap.CircleEditor'], function() {
+        // 实例化圆形编辑器,传入地图实例和要进行编辑的圆形实例
+        var circleEditor = new AMap.CircleEditor(that, circle)
+        // 开启编辑模式
+        circleEditor.open()
+      })
+    },
+    onPathNavigator() {
+      getGeoPoint().then(resp => {
+        if (resp.code === 0) {
+          const pathList = resp.data
+          if (pathList.length === 0) {
+            return
           }
-        }).catch(error => {
-          this.$notify({
-            message: error.message,
-            type: 'warning',
-            duration: 3000
+
+          this.path = pathList
+          AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
+            if (!PathSimplifier.supportCanvas) {
+              alert('当前环境不支持 Canvas!')
+              return
+            }
+            // 创建组件实例
+            var pathSimplifierIns = new PathSimplifier({
+              map: this.map,
+              zIndex: 100, // 图层叠加顺序
+              data: this.path, // 巡航路径
+              // 获取巡航路径中的路径坐标数组
+              getPath: (pathData, pathIndex) => {
+                return pathData.path
+              }
+            })
+            // 创建巡航器
+            var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
+              loop: false, // 是否循环
+              speed: 2000 // 速度(km/h)
+            })
+            pathNavigator.start()
           })
-        })
-      }).catch((e) => {
-        this.$message.error(e)
+        }
       })
     },
-    onSavePosition1() {
-    },
     onSavePosition() {
       this.showPositionDialog = false
       addGeoPosition(this.positionForm).then(resp => {
@@ -122,6 +267,22 @@ export default {
           this.$message.info('添加坐标失败')
         }
       })
+    },
+    onDrawCircle() {
+      this.showInput = true
+    },
+    handleNumChange() {
+      if (this.radius < 10) {
+        this.$message.error('半径至少应大于 10m')
+        return
+      }
+      this.showInput = false
+      this.showPositionDialog = false
+      console.log(this.pointArr)
+      this.drawCircle(this.pointArr, this.radius)
+    },
+    clearCircle() {
+      this.$message.info('清除图形')
     }
   }
 }

+ 2 - 2
src/views/map/MapIndex.vue

@@ -21,9 +21,9 @@
             <i class="el-icon-map-location" />
             <span slot="title">照片地图</span>
           </el-menu-item>
-          <el-menu-item index="/map/trail">
+          <el-menu-item index="/map/ol">
             <i class="el-icon-map-location" />
-            <span slot="title">轨迹地图</span>
+            <span slot="title">OL地图</span>
           </el-menu-item>
         </el-menu>
       </el-col>

+ 577 - 0
src/views/map/OpenLayersMap.vue

@@ -0,0 +1,577 @@
+<template>
+  <!-- 绘制、编辑、输出GeoJson数据 -->
+  <div id="map" ref="map">
+    <div class="btns">
+      <button @click="drawFeature">绘制</button>
+      <button @click="editorFeature" v-text="editorBtnText" />
+      <button @click="outputJson">输出Json</button>
+      <button @click="measure('distence')">距</button>
+      <button @click="measure('area')">面</button>
+      <button @click="measure('angle')">角</button>
+      <button @click="fullscreen" v-html="textarr" />
+      <button @click="single">坐标</button>
+      <button @click="clear">清除</button>
+    </div>
+  </div>
+</template>
+
+<script>
+import 'ol/ol.css'
+import Map from 'ol/Map'
+import View from 'ol/View'
+import { Vector as VectorSource, OSM, XYZ } from 'ol/source'
+import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
+import { GeoJSON } from 'ol/format'
+// 交互 Select在选择功能时触发。Modify用于修改功能几何的交互。Draw绘制功能几何的交互。
+import { Draw, Modify, Select } from 'ol/interaction'
+
+import Overlay from 'ol/Overlay'
+// 线条几何形状。
+import { LineString } from 'ol/geom'
+// 具有几何和其他属性属性属性的地理特征的矢量对象,类似于像 GeoJSON 这样的矢量文件格式中的特征。
+import Feature from 'ol/Feature'
+// 使用返回的键或
+import { unByKey } from 'ol/Observable'
+// 获取几何形状的球形长度和面积
+import { getLength, getArea } from 'ol/sphere'
+import Style from 'ol/style/Style'
+import Stroke from 'ol/style/Stroke'
+import Fill from 'ol/style/Fill'
+import Circle from 'ol/style/Circle'
+import { fromLonLat } from 'ol/proj'
+// import Icon from "ol/style/Icon";
+// import { transform } from "ol/proj";
+// import Point from "ol/geom/Point";
+
+export default {
+  name: 'OpenLayersMap',
+  data() {
+    return {
+      map: null,
+      vectorLayer: null,
+      vectorSource: null,
+      draw: null,
+      select: null,
+      modify: null,
+      editorBtnText: '编辑',
+      textarr: '全屏',
+      measureType: 'diatence',
+      tipDiv: null,
+      pointermoveEvent: null, // 地图pointermove事件
+      sketchFeature: null, // 绘制的要素
+      geometryListener: null, // 要素几何change事件
+      measureResult: '0' // 测量结果
+    }
+  },
+  mounted() {
+    // 底图使用 OpenStreetMap, 对应 view
+    var baseLayer = new TileLayer({
+      source: new OSM()
+    })
+    // 谷歌地球, 对应 view1
+    var baseLayer1 = new TileLayer({
+      source: new XYZ({
+        url: 'https://www.google.com/maps/vt?lyrs=y&gl=cn&x={x}&y={y}&z={z}',
+        crossOrigin: 'anonymous',
+        wrapX: true
+      })
+    })
+    // 谷歌地图, 对应 view2
+    var baseLayer2 = new TileLayer({
+      source: new XYZ({
+        url: 'http://www.google.cn/maps/vt/pb=!1m4!1m3!1i{z}!2i{x}!3i{y}!2m3!1e0!2sm!3i345013117!3m8!2szh-CN!3scn!5e1105!12m4!1e68!2m2!1sset!2sRoadmap!4e0'
+      })
+    })
+
+    // 矢量图层源
+    this.vectorSource = new VectorSource({
+      wrapX: false
+    })
+    // 矢量图层
+    this.vectorLayer = new VectorLayer({
+      source: this.vectorSource
+    })
+
+    var view = new View({
+      projection: 'EPSG:4326',
+      center: [104.06531800244139, 30.65852484539117],
+      zoom: 10
+    })
+    var view1 = new View({
+      projection: 'EPSG:3857', // 坐标系EPSG:4326或EPSG:3857
+      zoom: 0, // 打开页面时默认地图缩放级别
+      center: fromLonLat([121.5, 25]) // 转到墨卡托坐标系
+    })
+    var view2 = new View({
+      center: [104.06531800244139, 30.65852484539117],
+      zoom: 12,
+      maxZoom: 18,
+      projection: 'EPSG:4326',
+      constrainResolution: true, // 设置缩放级别为整数
+      smoothResolutionConstraint: false // 关闭无级缩放地图
+    })
+
+    this.map = new Map({
+      layers: [baseLayer, this.vectorLayer],
+      target: 'map',
+      view: view
+    })
+  },
+  created() {
+    document.title = 'OpenLayersMap'
+  },
+  methods: {
+    drawFeature() {
+      this.draw = new Draw({
+        source: this.vectorSource,
+        // 用此实例绘制的几何形状类型。
+        type: 'Polygon'
+      })
+      // addInteraction  将给定的交互添加到地图中
+      this.map.addInteraction(this.draw)
+      // 绘制完成
+      this.draw.on('drawend', () => {
+        //  removeInteraction 从地图中删除给定的交互。
+        this.map.removeInteraction(this.draw)
+        this.draw = null
+      })
+    },
+
+    editorFeature() {
+      if (this.editorBtnText === '编辑') {
+        this.editorBtnText = '完成编辑'
+        // 要素选择组件
+        this.select = new Select({
+          // wrapX 将世界水平包裹在草图覆盖物上。
+          wrapX: false
+        })
+        // 要素编辑
+        this.modify = new Modify({
+          // getFeatures 随机获取源上当前功能的快照。返回的阵列是副本,功能是源中功能的引用。
+          features: this.select.getFeatures()
+        })
+
+        // 编辑完成后
+        this.modify.on('modifyend', function(e) {
+          console.log('编辑后的要素:', e.features)
+        })
+
+        this.map.addInteraction(this.select)
+        this.map.addInteraction(this.modify)
+      } else {
+        this.editorBtnText = '编辑'
+        this.map.removeInteraction(this.select)
+        this.map.removeInteraction(this.modify)
+        this.select = null
+        this.modify = null
+      }
+    },
+
+    // 输出矢量图层要素为GeoJson数据
+    outputJson() {
+      const features = this.vectorSource.getFeatures()
+      const jsonObj = new GeoJSON().writeFeatures(features)
+      console.log('->GeoJson格式数据:', jsonObj)
+    },
+    creatDraw(type) {
+      let maxPoints = null
+      if (this.measureType === 'angle') maxPoints = 3
+      else maxPoints = null
+
+      // 矢量图层源
+      const vectorSource = new VectorSource({
+        wrapX: false
+      })
+      // 矢量图层
+      this.vectorLayer = new VectorLayer({
+        source: vectorSource,
+        style: new Style({
+          fill: new Fill({
+            color: 'rgba(252, 86, 49, 0.1)'
+          }),
+          stroke: new Stroke({
+            color: '#fc5531',
+            width: 3
+          }),
+          image: new Circle({
+            radius: 0,
+            fill: new Fill({
+              color: '#fc5531'
+            })
+          })
+        }),
+        name: '测量图层'
+      })
+      this.map.addLayer(this.vectorLayer)
+      this.draw = new Draw({
+        source: vectorSource,
+        type: type,
+        maxPoints: maxPoints,
+        style: new Style({
+          fill: new Fill({
+            color: 'rgba(252, 86, 49, 0.1)'
+          }),
+          stroke: new Stroke({
+            color: '#fc5531',
+            lineDash: [10, 10],
+            width: 3
+          }),
+          image: new Circle({
+            radius: 0,
+            fill: new Fill({
+              color: '#fc5531'
+            })
+          })
+        }),
+        // 绘制时点击处理事件
+        condition: (evt) => {
+          // 测距时添加点标注
+          if (
+            this.measureResult != '0' &&
+            !this.map.getOverlayById(this.measureResult) &&
+            this.measureType === 'distence'
+          ) {
+            this.creatMark(
+              null,
+              this.measureResult,
+              this.measureResult
+            ).setPosition(evt.coordinate)
+          }
+          return true
+        }
+      })
+      this.map.addInteraction(this.draw)
+
+      /**
+       * 绘制开始事件
+       */
+      this.draw.on('drawstart', (e) => {
+        this.sketchFeature = e.feature
+        const proj = this.map.getView().getProjection()
+        //* *****距离测量开始时*****//
+        if (this.measureType === 'distence') {
+          this.creatMark(null, '起点', 'start').setPosition(
+            this.map.getCoordinateFromPixel(e.target.downPx_)
+          )
+          this.tipDiv.innerHTML = '总长:0 m</br>单击确定地点,双击结束'
+          this.geometryListener = this.sketchFeature
+            .getGeometry()
+            .on('change', (evt) => {
+              this.measureResult = this.distenceFormat(
+                getLength(evt.target, { projection: proj, radius: 6378137 })
+              )
+              this.tipDiv.innerHTML =
+                '总长:' + this.measureResult + '</br>单击确定地点,双击结束'
+            })
+        }
+        //* *****面积测量开始时*****//
+        else if (this.measureType === 'area') {
+          this.tipDiv.innerHTML = '面积:0 m<sup>2</sup></br>继续单击确定地点'
+          this.geometryListener = this.sketchFeature
+            .getGeometry()
+            .on('change', (evt) => {
+              if (evt.target.getCoordinates()[0].length < 4) {
+                this.tipDiv.innerHTML =
+                  '面积:0m<sup>2</sup></br>继续单击确定地点'
+              } else {
+                this.measureResult = this.formatArea(
+                  getArea(evt.target, { projection: proj, radius: 6378137 })
+                )
+                this.tipDiv.innerHTML =
+                  '面积:' + this.measureResult + '</br>单击确定地点,双击结束'
+              }
+            })
+        }
+        //* *****角度测量开始时*****//
+        else if (this.measureType === 'angle') {
+          this.tipDiv.innerHTML = '继续单击确定顶点'
+          this.geometryListener = this.sketchFeature
+            .getGeometry()
+            .on('change', (evt) => {
+              if (evt.target.getCoordinates().length < 3) { this.tipDiv.innerHTML = '继续单击确定顶点' } else {
+                this.measureResult = this.formatAngle(evt.target)
+                this.tipDiv.innerHTML =
+                  '角度:' + this.measureResult + '</br>继续单击结束'
+              }
+            })
+        }
+      })
+
+      /**
+       * 绘制开始事件
+       */
+      this.draw.on('drawend', (e) => {
+        const closeBtn = document.createElement('span')
+        closeBtn.innerHTML = '×'
+        closeBtn.title = '清除测量'
+        closeBtn.style =
+          'width: 10px;height:10px;line-height: 12px;text-align: center;border-radius: 5px;display: inline-block;padding: 2px;color: rgb(255, 68, 0);border: 2px solid rgb(255, 68, 0);background-color: rgb(255, 255, 255);font-weight: 600;position: absolute;top: -25px;right: -2px;cursor: pointer;'
+        closeBtn.addEventListener('click', () => {
+          this.clearMeasure()
+        })
+        //* *****距离测量结束时*****//
+        if (this.measureType === 'distence') {
+          this.creatMark(closeBtn, null, 'close1').setPosition(
+            e.feature.getGeometry().getLastCoordinate()
+          )
+          this.creatMark(
+            null,
+            '总长:' + this.measureResult + '',
+            'length'
+          ).setPosition(e.feature.getGeometry().getLastCoordinate())
+          this.map.removeOverlay(this.map.getOverlayById(this.measureResult))
+        }
+        //* *****面积测量结束时*****//
+        else if (this.measureType === 'area') {
+          this.creatMark(closeBtn, null, 'close2').setPosition(
+            e.feature.getGeometry().getInteriorPoint().getCoordinates()
+          )
+          this.creatMark(
+            null,
+            '总面积:' + this.measureResult + '',
+            'area'
+          ).setPosition(
+            e.feature.getGeometry().getInteriorPoint().getCoordinates()
+          )
+        }
+        //* *****角度测量结束时*****//
+        else if (this.measureType === 'angle') {
+          this.creatMark(closeBtn, null, 'close3').setPosition(
+            e.feature.getGeometry().getCoordinates()[1]
+          )
+          this.creatMark(
+            null,
+            '角度:' + this.measureResult + '',
+            'angle'
+          ).setPosition(e.feature.getGeometry().getCoordinates()[1])
+        }
+        // 停止测量
+        this.stopMeasure()
+      })
+    },
+    /**
+     * 测量
+     */
+    measure(type) {
+      if (this.draw != null) return false // 防止在绘制过程再创建测量
+      this.measureType = type
+      if (this.vectorLayer != null) this.clearMeasure()
+      this.tipDiv = document.createElement('div')
+      this.tipDiv.innerHTML = '单击确定起点'
+      this.tipDiv.className = 'tipDiv'
+      this.tipDiv.style =
+        'width:auto;height:auto;padding:4px;border:1px solid #fc5531;font-size:12px;background-color:#fff;position:relative;top:60%;left:60%;font-weight:600;'
+
+      const overlay = new Overlay({
+        element: this.tipDiv,
+        autoPan: false,
+        positioning: 'bottom-center',
+        id: 'tipLay',
+        stopEvent: false // 停止事件传播到地图
+      })
+      this.map.addOverlay(overlay)
+
+      this.pointermoveEvent = this.map.on('pointermove', (evt) => {
+        overlay.setPosition(evt.coordinate)
+      })
+      if (this.measureType === 'distence' || this.measureType === 'angle') {
+        this.creatDraw('LineString')
+      } else if (this.measureType === 'area') {
+        this.creatDraw('Polygon')
+      }
+    },
+    /**
+     * 创建标记
+     */
+    creatMark(markDom, txt, idstr) {
+      if (markDom === null) {
+        markDom = document.createElement('div')
+        markDom.innerHTML = txt
+        markDom.style =
+          'width:auto;height:auto;padding:4px;border:1px solid #fc5531;font-size:12px;background-color:#fff;position:relative;top:60%;left:60%;font-weight:600;'
+      }
+      const overlay = new Overlay({
+        element: markDom,
+        autoPan: false,
+        positioning: 'bottom-center',
+        id: idstr,
+        stopEvent: false
+      })
+      this.map.addOverlay(overlay)
+      return overlay
+    },
+    /**
+     * 格式化距离结果输出
+     */
+    distenceFormat(length) {
+      let output
+      if (length > 100) {
+        output = Math.round((length / 1000) * 100) / 100 + ' ' + 'km' // 换算成km单位
+      } else {
+        output = Math.round(length * 100) / 100 + ' ' + 'm' // m为单位
+      }
+      return output // 返回线的长度
+    },
+    /**
+     * 格式化面积输出
+     */
+    formatArea(area) {
+      let output
+      if (area > 10000) {
+        output =
+          Math.round((area / 1000000) * 100) / 100 + ' ' + 'km<sup>2</sup>' // 换算成km单位
+      } else {
+        output = Math.round(area * 100) / 100 + ' ' + 'm<sup>2</sup>' // m为单位
+      }
+      return output // 返回多边形的面积
+    },
+    /**
+     * 计算角度输出
+     */
+    formatAngle(line) {
+      var coordinates = line.getCoordinates()
+      var angle = '0°'
+      if (coordinates.length === 3) {
+        const disa = getLength(
+          new Feature({
+            geometry: new LineString([coordinates[0], coordinates[1]])
+          }).getGeometry(),
+          {
+            radius: 6378137,
+            projection: this.map.getView().getProjection()
+          }
+        )
+
+        const disb = getLength(
+          new Feature({
+            geometry: new LineString([coordinates[1], coordinates[2]])
+          }).getGeometry(),
+          {
+            radius: 6378137,
+            projection: this.map.getView().getProjection()
+          }
+        )
+
+        const disc = getLength(
+          new Feature({
+            geometry: new LineString([coordinates[0], coordinates[2]])
+          }).getGeometry(),
+          {
+            radius: 6378137,
+            projection: this.map.getView().getProjection()
+          }
+        )
+        var cos = (disa * disa + disb * disb - disc * disc) / (2 * disa * disb) // 计算cos值
+        angle = (Math.acos(cos) * 180) / Math.PI // 角度值
+        angle = angle.toFixed(2) // 结果保留两位小数
+      }
+      if (isNaN(angle)) return '0°'
+      else return angle + '°' // 返回角度
+    },
+    /**
+     * 停止测量
+     */
+    stopMeasure() {
+      this.tipDiv = null
+      this.map.removeInteraction(this.draw) // 移除绘制组件
+      this.draw = null
+      this.map.removeOverlay(this.map.getOverlayById('tipLay')) // 移除动态提示框
+    },
+    /**
+     * 清除测量
+     */
+    clearMeasure() {
+      this.vectorLayer.getSource().clear()
+      this.map.getOverlays().clear()
+      // 移除监听事件
+      unByKey(this.pointermoveEvent) // 清除鼠标在地图的pointermove事件
+      unByKey(this.geometryListener) // 清除绘制图像change事件
+      this.pointermoveEvent = null
+      this.geometryListener = null
+      this.measureResult = '0'
+    },
+    // 全屏
+    fullscreen() {
+      if (this.textarr === '全屏') {
+        this.textarr = '缩小'
+        const rfs =
+          this.$refs.map.requestFullScreen ||
+          this.$refs.map.webkitRequestFullScreen ||
+          this.$refs.map.mozRequestFullScreen ||
+          this.$refs.map.msRequestFullScreen
+        if (typeof rfs !== 'undefined' && rfs) {
+          rfs.call(this.$refs.map)
+          console.log('1全屏')
+        } else if (typeof window.ActiveXObject !== 'undefined') {
+          // for IE,这里其实就是模拟了按下键盘的F11,使浏览器全屏
+          // eslint-disable-next-line no-undef
+          const wscript = new ActiveXObject('WScript.Shell')
+          console.log(wscript)
+          if (wscript != null) {
+            wscript.SendKeys('{F11}')
+            console.log('3全屏')
+          }
+          console.log('2全屏')
+        }
+      } else {
+        // el.webkitExitFullscreen()
+        this.textarr = '全屏'
+        const cfs =
+          document.exitFullscreen ||
+          document.msExitFullscreen ||
+          document.mozCancelFullScreen ||
+          document.webkitCancelFullScreen
+        console.log(cfs, 'cfs')
+        if (typeof cfs !== 'undefined' && cfs) {
+          cfs.call(document)
+          console.log('4全屏')
+        } else if (typeof window.ActiveXObject !== 'undefined') {
+          // for IE,这里和fullScreen相同,模拟按下F11键退出全屏
+          // eslint-disable-next-line no-undef
+          const wscript = new ActiveXObject('WScript.Shell')
+          console.log('5全屏')
+          if (wscript != null) {
+            wscript.SendKeys('{F11}')
+            console.log('6全屏')
+          }
+        }
+      }
+      // this.map.addControl(new FullScreen())
+    },
+    // 坐标
+    single() {
+      this.map.on('singleclick', function(e) {
+        console.log(e.coordinate)
+      })
+    },
+    // 清除
+    clear() {
+      // getSource 获取包装源的参考
+      this.vectorLayer.getSource().clear()
+      this.map.getOverlays().clear()
+    }
+  }
+}
+</script>
+
+<style scoped>
+#map {
+  width: 100%;
+  height: 600px;
+  position: relative;
+}
+.btns {
+  position: absolute;
+  z-index: 999;
+  left: 50%;
+  top: 20px;
+  transform: translate(-50%);
+}
+.btns button {
+  display: inline-block;
+  text-decoration: none;
+  text-align: center;
+  height: 30px;
+  width: 90px;
+}
+</style>

+ 0 - 297
src/views/map/TrailMap.vue

@@ -1,297 +0,0 @@
-<template>
-  <el-container>
-    <el-header height="60">
-      <el-button style="float: right; padding: 5px" type="text" @click="drawCircle">绘制圆形</el-button>
-      <el-button style="float: right; padding: 5px" type="text" @click="onPathNavigator">路径巡航</el-button>
-    </el-header>
-    <el-main>
-      <div id="container" class="text item amap-wrapper" />
-    </el-main>
-
-    <el-dialog :visible.sync="showPositionDialog" width="30%" center>
-      <el-card class="box-card" style="padding-right: 5px; padding-left: 5px; padding-bottom: 5px">
-        <div slot="header" class="clearfix">
-          <span>选中的坐标</span>
-          <el-button style="float: right; padding: 10px" type="text" @click="onSavePosition">保存坐标</el-button>
-        </div>
-        <div class="text item">
-          <span style="color: red">({{ positionForm.lng }}, {{ positionForm.lat }})</span>
-        </div>
-      </el-card>
-    </el-dialog>
-  </el-container>
-</template>
-
-<script>
-import AMapLoader from '@amap/amap-jsapi-loader'
-import { addGeoPosition, getGeoPoint } from '@/api/map'
-
-export default {
-  name: 'Map',
-  data() {
-    return {
-      amap: null,
-      plugins: ['Scale'],
-      mapCenter: [114.0000, 30.0000],
-      // zoom=6 的比例尺为 100km
-      zoom: 6,
-      province: '',
-      provinces: [],
-      city: '',
-      citys: [],
-      district: '',
-      districts: [],
-      path1: [{
-        path: [
-          [116.361904, 39.913423],
-          [116.367904, 39.913423]
-        ]
-      }],
-      path: [],
-      showPositionDialog: false,
-      positionForm: {
-        lng: null,
-        lat: null
-      },
-      mapKeys: {
-        securityJsCode: '983d6ee43bab3edf3693e91508f94aa9',
-        key: '7b75ab2839ce68b884c7a682501ea774'
-      }
-    }
-  },
-  mounted() {
-    this.initAMap()
-  },
-  unmounted() {
-    this.map?.destroy()
-  },
-  created() {
-    document.title = '地图'
-  },
-  methods: {
-    initAMap() {
-      window._AMapSecurityConfig = {
-        securityJsCode: this.mapKeys.securityJsCode
-      }
-      AMapLoader.load({
-        key: this.mapKeys.key, // 申请好的Web端开发者Key,首次调用 load 时必填
-        version: '1.4.15', // 指定要加载的 JSAPI 的版本,缺省时默认为 1.4.15
-        plugins: ['AMap.Autocomplete', 'AMap.PlaceSearch', 'AMap.Scale', 'AMap.OverView', 'AMap.ToolBar',
-          'AMap.MapType', 'AMap.PolyEditor', 'AMap.CircleEditor', 'AMap.MassMarks', 'AMap.Size',
-          'AMap.Pixel'], // 需要使用的的插件列表,如比例尺'AMap.Scale',支持添加多个如:['...','...']
-        AMapUI: { // 重点就是这个
-          version: '1.0',
-          plugins: ['misc/PathSimplifier', 'overlay/SimpleMarker']// SimpleMarker设置自定义图标,PathSimplifier轨迹展示组件
-        }
-      }).then((AMap) => {
-        // 设置地图容器id
-        this.map = new AMap.Map('container', {
-          viewMode: '2D', // 是否为3D地图模式
-          zoom: 6, // 初始化地图级别
-          center: [104.065753, 30.657462] // 初始化地图中心点位置
-        })
-
-        this.map.on('click', (e) => {
-          // 获取经纬度
-          this.positionForm.lng = e.lnglat.getLng()
-          this.positionForm.lat = e.lnglat.getLat()
-          this.showPositionDialog = true
-        })
-      }).catch((e) => {
-        console.log(e)
-      })
-    },
-    zoomchange(e) {
-      console.log('当前缩放级别: ' + this.amap.getZoom())
-    },
-    // 轨迹巡航
-    loadPathSimplifier() {
-      AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
-        if (!PathSimplifier.supportCanvas) {
-          alert('当前环境不支持 Canvas!')
-          return
-        }
-        // 创建组件实例
-        var pathSimplifierIns = new PathSimplifier({
-          map: this.map,
-          zIndex: 100, // 图层叠加顺序
-          data: this.path, // 巡航路径
-          // 获取巡航路径中的路径坐标数组
-          getPath: (pathData, pathIndex) => {
-            return pathData.path
-          }
-        })
-        // 创建巡航器
-        var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
-          loop: false, // 是否循环
-          speed: 2000 // 速度(km/h)
-        })
-        pathNavigator.start()
-      })
-    },
-    getDistrict(map) {
-      // 创建行政区查询对象
-      var district = new AMap.DistrictSearch({
-        // 返回行政区边界坐标等具体信息
-        extensions: 'all',
-        // 设置查询行政区级别为 区
-        level: 'street',
-        subdistrict: 3
-      })
-
-      this.zoom = 11
-      const area = '双流'
-      district.search(area, function(status, result) {
-        var bounds = result.districtList[0].boundaries
-        var polygons = []
-        if (bounds) {
-          for (var i = 0, l = bounds.length; i < l; i++) {
-            // 生成行政区划polygon
-            var polygon = new AMap.Polygon({
-              map: map,
-              strokeWeight: 1,
-              path: bounds[i],
-              fillOpacity: 0.7,
-              fillColor: '#CCF3FF',
-              strokeColor: '#CC66CC'
-            })
-            polygons.push(polygon)
-          }
-
-          // 地图自适应
-          map.setFitView()
-        }
-      })
-    },
-    drawCircle1() {
-      this.$message.info('加载路径')
-      getGeoPoint().then(resp => {
-        if (resp.code === 0) {
-          const pathList = resp.data
-          if (pathList.length === 0) {
-            return
-          }
-
-          this.path = pathList
-          AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
-            if (!PathSimplifier.supportCanvas) {
-              alert('当前环境不支持 Canvas!')
-              return
-            }
-            // 创建组件实例
-            var pathSimplifierIns = new PathSimplifier({
-              map: this.map,
-              zIndex: 100, // 图层叠加顺序
-              data: this.path, // 巡航路径
-              // 获取巡航路径中的路径坐标数组
-              getPath: (pathData, pathIndex) => {
-                return pathData.path
-              }
-            })
-            // 创建巡航器
-            var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
-              loop: false, // 是否循环
-              speed: 2000 // 速度(km/h)
-            })
-            pathNavigator.start()
-          })
-        }
-      })
-    },
-    drawCircle() {
-      // 设置圆形位置
-      var center = new AMap.LngLat(104.065753, 30.657462)
-      // 设置圆的半径大小
-      var radius = 1000
-      // 创建圆形 Circle 实例
-      var circle = new AMap.Circle({
-        center: center, // 圆心
-        radius: radius, // 半径
-        borderWeight: 3, // 描边的宽度
-        strokeColor: '#ff3333', // 轮廓线颜色
-        strokeOpacity: 1, // 轮廓线透明度
-        strokeWeight: 1, // 轮廓线宽度
-        fillOpacity: 0.4, // 圆形填充透明度
-        strokeStyle: 'line', // 轮廓线样式
-        strokeDasharray: [10, 10],
-        fillColor: '#1791fc', // 圆形填充颜色
-        zIndex: 50 // 圆形的叠加顺序
-      })
-
-      // 圆形 Circle 对象添加到 Map
-      this.map.add(circle)
-      // 根据覆盖物范围调整视野
-      this.map.setFitView([circle])
-
-      // 鼠标移入事件
-      circle.on('mouseover', function() {
-        console.log('鼠标移入')
-      })
-
-      var that = this.map
-      // 引入圆形编辑器插件
-      this.map.plugin(['AMap.CircleEditor'], function() {
-        // 实例化圆形编辑器,传入地图实例和要进行编辑的圆形实例
-        var circleEditor = new AMap.CircleEditor(that, circle)
-        // 开启编辑模式
-        circleEditor.open()
-      })
-    },
-    onSavePosition() {
-      this.showPositionDialog = false
-      addGeoPosition(this.positionForm).then(resp => {
-        if (resp.code !== 0) {
-          this.$message.info('添加坐标失败')
-        }
-      })
-    },
-    onPathNavigator() {
-      getGeoPoint().then(resp => {
-        if (resp.code === 0) {
-          const pathList = resp.data
-          if (pathList.length === 0) {
-            return
-          }
-
-          this.path = pathList
-          AMapUI.load(['ui/misc/PathSimplifier'], (PathSimplifier) => {
-            if (!PathSimplifier.supportCanvas) {
-              alert('当前环境不支持 Canvas!')
-              return
-            }
-            // 创建组件实例
-            var pathSimplifierIns = new PathSimplifier({
-              map: this.map,
-              zIndex: 100, // 图层叠加顺序
-              data: this.path, // 巡航路径
-              // 获取巡航路径中的路径坐标数组
-              getPath: (pathData, pathIndex) => {
-                return pathData.path
-              }
-            })
-            // 创建巡航器
-            var pathNavigator = pathSimplifierIns.createPathNavigator(0, {
-              loop: false, // 是否循环
-              speed: 2000 // 速度(km/h)
-            })
-            pathNavigator.start()
-          })
-        }
-      })
-    }
-  }
-}
-</script>
-
-<style>
-.amap-wrapper {
-  width: 100%;
-  height: 600px;
-}
-
-.movie-list {
-  padding-top: 15px;
-  padding-left: 3%;
-  padding-right: 3%;
-}
-</style>