定制化天地图-叠加静态图层-并让它动起来

天地图提供了网络地图开发接口,同时有部分web开发的插件,但是本文遇到的场景并不考虑使用天地图本身的插件,而是使用openlayer6.x完成需求。

需求

  • 加载天地图影像图层
  • 加载天地图矢量图层
  • 在图层上叠加静态图片,以增强展示效果
  • 希望图片能动起来-旋转以实现雷达图的效果

技术选型

天地图服务注册

这里使用的是天地图的地图服务数据api,api提供了较多开源接口,大家可以按需使用。

注:开发者需要注册webkey,并受到日访问次数限制,生产环境使用,可以升级企业级或*部门级别,以减少限制。

依赖引入

这里只是openlayer的依赖,vue相关的暂不赘述

//这里只是openlayer的依赖,vue相关的暂不赘述
//openlayer官网的示例的依赖比这里要多,其他的用不到,不需要引入
"ol": "6.9.0"

模块导入

  import 'ol/ol.css';
  import Map from 'ol/Map';
  import TileLayer from 'ol/layer/Tile';
  //WMTS的图层模块
  import WMTS from 'ol/source/WMTS';
  import WMTSTileGrid from 'ol/tilegrid/WMTS';
  //坐标模块
  import {get as getProjection} from 'ol/proj';
  import {getTopLeft, getWidth} from 'ol/extent';
  import View from 'ol/View';
  //图片图层
  import ImageLayer from 'ol/layer/Image';
  import Static from 'ol/source/ImageStatic';
  
  //静态图片的路径
  import mapXWImg from '@/assets/test3.png';

构造影像图层

引入天地图的影响图层

      //影像图层
      let wmtsLayer = new TileLayer({
        opacity: 1,
        source: new WMTS({
          attributions:
            'Tiles © <a href="#"' +
            ' target="_blank">skyocar</a>',
          url: 'http://t{0-7}.tianditu.gov.cn/img_c/wmts?tk=' + this.webKey,
          layer: 'img',
          matrixSet: 'c',
          format: 'image/png',
          projection: this.projection,
          tileGrid: new WMTSTileGrid({
            origin: getTopLeft(projectionExtent),
            resolutions: resolutions,
            matrixIds: matrixIds,
          }),
          style: 'default',
          wrapX: true,
        }),
      });

构造矢量图层

矢量图层的加载是为了地图上展示出地理位置标识等。

//这个是矢量标绘图层,标注底图中的各文字信息
      let cvaLayer = new TileLayer({
        opacity: 0.7,
        source: new WMTS({
          url: 'http://t{0-7}.tianditu.gov.cn/cva_c/wmts?tk=' + this.webKey,
          layer: 'cva',
          matrixSet: 'c',
          format: 'tiles',
          projection: this.projection,
          tileGrid: new WMTSTileGrid({
            origin: getTopLeft(projectionExtent),
            resolutions: resolutions,
            matrixIds: matrixIds,
          }),
          style: 'default',
          wrapX: true,
        }),
      });

加载静态图片层

静态图片的加入可以应对局部地图增强。主要的难点在于中心点定位,这里采用中心定位法,让图片的中心定位到当前屏幕地图中心位置,并设置特定比例的缩放。利用中心点控制、缩放控制,把图片完美的覆盖到地图特定的点位(这也是此次的核心诉求)。

图片图层定位的方法是尝试出来的,并没有找到准确的定位策略,如下。

这里的  -- this.mapCenter = [118.6504, 32.0143]

        /** 叠加图片图层 */
      getImageLayer: function () {
        let p = 0.0001; //图片的缩放尺寸,通过调整图片的加载尺寸和中心点位置,可以调整图片到合适的位置加载
        let w = 1210, h = 1210; //图片的宽、高
        const imageExtent = [
          this.mapCenter[0] - w * p / 2, this.mapCenter[1] - h * p / 2,
          this.mapCenter[0] + w * p / 2, this.mapCenter[1] + h * p / 2
        ];
        return new ImageLayer({
          className: "image_css",
          extent: imageExtent,
          source: new Static({
            url: mapXWImg,
            projection: this.projection,
            imageExtent: imageExtent,
            dragPan: false
          }),
        })
      },

加载地图主类

<template>
  <div class="app-container">
    <div id="map" class="map" ref="rootmap">

    </div>
  </div>
</template>
<style lang='scss'>
  .map {
    width: 100%;
    height: 800px;
  }

  #mouse-position {
    float: right;
    position: absolute;
    right: 3px;
    width: 200px;
    height: 20px;
    /* 将z-index设置为显示在地图上层 */
    z-index: 2000;
  }

</style>
      this.pageMap = new Map({
        layers: [
          wmtsLayer,
          cvaLayer,
          imageLayer,
        ],
        target: 'map',
        view: new View({
          center: this.mapCenter,
          projection: this.projection,
          zoom: 13,
        }),
      });

效果如下: 

定制化天地图-叠加静态图层-并让它动起来

让静态图片层动起来

这里是为了应对雷达图的效果,请UI同学切了个雷达图片,然后我控制旋转,就完成了雷达图了。这里是使用css3让地图动起来的,代码如下:

 @-webkit-keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
    to {
      -webkit-transform: rotate(360deg);
    }
  }

  .image_css {
    -webkit-transform: rotate(360deg);
    animation: rotation 3s linear infinite;
    -moz-animation: rotation 3s linear infinite;
    -webkit-animation: rotation 3s linear infinite;
    -o-animation: rotation 3s linear infinite;
  }

