前端进阶之---GIS可视化leaflet的使用

0 阅读4分钟

如果你正在做可视化地图相关的业务或者功能的话,希望本篇文章能够给你提供帮助

1. leaflet的介绍

取自官网的介绍(leafletjs.cn/):Leaflet 是一个开源并且对移动端友好的交互式地图 JavaScript 库。 它大小仅仅只有 42 KB of JS, 并且拥有绝大部分开发者所需要的所有地图特性 。

Leaflet 简单、高效并且易用。 它可以高效的运行在桌面和移动平台, 拥有着大量的 扩展插件、 优秀的文档、简单易用的 API 和完善的案例, 以及可读性较好的 源码 。

功能很强大,不管是PC端还是移动端,都有他的用武之地

2. leaflet在vue3项目中的简单使用

  1. 首先引入leaflet包
npm install leaflet
  1. 初始化一个地图画面
// 结构
<template>
  <div class="portal">
      <div class="map-content" id="mapId"></div>
  </div>
</template>

// js代码
var map = L.map('map').setView([51.505, -0.09], 13); L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png',
{ attribution: '&copy; 
<a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); 
L.marker([51.5, -0.09]).addTo(map) .bindPopup('A pretty CSS3 popup.<br> Easily customizable.') .openPopup();

以上是按照官网实现的一个简单的地图功能,我们平常开发中一般都需要自己去寻找瓦片,然后根据自己的业务来实现地图

leaflet在真实项目中的使用

  1. 首先我们先创建一个文件夹lib,存储leaflet自定义的一些文件

image.png

  1. 然后将自定义的文件leaflet.ChineseTmsProviders和Leaflet.chartLayer创建一下
// leaflet.ChineseTmsProviders文件

// this L.CRS.Baidu from https://github.com/muyao1987/leaflet-tileLayer-baidugaode/blob/master/src/tileLayer.baidu.js

if (L.Proj) {
  L.CRS.Baidu = new L.Proj.CRS(
    'EPSG:900913',
    '+proj=merc +a=6378206 +b=6356584.314245179 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext  +no_defs',
    {
      resolutions: (function () {
        var level = 19
        var res = []
        res[0] = Math.pow(2, 18)
        for (var i = 1; i < level; i++) {
          res[i] = Math.pow(2, 18 - i)
        }
        return res
      })(),
      origin: [0, 0],
      bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244]),
    }
  )
}

L.TileLayer.ChinaProvider = L.TileLayer.extend({
  initialize: function (type, options) {
    // (type, Object)
    var providers = L.TileLayer.ChinaProvider.providers

    options = options || {}

    var parts = type.split('.')

    var providerName = parts[0]
    var mapName = parts[1]
    var mapType = parts[2]

    var url = providers[providerName][mapName][mapType]
    options.subdomains = providers[providerName].Subdomains
    options.key = options.key || providers[providerName].key

    if ('tms' in providers[providerName]) {
      options.tms = providers[providerName]['tms']
    }

    L.TileLayer.prototype.initialize.call(this, url, options)
  },
})

