首页 > 编程学习 > vue后台系统管理项目-openlayers地图定位、港口数据标记功能

⭐️⭐️⭐️  作者:船长在船上
🚩🚩🚩  主页:来访地址船长在船上的博客
🔨🔨🔨  简介:CSDN前端领域优质创作者,资深前端开发工程师,专注前端开发,在CSDN总结工作中遇到的问题或者问题解决方法以及对新技术的分享,欢迎咨询交流,共同学习。

🔔🔔🔔   感谢:如果觉得博主的文章不错或者对你的工作有帮助或者解决了你的问题,可以点赞收藏关注、支持一下博主。如有疑问可以留言、评论,看到后会及时回复。

2022十一长假开始了,开心、吉祥、平安、如意、好运、福气、温馨、甜蜜、思念、圆满。加起来“十分快乐”送给你,祝大家十一国庆节快乐!

7天长假也不忘学习,在这里总结整理自己开发的后台管理系统-openlayers地图功能:

  • 使用openlayers开发实现查找的货源在地图的显示标注,点击货源图标进行当前港口信息显示;
  • 通过查询的港口列表数据,点击当前港口在地图定位显示,添加港口选中标记功能;
  • OpenLayers 是一个专为Web GIS 客户端开发提供的JavaScript 类库包,用于实现标准格式发布的地图数据访问。 
  • OpenLayers是一个用于开发WebGIS客户端的JavaScript包。
  • 在操作方面,OpenLayers 除了可以在浏览器中帮助开发者实现地图浏览的基本效果,比如放大(Zoom In)、缩小(Zoom Out)、平移(Pan)等常用操作之外,还可以进行选取面、选取线、要素选择、图层叠加等不同的操作,甚至可以对已有的OpenLayers 操作和数据支持类型进行扩充,为其赋予更多的功能。
  • openlayers官方文档学习:

    OpenLayers - Quick Start

 实现效果:

 

 

目录

channelMap.vue页面

接口引入

 openlayers地图资源引入

data数据定义

methods方法集合

created加载

mounted地图加载

beforeDestroy地图移除注册


 

channelMap.vue页面

注意事项:

  • 定义地图id为mapDiv,方便获取实现地图渲染
<div id="mapDiv"></div>
  • 自定义弹窗功能:
<!-- 弹窗元素 -->
<div class="popup serchPopup" ref="popup" v-show="showPopup" >
    <div class="ship-header" v-show="portName">
        <div class="cname">{{portName}}<span class="pr20"></span></div>
        <img class="icon-close" @click="closePopup" src="../../assets/sy_close.png"/>
     </div>
</div>
  • 根据港口定位,在地图上显示港口货源数据
<el-table-column label="操作">
              <template slot-scope="scope">
                <el-button @click="handlePosition(scope.row)" type="text" size="small">定位</el-button>
              </template>
</el-table-column>
  • 分页
<el-pagination background layout="prev, pager, next" :total="pagination.total" :current-page="pagination.pageNum"
            @size-change="handleSizeChange" @current-change="handleCurrentChange">
</el-pagination>

 

<template>
  <div class="channelMap p20">
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>地图货源分布</el-breadcrumb-item>
    </el-breadcrumb>
    <div class="chanelContent mt20">
      <!-- 地图 -->
      <div style="width:100%;height:100%">
        <div id="mapDiv"></div>
        <!-- 弹窗元素 -->
        <div class="popup serchPopup" ref="popup" v-show="showPopup" >
          <div class="ship-header" v-show="portName">
            <div class="cname">{{portName}}<span class="pr20"></span></div>
            <img class="icon-close" @click="closePopup" src="../../assets/sy_close.png"/>
          </div>
        </div>
      </div>
      <div class="sourceDistribution">
        <i class="icon sourceInfoImg" :class="isShowSourceInfo==true?'el-icon-error':'el-icon-info'" @click="handleShowSource"></i>
        <i class="icon sourceImg" :class="isShowFollow==true?'el-icon-error':'el-icon-info'" @click="handleShowFollow"></i>
        <!-- 货源分布 -->
        <div class="distribution" v-show="isShowSourceInfo">
          <el-form :model="sourceForm" ref="sourceForm" label-width="" class="demo-ruleForm" size="35">
            <el-row>
              <el-col :span="14">
                  <el-form-item label="" prop="portName" class="mb10">
                    <el-input v-model="sourceForm.portName" size="small" placeholder="请输入港口名称进行搜索" clearable></el-input>
                  </el-form-item>
              </el-col>
              <el-col :span="9" :offset="1">
                  <el-form-item class="mb10" label=" ">
                    <el-button type="primary" size="small" @click="sourceSubmit">查询</el-button>
                    <el-button @click="resetForm" size="small">重置</el-button>
                  </el-form-item>
              </el-col>
            </el-row>
          </el-form>
          <el-table :data="sourceData" border highlight-current-row :header-cell-style="{textAlign: 'center',background:'#fafafa'}"
            :cell-style="{ textAlign: 'center' }" class="sourceTable mb10" id="sourceTable" @sort-change="sortChange">
            <el-table-column prop="portName" label="港口名称"></el-table-column>
            <el-table-column prop="sourceCount" label="货源数量" sortable></el-table-column>
            <el-table-column label="操作">
              <template slot-scope="scope">
                <el-button @click="handlePosition(scope.row)" type="text" size="small">定位</el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-pagination background layout="prev, pager, next" :total="pagination.total" :current-page="pagination.pageNum"
            @size-change="handleSizeChange" @current-change="handleCurrentChange">
          </el-pagination>
        </div>
        

      </div>
    </div>
  </div>
