This article introduces you to the use of openLayers offline map from zero to one, including:

  • Get offline map tiles
  • Openlayers loads offline map tiles
  • Display geographic coordinates
  • Displays the geographic coordinate range.
  • This is a must see. The OpenLayers API is really not very friendly compared to commercial maps like Autonavi and Baidu.

Get offline map tiles

  • Autonavi provides an API that you are interested in. This article does not take that approach.
  • Download offline map tiles, there are many methods online, most of the charges to find a suitable tool really difficult, find a free contribution out of the omnipotent map downloader
  • Link: pan.baidu.com/s/18LiUAh1-… Extraction code: YD88

  • Offline tile download is completed to such a piece of data

Publishing offline Data

  • The map offline tile must be on the server, it’s too big for the project.
  • Local test, local up a service used to access the map offline tile, this article uses HTTP -server to view the installation of a line of command.

  • The http://192.168.3.6:8081 local service and IP port will be used below

This paper takes the VUE project as an example

Install the openlayers

  • The document
  • npm install ol
  • Of course, it can also be imported directly through JS
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/build/ol.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.8.1/css/ol.css">
Copy the code

Create the map and load the points

<template>
  <div style="width: 100%; height: 100%">
    <div class="map" id="map"></div>
    <el-card id="popup" class="popup">
      <div class="popupContainer"></div>
    </el-card>
  </div>
</template>