L.TileLayer.ChinaProvider.providers = {
  TianDiTu: {
    Normal: {
      Map: '//t{s}.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk={key}',
      Annotion:
        '//t{s}.tianditu.gov.cn/DataServer?T=cva_w&X={x}&Y={y}&L={z}&tk={key}',
    },
    Satellite: {
      Map: '//t{s}.tianditu.gov.cn/DataServer?T=img_w&X={x}&Y={y}&L={z}&tk={key}',
      Annotion:
        '//t{s}.tianditu.gov.cn/DataServer?T=cia_w&X={x}&Y={y}&L={z}&tk={key}',
    },
    Terrain: {
      Map: '//t{s}.tianditu.gov.cn/DataServer?T=ter_w&X={x}&Y={y}&L={z}&tk={key}',
      Annotion:
        '//t{s}.tianditu.gov.cn/DataServer?T=cta_w&X={x}&Y={y}&L={z}&tk={key}',
    },
    Subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
    key: 'b0c36010bb50280fd8cff2311c58e689',
  },

  GaoDe: {
    Normal: {
      Map: '//webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
    },
    Satellite: {
      Map: '//webst0{s}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
      Annotion:
        '//webst0{s}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}',
    },
    Subdomains: ['1', '2', '3', '4'],
  },

  Google: {
    Normal: {
      Map: '//www.google.cn/maps/vt?lyrs=m@189&gl=cn&x={x}&y={y}&z={z}',
    },
    Satellite: {
      Map: '//www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}',
      Annotion: '//www.google.cn/maps/vt?lyrs=y@189&gl=cn&x={x}&y={y}&z={z}',
    },
    Subdomains: [],
  },

  Geoq: {
    Normal: {
      Map: '//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer/tile/{z}/{y}/{x}',
      PurplishBlue:
        '//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}',
      Gray: '//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetGray/MapServer/tile/{z}/{y}/{x}',
      Warm: '//map.geoq.cn/ArcGIS/rest/services/ChinaOnlineStreetWarm/MapServer/tile/{z}/{y}/{x}',
    },
    Theme: {
      Hydro:
        '//thematic.geoq.cn/arcgis/rest/services/ThematicMaps/WorldHydroMap/MapServer/tile/{z}/{y}/{x}',
    },
    Subdomains: [],
  },

  OSM: {
    Normal: {
      Map: '//{s}.tile.osm.org/{z}/{x}/{y}.png',
    },
    Subdomains: ['a', 'b', 'c'],
  },

  SHIPFORMULA: {
    OSM: {
      Map: 'https://maps.shipformula.com/wmts/osm/webmercator/{z}/{x}/{y}.png',
    },
    DT: {
      Map: 'https://maps.shipformula.com/wmts/dtmap/webmercator/{z}/{x}/{y}.png',
    },
    Satellite: {
      Map: 'https://maps.shipformula.com/wmts/satellite/webmercator/{z}/{x}/{y}.png',
    },
    ZHSatellite: {
      Map: 'https://maps.shipformula.com/wmts/zhsatellite/webmercator/{z}/{x}/{y}.png',
    },
    Subdomains: [],
  },
  Baidu: {
    Normal: {
      Map: '//online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1',
    },
    Satellite: {
      Map: '//shangetu{s}.map.bdimg.com/it/u=x={x};y={y};z={z};v=009;type=sate&fm=46',
      Annotion:
        '//online{s}.map.bdimg.com/tile/?qt=tile&x={x}&y={y}&z={z}&styles=sl&v=020',
    },
    Subdomains: '0123456789',
    tms: true,
  },

  SHIPXY: {
    Normal: {
      Map: 'http://m12.shipxy.com/tile.c?l=Na&m=o&x={x}&y={y}&z={z}',
    },
    Subdomains: [],
  },

  SF: {
    Normal: {
      Map: 'http://map.shipformula.com/styles/klokantech-basic/{z}/{x}/{y}.png',
      Bright: 'http://map.shipformula.com/styles/osm-bright/{z}/{x}/{y}.png',
      Dark: 'http://map.shipformula.com/styles/dark-matter/{z}/{x}/{y}.png',
      Positron: 'http://map.shipformula.com/styles/positron/{z}/{x}/{y}.png',
    },
    Satellite: {
      Map: 'http://map.shipformula.com/styles/satellite/{z}/{x}/{y}.png',
      Hybrid: 'http://map.shipformula.com/styles/hybrid/{z}/{x}/{y}.png',
    },
    Subdomains: [],
  },

  Local: {
    Normal: {
      Map: 'http://192.168.30.229:8083/styles/klokantech-basic/{z}/{x}/{y}.png',
      Bright: 'http://192.168.30.229:8083/styles/osm-bright/{z}/{x}/{y}.png',
      Dark: 'http://192.168.30.229:8083/styles/dark-matter/{z}/{x}/{y}.png',
      Positron: 'http://192.168.30.229:8083/styles/positron/{z}/{x}/{y}.png',
    },
    Satellite: {
      Map: 'http://192.168.30.229:8083/styles/satellite/{z}/{x}/{y}.png',
      Hybrid: 'http://192.168.30.229:8083/styles/hybrid/{z}/{x}/{y}.png',
    },
    Subdomains: [],
  },
}

L.tileLayer.chinaProvider = function (type, options) {
  return new L.TileLayer.ChinaProvider(type, options)
}
···