效果如下: 

 定制化天地图-叠加静态图层-并让它动起来

完整的代码

<template>
  <div class="app-container">
    <div id="map" class="map" ref="rootmap">

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

<script>
  import 'ol/ol.css';
  import Map from 'ol/Map';
  import TileLayer from 'ol/layer/Tile';
  import WMTS from 'ol/source/WMTS';
  import WMTSTileGrid from 'ol/tilegrid/WMTS';
  import {get as getProjection} from 'ol/proj';
  import {getTopLeft, getWidth} from 'ol/extent';
  import View from 'ol/View';
  import ImageLayer from 'ol/layer/Image';
  import Static from 'ol/source/ImageStatic';
  //静态图片的路径
  import mapXWImg from '@/assets/test3.png';

  export default {
    name: 'OlMap',
    props: ['tableData', 'targetPoint'],
    data() {
      return {
        map1: null,
        view: null,
        vectorSource: null,
        mousePositionControl: null,
        /** webKey 来源于天地图的开发者密钥,个人开发者有访问限制(每天1万次),
         * 上线项目应该使用客户的信息申请企业或*,以解除访问限制 https://console.tianditu.gov.cn/api/traffic */
        webKey: '369e7d3985dee029f58564a7747e0dd0',
        // webKey: '2a7fadcac8665927c912c4de29ada090',
        mapCenter: [118.6504, 32.0143], //底图初始中心点
        projection: getProjection('EPSG:4326'),
        pageMap: null
      }
    },
    created() {
    },
    activated() {
    },
    mounted() {
      const projectionExtent = this.projection.getExtent();
      const size = getWidth(projectionExtent) / 256;
      const resolutions = new Array(19);
      const matrixIds = new Array(19);
      for (let z = 0; z < 19; ++z) {
        // generate resolutions and matrixIds arrays for this WMTS
        resolutions[z] = size / Math.pow(2, z);
        matrixIds[z] = z;
      }
      let imageLayer = this.getImageLayer();
      imageLayer.on("change", function (event) {

      });

      //影像图层
      let wmtsLayer = new TileLayer({
        opacity: 1,
        source: new WMTS({
          attributions:
            'Tiles © <a href="#"' +
            ' target="_blank">skyocar</a>',
          url: 'http://t{0-7}.tianditu.gov.cn/img_c/wmts?tk=' + this.webKey,
          layer: 'img',
          matrixSet: 'c',
          format: 'image/png',
          projection: this.projection,
          tileGrid: new WMTSTileGrid({
            origin: getTopLeft(projectionExtent),
            resolutions: resolutions,
            matrixIds: matrixIds,
          }),
          style: 'default',
          wrapX: true,
        }),
      });
      //这个是矢量标绘图层,标注底图中的各文字信息
      let cvaLayer = new TileLayer({
        opacity: 0.7,
        source: new WMTS({
          url: 'http://t{0-7}.tianditu.gov.cn/cva_c/wmts?tk=' + this.webKey,
          layer: 'cva',
          matrixSet: 'c',
          format: 'tiles',
          projection: this.projection,
          tileGrid: new WMTSTileGrid({
            origin: getTopLeft(projectionExtent),
            resolutions: resolutions,
            matrixIds: matrixIds,
          }),
          style: 'default',
          wrapX: true,
        }),
      });

      this.pageMap = new Map({
        layers: [
          wmtsLayer,
          cvaLayer,
          imageLayer,
        ],
        target: 'map',
        view: new View({
          center: this.mapCenter,
          projection: this.projection,
          zoom: 13,
        }),
      });

    },
    methods: {

      /** 叠加图片图层 */
      getImageLayer: function () {
        let p = 0.0001; //图片的缩放尺寸,通过调整图片的加载尺寸和中心点位置,可以调整图片到合适的位置加载
        let w = 1210, h = 1210; //图片的宽、高
        const imageExtent = [
          this.mapCenter[0] - w * p / 2, this.mapCenter[1] - h * p / 2,
          this.mapCenter[0] + w * p / 2, this.mapCenter[1] + h * p / 2
        ];
        return new ImageLayer({
          className: "image_css",
          extent: imageExtent,
          source: new Static({
            url: mapXWImg,
            projection: this.projection,
            imageExtent: imageExtent,
            dragPan: false
          }),
        })
      },

    }

  };
</script>
<style lang='scss'>

  .map {
    width: 100%;
    height: 800px;
  }
  @-webkit-keyframes rotation {
    from {
      -webkit-transform: rotate(0deg);
    }
    to {
      -webkit-transform: rotate(360deg);
    }
  }
  .image_css {
    -webkit-transform: rotate(360deg);
    animation: rotation 3s linear infinite;
    -moz-animation: rotation 3s linear infinite;
    -webkit-animation: rotation 3s linear infinite;
    -o-animation: rotation 3s linear infinite;
  }
</style>

未解决的问题

虽然实现了效果,但依然有瑕疵未完全解决,对于球面坐标系和桌面坐标系的理解依然不足,希望有心人可以指导一二。

  1. 因地图坐标系和桌面坐标系的不同,暂时未找到保持一致的算法,一旦地图拖动,会导致雷达图旋转点错位;
  2. 静态图片叠加的定位算法有待优化,需要找到更适合的算法,以便于快速定位。

上一篇:在OpenGL ES和Android中处理大型位图


下一篇:OpenGL Indirect Culling间接剔除实例