<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import Feature from 'ol/Feature';
import VectorSource from 'ol/source/Vector';
import Overlay from 'ol/Overlay';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import View from 'ol/View';
import {transform} from 'ol/proj';
import XYZ from 'ol/source/XYZ'
import Point from 'ol/geom/Point';
import GeoJSON from 'ol/format/GeoJSON';
import {Fill, Stroke, Icon, Style} from 'ol/style'
import markerImg from '@/assets/img/markerIcon.png'
export default {
  name: "openlayersMap",
  data () {
    return {
      mapObj: null.mapDom: null.mapPointList: [].pointLayerSource:null.pointLayer: null.markerIcon: markerImg
    }
  },
  mounted() {
    this.initMap()
  },
  methods: {
    In some cases there will be two maps in the container and the map will not display properly.
    // There is no corresponding API provided by the official.
    mapClear (){
      if (this.mapDom) {
        this.mapDom.innerHTML = ' '
        this.mapDom = null}},// Initialize the map
    initMap () {
      // Try clearing first
      this.mapClear()
      // Get the map container
      this.mapDom = document.getElementById('map')

      // Initialize the map configuration
      this.mapObj = new Map({
        target: this.mapDom, // Map container
        view: new View({
          center: [117.990969.36.635013].// Map center point
          zoom: 10./ / zoom
          projection: 'EPSG:4326' / / coordinate system})})// Add a layer that uses an offline tile map
      const offlineMapLayer = new TileLayer({
        source: new XYZ({
          url: 'http://192.168.3.6:8081' + '/{z}/{x}/{y}.png'  // Set the path for storing the local offline tile})})// Add layers to the map
      this.mapObj.addLayer(offlineMapLayer)

      // Load the geographic coordinates
      this.addPoint()
    },

    // Add the geographic coordinates
    addPoint () {
      this.delPointAll()
      // Array of geographic coordinates
      const pointData = [
        {longitude: 117.990969.latitude: 36.635013}
      ]

      pointData.map(item= > {
        / / create
        const point = new Feature({
          geometry: new Point([item.longitude, item.latitude]),
          data: item
        })

        // Point style
        const iconStyle = new Style({
          image: new Icon({
            color: '#ffffff'.crossOrigin: 'anonymous'.src: this.markerIcon,
          }),
        })
        // Set the style
        point.setStyle(iconStyle)
        // Save to data for easy deletion
        this.mapPointList.push(point)
      })

      // Create geoJSON data source
      this.pointLayerSource = new VectorSource({features: this.mapPointList})
      // Create layers and load data
      this.pointLayer = new VectorLayer({source: this.pointLayerSource})
      // Add layers to the map
      this.mapObj.addLayer(this.pointLayer)
    },

    // Delete the geographic point
    delPointAll(){
      // Check whether the deleted data source exists
      if (this.pointLayerSource) {
        // Iterate over the delete
        this.mapPointList.map(item= > {
          this.pointLayerSource.removeFeature(item)
        })

        // Delete the layer and reset the data
        this.mapObj.removeLayer(this.pointLayer)
        this.pointLayerSource = null
        this.pointLayer = null
        this.mapPointList = []
      }
    }
  },
  beforeDestroy() {
    this.mapClear()
  }
}
</script>

<style scoped>
.map {
  width: 100%;
  height: 100%;
}
</style>
Copy the code

Geographic point appending

  • Layer data sourceVectorSourceProvide one after creationaddFeaturemethodspointLayerSource.addFeature(item) itemIs to createpointPoint data is as follows:
/ / create
const point = new Feature({
  geometry: new Point([item.longitude, item.latitude]),
  data: item
})

// Point style
const iconStyle = new Style({
  image: new Icon({
    color: '#ffffff'.crossOrigin: 'anonymous'.src: this.markerIcon,
  }),
})
// Set the style
point.setStyle(iconStyle)
Copy the code

Not surprisingly, the page will display a map and an anchor point if you haven’t checked first

  • The first request for the above service will be stuck in CMD and a few press enter.
  • Check whether the offline map tile request path is correct

  • Are the coordinates correct and within the displayed map area?

Point response events display popup window

<template>
  <div style="width: 100%; height: 100%">
    <div class="map" id="map"></div>
    <el-card id="popup" class="popup">
      <div class="popupContainer"></div>
    </el-card>
  </div>
</template>

<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import Feature from 'ol/Feature';
import VectorSource from 'ol/source/Vector';
import Overlay from 'ol/Overlay';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import View from 'ol/View';
import {transform} from 'ol/proj';
import XYZ from 'ol/source/XYZ'
import Point from 'ol/geom/Point';
import GeoJSON from 'ol/format/GeoJSON';
import {Fill, Stroke, Icon, Style} from 'ol/style'
import markerImg from '@/assets/img/markerIcon.png'
export default {
  name: "openlayersMap",
  data () {
    return {
      mapObj: null.mapDom: null.mapPointList: [].pointLayerSource:null.pointLayer: null.markerIcon: markerImg
    }
  },
  mounted() {
    this.initMap()
  },
  methods: {
    In some cases there will be two maps in the container and the map will not display properly.
    // There is no corresponding API provided by the official.
    mapClear (){
      if (this.mapDom) {
        this.mapDom.innerHTML = ' '
        this.mapDom = null}},// Initialize the map
    initMap () {
      // Try clearing first
      this.mapClear()
      // Get the map container
      this.mapDom = document.getElementById('map')

      // Initialize the map configuration
      this.mapObj = new Map({
        target: this.mapDom, // Map container
        view: new View({
          center: [117.990969.36.635013].// Map center point
          zoom: 10./ / zoom
          projection: 'EPSG:4326' / / coordinate system})})// Add a layer that uses an offline tile map
      const offlineMapLayer = new TileLayer({
        source: new XYZ({
          url: 'http://192.168.3.6:8081' + '/{z}/{x}/{y}.png'  // Set the path for storing the local offline tile})})// Add layers to the map
      this.mapObj.addLayer(offlineMapLayer)

      // Map click event
      this.mapOnClick()
      // Load the geographic coordinates
      this.addPoint()
    },

    // Map click event
    mapOnClick (){
      const self = this

      // popupDom
      const popupDom = document.getElementById('popup')
      / / create the popup
      const popup = new Overlay({
        element: popupDom,
        positioning: 'bottom-center'.stopEvent: false
      })
      // Load to map
      this.mapObj.addOverlay(popup)

      // Map click event
      this.mapObj.on('click'.function (evt) {
        // Get the click location data
        const feature = self.mapObj.forEachFeatureAtPixel(evt.pixel, function (feature) {
          return feature;
        })

        // Determine if a custom popup is clicked based on the click element className
        const isClickPopUp = evt.originalEvent.path.map(item= > item.className).includes('el-card__body')
        if(! isClickPopUp) { popupDom.style.display ='none'
        }

        // The official example uses the JQ + Bootstrap popup, but I don't think it's necessary if you use the Bootstrap component a lot.
        const popupContainer = document.getElementsByClassName('popupContainer') [0]

        // Determine the data
        if (feature) {
          // feature.values_.data, the data field is added when the addPoint function creates the point
          if (feature.values_.data) {
            const pointData = feature.values_.data
            popup.setPosition(evt.coordinate)
            popupContainer.innerHTML = `<div>${pointData.name}</div>`
            popupDom.style.display = 'block'}}})},// Add the geographic coordinates
    addPoint () {
      this.delPointAll()
      // Array of geographic coordinates
      const pointData = [
        {longitude: 117.990969.latitude: 36.635013.name: 'Li Dazhuang'}
      ]

      pointData.map(item= > {
        / / create
        const point = new Feature({
          geometry: new Point([item.longitude, item.latitude]),
          data: item
        })

        // Point style
        const iconStyle = new Style({
          image: new Icon({
            color: '#ffffff'.crossOrigin: 'anonymous'.src: this.markerIcon,
          }),
        })
        // Set the style
        point.setStyle(iconStyle)
        // Save to data for easy deletion
        this.mapPointList.push(point)
      })

      // Create geoJSON data source
      this.pointLayerSource = new VectorSource({features: this.mapPointList})
      // Create layers and load data
      this.pointLayer = new VectorLayer({source: this.pointLayerSource})
      // Add layers to the map
      this.mapObj.addLayer(this.pointLayer)
    },

    // Delete the geographic point
    delPointAll(){
      // Check whether the deleted data source exists
      if (this.pointLayerSource) {
        // Iterate over the delete
        this.mapPointList.map(item= > {
          this.pointLayerSource.removeFeature(item)
        })

        // Delete the layer and reset the data
        this.mapObj.removeLayer(this.pointLayer)
        this.pointLayerSource = null
        this.pointLayer = null
        this.mapPointList = []
      }
    }
  },
  beforeDestroy() {
    this.mapClear()
  }
}
</script>

<style scoped>
.map {
  width: 100%;
  height: 100%;
}
</style>
Copy the code

Add administrative area scope

  • Administrative region refers to the scope of a province, city or region. Generally, a city has a geoJSON data, from which datAV Geo can be obtained

  • Notice the order of the layers and if the point layer is not on top it may cause the click event to not respond properly and the layer added first is below
<template>
  <div style="width: 100%; height: 100%">
    <div class="map" id="map"></div>
    <el-card id="popup" class="popup">
      <div class="popupContainer"></div>
    </el-card>
  </div>
</template>

<script>
import 'ol/ol.css';
import Map from 'ol/Map';
import Feature from 'ol/Feature';
import VectorSource from 'ol/source/Vector';
import Overlay from 'ol/Overlay';
import {Tile as TileLayer, Vector as VectorLayer} from 'ol/layer';
import View from 'ol/View';
import {transform} from 'ol/proj';
import XYZ from 'ol/source/XYZ'
import Point from 'ol/geom/Point';
import GeoJSON from 'ol/format/GeoJSON';
import {Fill, Stroke, Icon, Style} from 'ol/style'

import {inject, onMounted, watch, onUnmounted} from 'vue'
import home from "@model/home";

export default {
  name: "MapBox".props: {
    pointData: {
      type: Array.default: []}},emits: ['mapClickPoint'].setup(props, {emit}) {
    const {APP_MAP_ZOOM, APP_MAP_TILES_URL} = window.appConfig


    let mapObj = null
    let mapDom = null

    const mapClear = () = > {
      if (mapObj) {
        mapDom.innerHTML = ' '
      }
    }
    onUnmounted(() = > {
      mapClear()
    })
    const initMap = () = > {
      mapClear()
      mapDom = document.getElementById('map')
      // set the map center to chengdu, and the local offlineMapTiles just have a chengdu tile with zoom 4
      const center = transform([117.990969.36.635013].'EPSG:4326'.'EPSG:3857');

      mapObj = new Map({
        target: mapDom,
        view: new View({
          center: center,
          zoom: APP_MAP_ZOOM,
          projection: 'EPSG:3857'})})// Add a layer that uses an offline tile map
      const offlineMapLayer = new TileLayer({
        source: new XYZ({
          url: APP_MAP_TILES_URL + '/{z}/{x}/{y}.png'  // Set the path for storing the local offline tile})}); mapObj.addLayer(offlineMapLayer) mapOnClick() addAreaPolygon() getGpsList() watch(() = > props.pointData, () = > {
        addPoint()
      }, {immediate: true.deep: true})}// Map click event
    const mapOnClick = () = > {
      const popupDom = document.getElementById('popup')
      const popup = new Overlay({
        element: popupDom,
        positioning: 'bottom-center'.stopEvent: false
      })
      mapObj.addOverlay(popup)

      mapObj.on('click'.function (evt) {
        const feature = mapObj.forEachFeatureAtPixel(evt.pixel, function (feature) {
          return feature;
        })

        // Determine if a custom popup is clicked based on the click element className
        const isClickPopUp = evt.originalEvent.path.map(item= > item.className).includes('el-card__body')
        if(! isClickPopUp) { popupDom.style.display ='none'
        }

        // The official example uses the JQ + Bootstrap popup, but I don't think it's necessary if you use the Bootstrap component a lot.
        const popupContainer = document.getElementsByClassName('popupContainer') [0]

        if (feature) {
          if ( feature.values_.gpsPointData) {
            const pointData = feature.values_.gpsPointData
            popup.setPosition(evt.coordinate)
            popupContainer.innerHTML = `<div>${pointData.name}</div>`
            popupDom.style.display = 'block'
          }

          emit('mapClickPoint', pointData)
        } else {
          emit('mapClickPoint', {})}})}let mapPointList = []
    let pointLayerSource = null
    let pointLayer = null
    const addPoint = () = > {
      delPointAll()

      props.pointData.map(item= > {
        if (item.checked) {
          item.poi_list.map(pointItem= > {
            pointItem.panelType = item.value
            const point = new Feature({
              geometry: new Point(transform([pointItem.longitude, pointItem.latitude], 'EPSG:4326'.'EPSG:3857')),
              data: pointItem
            })

            const imgSrc = require(`@/assets/img/map/icon_${item.value}.png`)
            const iconStyle = new Style({
              image: new Icon({
                color: '#ffffff'.crossOrigin: 'anonymous'.src: imgSrc,
              }),
            })
            point.setStyle(iconStyle)

            mapPointList.push(point)
          })
        }
      })

      // Data layer
      pointLayerSource = new VectorSource({features: mapPointList})
      pointLayer = new VectorLayer({source: pointLayerSource})
      mapObj.addLayer(pointLayer)
    }
    const delPointAll = () = > {
      if (pointLayerSource) {
        mapPointList.map(item= > {
          pointLayerSource.removeFeature(item)
        })

        mapObj.removeLayer(pointLayer)
        pointLayerSource = null
        pointLayer = null
        mapPointList = []
      }
    }

    // Add a range of counties
    const addAreaPolygon = () = > {
      let geoJson = require('@/mock/zb.json')
      const vectorSource = new VectorSource({
        features: new GeoJSON({featureProjection: 'EPSG:3857'}).readFeatures(geoJson),
      })

      const layer = new VectorLayer({
        source: vectorSource,
        style: new Style({
          stroke: new Stroke({
            color: 'blue'.lineDash: [4].width: 3,}).fill: new Fill({
            color: 'rgba (0, 0, 0, 0.1)',})})}); mapObj.addLayer(layer) }// Todo gets the WS push data injected by providing the home file
    const wsDataInfo = inject('wsDataInfo')
    watch(wsDataInfo, () = > {
      if (wsDataInfo.value) {
        let data = JSON.parse(wsDataInfo.value.data)
        const type = data.pushType + ' '

        console.log(data, 'p[p[[')
        if (type === '2') {
          addGpsPoint([data], false}}}, {immediate: true.deep: true})

    // Get the list of GPS points
    const getGpsList = () = > {
      home.getGpsList().then(res= > {
        if (res.state === 0) {
          // addGpsPoint(res.resultInfo)}})}let gpsPointList = []
    let gpsPointLayerSource = null
    let gpsPointLayer = null
    const addGpsPoint = (data, del = true) = > {

      // Initially loading multiple points...
      let pointList = []
      data.map(item= > {
        const point = new Feature({
          geometry: new Point(transform([item.longitude, item.latitude], 'EPSG:4326'.'EPSG:3857')),
          gpsPointData: item
        })

        const imgSrc = require(`@/assets/img/map/gpsIcon.png`)
        const iconStyle = new Style({
          image: new Icon({
            color: '#ffffff'.crossOrigin: 'anonymous'.src: imgSrc,
          }),
        })
        point.setStyle(iconStyle)

        pointList.push(point)
      })

      // Data layergpsPointList.push(... pointList)if(! gpsPointLayerSource) {// Data layer
        gpsPointLayerSource = new VectorSource({features: gpsPointList})
        gpsPointLayer = new VectorLayer({source: gpsPointLayerSource})
        mapObj.addLayer(gpsPointLayer)
      } else {
        pointList.map(item= > {
          gpsPointLayerSource.addFeature(item)
        })
      }
    }
    const delGpsPointAll = () = > {
      if (gpsPointLayerSource) {
        gpsPointList.map(item= > {
          gpsPointLayer.removeFeature(item)
        })

        mapObj.removeLayer(gpsPointLayer)
        gpsPointLayerSource = null
        gpsPointLayer = null
        gpsPointList = []
      }
    }

    onMounted(() = > {
      initMap()
    })
  }
}
</script>

<style lang="scss" scoped>
.map {
  width: 100%;
  height: 100%;
}
</style>
Copy the code

Is also a novice on the road to see the documentation, if the error or better writing method also please friends pointed out, thank you!