```js
// Leaflet.chartLayer
// import Constants from '../config/Constants';
// const CHART_URL = ['http://shipdt-emap.shipdt.com/emap_36/']
// const CHART_URL = ['http://emap.n.shipdt.com/emap_6/']
const CHART_URL = ['https://www.shipformula.com/emap_6/']
const CHART_ERROR = 'https://mobile.shipformula.com/static/images/error.png'
/**
 * 海图Layer 继承自TileLayer
 *
 * @param {Object}
 *            tomcat中映射该切片目录url
 * @param {Object}
 *            options
 *            http://enc-cmap2.myships.com/ChartMap/L02/R00000001/C00000003.png
 */
L.TileLayer.ChartLayer = L.TileLayer.extend({
  initialize(url, options) {
    options = L.setOptions(this, options)
    options.errorTileUrl = CHART_ERROR
    const tempurl = CHART_URL[Math.floor(Math.random() * CHART_URL.length)]
    this.url = `${tempurl}{s}/{x}.png`
    L.TileLayer.prototype.initialize.call(this, this.url, options)
  },
})
/**
 * 重写TileLayer中获取切片url方法
 *
 * @param {Object}
 *            tilePoint
 */
L.TileLayer.ChartLayer.prototype.getTileUrl = function (tilePoint) {
  return L.Util.template(
    this._url,
    L.extend({
      s() {
        const zl = tilePoint.z
        const ty = tilePoint.y
        let dir = ''
        let levDir = ''
        let rowDir = ''
        if (zl < 2) {
          levDir = `LN${zl.toString()}`
        } else if (zl < 12) {
          levDir = `L0${(zl - 2).toString()}`
        } else {
          levDir = `L${(zl - 2).toString()}`
        }
        rowDir = `R${L.tileLayer.getHexString(ty)}`
        dir = `${levDir}/${rowDir}`
        return dir
      },
      x: `C${L.tileLayer.getHexString(tilePoint.x)}`,
    })
  )
}
L.tileLayer.getHexString = function (value) {
  let strHex = value.toString(16)
  switch (strHex.length) {
    case 1:
      strHex = `0000000${strHex}`
      break
    case 2:
      strHex = `000000${strHex}`
      break
    case 3:
      strHex = `00000${strHex}`
      break
    case 4:
      strHex = `0000${strHex}`
      break
    case 5:
      strHex = `000${strHex}`
      break
    case 6:
      strHex = `00${strHex}`
      break
    case 7:
      strHex = `0${strHex}`
      break
    default:
  }
  return strHex
}
let chartLyaer = null
const ChartLayer = function (url, options) {
  if (!chartLyaer) {
    chartLyaer = new L.TileLayer.ChartLayer(url, options)
  }
  return chartLyaer
}

export default {
  ChartLayer,
}
  1. 接下来我们在vue项目里引入创建的两个js文件
<!-- 工作台 -->
<template>
  <div class="portal">
    <div class="map">
      <div class="map-content" id="mapId"></div>
    </div>
  </div>
</template>
<script setup>
import 'leaflet/dist/leaflet.css'
import L from 'leaflet'
import '@/lib/leaflet.ChineseTmsProviders'
import 'leaflet-providers'
import SeaMap from '@/lib/Leaflet.chartLayer'
import 'leaflet.markercluster'
import 'leaflet.markercluster.placementstrategies'
onMounted(() => {
  initMap()
})
// 地图相关数据
const state = reactive({
  glbmap: null,
})
const initMap = async () => {
  const osm = L.tileLayer.chinaProvider('GaoDe.Normal.Map', {
    preferCanvas: true,
    maxZoom: 18,
    minZoom: 3,
    name: 'osm',
  })

  const google = L.tileLayer.chinaProvider('GaoDe.Satellite.Map', {
    preferCanvas: true,
    maxZoom: 18,
    minZoom: 3,
    name: 'google',
  })
  const googletdt = L.tileLayer.chinaProvider('GaoDe.Satellite.Annotion', {
    preferCanvas: true,
    maxZoom: 18,
    minZoom: 3,
    name: 'googletdt',
  })
  const seaMap = SeaMap.ChartLayer('', {
    preferCanvas: true,
    name: 'seaMap',
  })
  const baseLayers = {
    默认图: osm,
    卫星图: L.layerGroup([google, googletdt]),
    海图: seaMap,
  }
  state.glbmap = L.map('mapId', {
    preferCanvas: true,
    attributionControl: false,
    renderer: L.canvas(),
    center: [34.22088, 121.14048],
    zoom: 6,
    layers: [osm],
    zoomControl: false,
    worldCopyJump: true,
    zoomSnap: 1,
    zoomDelta: 1,
    wheelPxPerZoomLevel: 120,
    crs: L.CRS.EPSG3857,
  })
  let mapControl = L.control.layers(baseLayers).addTo(state.glbmap)
  mapControl.setPosition('topright')
}
</script>
<style lang="scss" scoped>
.portal {
  width: 100%;
  height: calc(100vh - 91px);
  box-sizing: border-box;
  position: relative;
}
.map {
  width: 100%;
  height: 100%;
  .map-content {
    width: 100%;
    height: 100%;
  }
}
</style>

接下来我们就可以看到地图效果了

image.png

后续篇章我们就在地图做一些交互效果