</template>

安装openlayers:


cnpm i -S ol
#或者
npm install ol

 

接口引入

import {
  baseUrl,
  sourceOfPortPageList,
  sourceMapOfPort,
  sourceStatusOfSysUser
} from "../../api/userMG";

 openlayers地图资源引入

// openlayers地图
import "ol/ol.css";
import { Icon, Style ,Text,Fill,Stroke,Circle as CircleStyle} from "ol/style";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import XYZ from "ol/source/XYZ";
import { get as getProjection ,fromLonLat} from "ol/proj.js";
import { getBottomLeft, getTopRight } from "ol/extent.js";
import { Vector as SourceVec } from "ol/source";
import { Vector as LayerVec } from "ol/layer";
import Overlay from "ol/Overlay"; //弹窗
import { Point } from "ol/geom";
import { Feature } from "ol";
import Observable from 'ol/Observable';
import { defaults as defaultControls } from "ol/control"; //默认缩放
import { FullScreen, ScaleLine, ZoomSlider } from "ol/control"; //全屏,比例尺控件
import TileGrid from "ol/tilegrid/TileGrid";
import { LineString, Polygon } from "ol/geom.js";
import {defaults as defaultInteractions} from 'ol/interaction';//旋转

data数据定义

data(){
    return{
      sourceIcon:require("../../assets/source.png"),
      
      // 图层
      cjinobeaconMap:null,
      framesMap:null,
      soundgMap:null,

      mapTimers:null,
      shipTimer: null,
      allSourceTimer: null,
      tk: "密钥",
      map: null,
      zoom: 7, // 地图的初始化级别,及放大比例
      allSource: [],
      portName:"",
      showPopup:false,
      overlay: null,

      featuresMarker: [],
      features: [],
      iconStyle: null,
      isVector: false,
      vectorLayer: null,
      center: {
        longitude: "114.29372666666667",
        latitude: "30.577845"
      },
      moveEvent:null,
      // 定位货源数量图层
      sourceVector:false,
      sourceVectorLayer:null,
      checkSourceIcon:require("../../assets/checkSourceIcon.png"),
      
      sourceForm:{
        portName:"",
        sortType:"2",
      },
      sourceData:[],
      pagination:{
        pageNum:1,
        pageSize:10,
        total:0
      },
      
    }
  },

methods方法集合

initMap方法:初始化加载地图资源,添加地图资源,设置地图区域范围;

this.mapClick(); // 添加点击事件
this.mapMoveend();//添加拖拽地图事件

initMap() {
      let defaultsMap = {
        tileUrl1:"",
        tileUrl2:"",
        tileUrl3:"",
        origin: [-400, 400],
        zoom: 7,
        resolutions: [
          //自定义
        ],
        fullExtent: [
          //自定义
        ],
        inters: [1000, 100],
        center: [this.center.longitude, this.center.latitude],
        projection: getProjection("EPSG:4326")
      };

      // 底图天地图注记  cta——道路+中文注记
      let baseLayer = new TileLayer({
        title: "天地图",
        source: new XYZ({
          url:
            "http://t4.tianditu.com/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=" +
            this.tk
        }),
        zIndex: 2
      });
      //天地图路网
      let roadLayer = new TileLayer({
        title: "天地图路网",
        source: new XYZ({
          projection: defaultsMap.projection,
          url:
            "http://t4.tianditu.com/DataServer?T=vec_c&x={x}&y={y}&l={z}&tk=" +
            this.tk
        }),
        zIndex: 1
      });

      // 加载地图层mapservice
      let tileGrid = new TileGrid({
        tileSize: 256,
        origin: defaultsMap.origin,
        extent: defaultsMap.fullExtent,
        resolutions: defaultsMap.resolutions
      });

      // 航道图层
      this.cjinobeaconMap = new TileLayer({
        source: new XYZ({
          tileGrid: tileGrid,
          projection: defaultsMap.projection,
          url: defaultsMap.tileUrl1
        }),
        zIndex: 9
      });
      
      
       // 弹窗
      this.overlay = new Overlay({
        element: this.$refs.popup, // 弹窗标签,在html里
        autoPan: true, // 如果弹窗在底图边缘时,底图会移动
        autoPanAnimation: {
          // 底图移动动画
          duration: 250
        },
        stopEvent: false,
        offset: [0, -10],
        className:"popupOverlay",
      });

      // 加载地图
      this.map = new Map({
        target: "mapDiv",
        controls: defaultControls().extend([
          new FullScreen(),
          new ScaleLine({
            units: "metric"
          })
          // new ZoomSlider()
        ]),
        interactions: defaultInteractions({
          pinchRotate: false, // 移动端禁止地图旋转
          doubleClickZoom:false,//屏蔽双击放大事件
        }),
        loadTilesWhileAnimating: true,
        layers: [baseLayer, roadLayer, this.cjinobeaconMap],
        overlays: [this.overlay], // 把弹窗加入地图
        view: new View({
          projection: defaultsMap.projection,
          center: defaultsMap.center, 
          extent: defaultsMap.fullExtent,
          // resolutions: defaultsMap.resolutions,
          zoom: 14,
          // minZoom: 7,
          // maxZoom:17,
          //设置缩放级别为整数 
          constrainResolution: true,
          //关闭无级缩放地图
          smoothResolutionConstraint: false
        })
      });
      this.mapClick(); // 添加点击事件
      this.mapMoveend();
    },

mapClick()方法:

this.map.on("singleclick", evt => {})使用,点击当前港口获取当前的港口信息,控制弹窗显示

 

// 弹窗
    mapClick() {
      this.map.on("singleclick", evt => {
        let pixel = this.map.getEventPixel(evt.originalEvent);
        let feature = this.map.forEachFeatureAtPixel(
          evt.pixel,
          feature => feature
        );
        if (feature) {
          console.log(feature,"点击")
          this.portName = feature.values_.portName;//港口
          if(this.portName){
            this.showPopup = true;
            let coordinates = feature.getGeometry().getCoordinates();
            console.log(coordinates)
            setTimeout(() => {
              this.overlay.setPosition(coordinates);
            }, 0);
          }else {
            this.showPopup = false;
          }

        }
      });
    },

closePopup()方法:关闭弹窗,清楚定位

// 关闭弹窗
    closePopup() {
      this.overlay.setPosition(undefined);
      this.showPopup = false;
    },

 地图拖拽事件

this.map.on('movestart', (evt) => {})使用

//地图拖拽事件
    mapMoveend(){
      this.map.on('movestart', (evt) => {
        this.mapTimers && clearTimeout(this.mapTimers)
        this.mapTimers = null
      });
      this.map.on('moveend', this.viewChange);
    },

viewChange()触发获取所有货源

    debounce(func, wait = 500){
      var _this = this;
      return function (...args) {
        if (_this.mapTimers) clearTimeout(_this.mapTimers);
        _this.mapTimers = setTimeout(() => {
          func.apply(_this, args);
        }, wait);
      };
    },
    viewChange(){
      this.debounce(this.getAllSource, 500)();
    },

获取所有货源数据

根据当前屏幕区域动态显示货源数据,拖动地图仅显示当前区域,优化数据加载

getAllSource() {
        let view = this.map.getView();
        let arr = view.calculateExtent(this.map.getSize());//获取左下角和右上角经纬度
        let params = {
          leftLongitude: arr[0],
          leftLatitude: arr[1],
          rightLongitude: arr[2],
          rightLatitude: arr[3],
        };
        sourceMapOfPort(params).then(res => {
          console.log(res, "地图");
          if (res.code == 200) {
            this.featuresMarker=[];
            if (this.isVector) {
              this.map.removeLayer(this.vectorLayer);
            }

            res.data.map((item, index) => {
              this.featuresMarker.push(
                new Feature({
                  geometry: new Point([item.longitude, item.latitude], "XY"),
                  portName:item.portName,
                  sourceCount:item.sourceCount,
                  index: index
                })
              );
            });
            
            创建图标样式
            // this.iconStyle = new Style({
            //   image: new Icon({
            //       opacity: 1,
            //       src:this.TKshipIcon
            //   })
            // })
            let iconStyles = [];
            this.featuresMarker.forEach(s => {
                iconStyles.push(
                  new Style({
                    image: new Icon({
                      src: this.sourceIcon,
                      scale:0.7
                    }),
                    text: new Text({
                      text:s.values_.sourceCount.toString(),
                      font:"bold 14px Microsoft YaHei",
                      offsetY:0,
                      textAlign:"center",
                      fill: new Fill({
                        color:"#333",
                      }),
                      // stroke: new Stroke({
                      //   color:"#fff",
                      //   width: 3
                      // })
                    })

                  })
                );
            });
            //数据源
            let vectorSource = new SourceVec({
              features: this.featuresMarker
            });
            this.vectorLayer = new LayerVec({
              source: vectorSource,
              // style: this.iconStyle,
              style: function(feature) {
                var iconStyle = iconStyles[feature.values_.index];
                return [iconStyle];
              },
              zIndex: 10
            });
            this.map.addLayer(this.vectorLayer);
            this.isVector = true;
          } else {
            this.$toast("获取数据失败");
          }
        });
    },

点击港口列表定位按钮,在地图选中当前港口,滑动到当前港口位置,添加选中图标

// 定位
    handlePosition(item){
      console.log(item,"选中货源")
      let view = this.map.getView();
      view.animate({
        center: [item.longitude, item.latitude],
        duration: 0
      });
      if (this.sourceVector) {
        this.map.removeLayer(this.sourceVectorLayer);
      }
      let positionMarker = [];
      positionMarker.push(
        new Feature({
          geometry: new Point([item.longitude, item.latitude], "XY"),
          index: 0,
        })
      );

      let iconStyles = [];
      positionMarker.forEach(() => {
        iconStyles.push(
          new Style({
            image: new Icon({
              src: this.checkSourceIcon,
              scale: 1.7,
            }),
          })
        );
      });
      let vectorSource = new SourceVec({
        features: positionMarker,
      });
      this.sourceVectorLayer = new LayerVec({
        name: "sourceVectorLayer",
        source: vectorSource,
        style: function (feature) {
          var iconsStyle = iconStyles[feature.values_.index];
          return [iconsStyle];
        },
        zIndex: 13,
      });
      this.map.addLayer(this.sourceVectorLayer);
      this.map.getView().setCenter([item.longitude, item.latitude]);
      this.sourceVector = true;
    },

获取港口列表数据

    // 列表数据
    sourceOfPortPageList(){
      this.sourceForm.pageNum = this.pagination.pageNum;
      this.sourceForm.pageSize = this.pagination.pageSize;
      sourceOfPortPageList(this.sourceForm).then(res => {
          if (res.code == 200) {
            this.sourceData = res.data.records;
            this.current = res.data.current;
            this.pagination.pageSize = res.data.size;
            this.pagination.total = res.data.total;
          }
      }).catch(err => {
      });

    },
    

 分页

handleSizeChange(val){
      this.pagination.pageSize = val;
      this.pagination.pageNum = 1;
      this.sourceOfPortPageList();
    },
    handleCurrentChange(val){
      this.pagination.pageNum = val;
      this.sourceOfPortPageList();
    },

created加载

created(){
    this.sourceOfPortPageList();
    this.getSourceFollowList();
  },

mounted地图加载


  mounted(){
    this.initMap(); //加载地图
    
  },

beforeDestroy地图移除注册

beforeDestroy(){
    this.mapTimers && clearTimeout(this.mapTimers);
    this.map.un("moveend", this.viewChange);
  }

 

 

👉👉👉  欢迎来访船长在船上的博客,文章持续更新;项目功能持续迭代,请及时收藏关注,方便查看。  发文不易,点赞收藏评论关注一下! 

开心、吉祥、平安、如意、好运、福气、温馨、甜蜜、思念、圆满。加起来“十分快乐”送给你,祝大家十一国庆节快乐!

Copyright © 2010-2022 mfbz.cn 版权所有 |关于我们| 联系方式|豫ICP备15888888号