mapbox画瓦片网格

2023-05-10  本文已影响0人  姜治宇

经纬度转瓦片数字

瓦片栅格,就是我们在地图上打格子,比如zoom为0时是顶级,你画几个格子,然后标上行列号,比如0-0,0-1,然后随着zoom的不断变化,格子也不断裂变,行列号也不断变大。
那么,我们在地图上画网格,如何跟瓦片栅格的数字一致呢?
瓦片栅格数字,跟经纬度有个换算关系:

  import mapboxgl from 'mapbox-gl';
  import * as turf from '@turf/turf';
...
  //经纬度转瓦片序列号
  lngLat2Tile(coordinates, zoom) {
    const { x, y } = mapboxgl.MercatorCoordinate.fromLngLat({
      lng: coordinates[0],
      lat: coordinates[1],
    });

    const scale = Math.pow(2, zoom);
    const tileX = Math.floor(x * scale);
    const tileY = Math.floor(y * scale);
    return { x: tileX, y: tileY, z: zoom };
  }

瓦片数字转bbox

那么知道了当前瓦片的数字,如何反推经纬度呢?实际上,一个瓦片对应的是一个格子,那么,我们要得到的,其实是一个边界信息。

var d2r = Math.PI / 180,
  r2d = 180 / Math.PI;

/**
 * Get the bbox of a tile
 *
 * @name tileToBBOX
 * @param {Array<number>} tile
 * @returns {Array<number>} bbox
 * @example
 * var bbox = tileToBBOX([5, 10, 10])
 * //=bbox
 */
function tileToBBOX(tile) {
  var e = tile2lon(tile[0] + 1, tile[2]);
  var w = tile2lon(tile[0], tile[2]);
  var s = tile2lat(tile[1] + 1, tile[2]);
  var n = tile2lat(tile[1], tile[2]);
  return [w, s, e, n];
}
function tile2lon(x, z) {
  return (x / Math.pow(2, z)) * 360 - 180;
}

function tile2lat(y, z) {
  var n = Math.PI - (2 * Math.PI * y) / Math.pow(2, z);
  return r2d * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)));
}

export default {
  tileToBBOX: tileToBBOX,
};

拿到了这个边界,我们可以借助turf库,拿到格子的坐标信息,这样就可以画出这个格子了。

  import mapboxgl from 'mapbox-gl';
  import * as turf from '@turf/turf';
  import tilebelt from '../common/tilebelt';
...
  initGrid() {
      const key = 'draw-grid';
      this.mapboxService.map.addSource(key, {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: [],
        },
      });

      this.mapboxService.map.addLayer({
        id: key,
        type: 'fill',
        source: key,
        paint: {
         // 'fill-color': 'orange',
         // 'fill-opacity': 0.3,
        },
      });
    
  }
  drawGrid(x,y,z){
      const bbox = tilebelt.tileToBBOX([x, y, z]);
      console.log('边界信息>>>', bbox);
      const rect = turf.bboxPolygon([bbox[0], bbox[1], bbox[2], bbox[3]]);
      console.log('rect', rect);
      const featuresArr = [
        {
          type: 'Feature',
          geometry: {
            type: 'Polygon',
            coordinates: rect.geometry.coordinates,
          },
        },
      ];

      this.renderTaskGrid('draw-grid', featuresArr, {
        'fill-color': 'orange',
        'fill-opacity': 0.3,
      });
  }

  renderTaskGrid(key, featureArr, options) {
    //渲染任务格子
    console.log('key', key);
    if (this.mapboxService.map.getSource(key)) {
      this.mapboxService.map.getSource(key).setData({
        //设置数据
        type: 'FeatureCollection',
        features: featureArr,
      });
      // this.mapboxService.map.setPaintProperty(key, 'fill-color', color); //设置格子颜色
      if (Object.keys(options).length > 0) {
        Object.keys(options).map((v) => {
          this.mapboxService.map.setPaintProperty(key, v, options[v]);
        });
      }

    }
  }

格子的外边界

我们画出瓦片的格子其实是填充的正方形,mapbox定义的fill类型的外边界stroke,默认就是1,无法改变。所以,我们要打网格,还要单独画line。

import mapboxgl from 'mapbox-gl';
import * as turf from '@turf/turf';
// import { CustomSource } from '../common/customSource';
import tilebelt from '../common/tilebelt';
...
  polygonKeys = [
      'active-taskgrid',
      'draw-taskgrid-1',
      'draw-taskgrid-2',
  ];
  lineKeys = ['task-grid-draw-line'];
  initGrid() {
    //格子包括边框和填充,polygon无法改变stroke笔触
    this.mapboxService.map.addSource(this.lineKeys[0], {
      type: 'geojson',
      data: {
        type: 'FeatureCollection',
        features: [],
      },
    });

    this.mapboxService.map.addLayer({
      id: this.lineKeys[0],
      type: 'line',
      source: this.lineKeys[0],
      paint: {
        'line-color': 'white',
        'line-width': 2,
      },
    });
    //填充
    for (const key of this.polygonKeys) {
      this.mapboxService.map.addSource(key, {
        type: 'geojson',
        data: {
          type: 'FeatureCollection',
          features: [],
        },
      });

      this.mapboxService.map.addLayer({
        id: key,
        type: 'fill',
        source: key,
        paint: {
          // 'fill-color': 'orange',
          'fill-opacity': 0,
        },
      });
    }

  }

  async drawGrid() {
    const arr = this.getBBox(); //所有可见的格子
    this.drawLine(arr); //画边界线
    const res = [
      {
        name: '格子1',
        status: 1,
        desc: '已完成',
        grid: { x: 27239, y: 12810, z: 15 },
      },
      {
        name: '格子2',
        status: 2,
        desc: '二次标注',
        grid: { x: 27239, y: 12812, z: 15 },
      },
      {
        name:'格子3',
        status:1,
        desc:'已完成',
        grid:{ x: 27244, y: 12809, z: 15 },
      }
    ];
    this.drawPolygon(res);
  }
  clearGrid() {
    this.clearLine();
    this.clearPolygon();
  }
  getBBox() {
    const bboxArr: any = [];
    const arr: any =
      this.mapboxService.map.style._sourceCaches[
        'other:img_tiles'
      ].getVisibleCoordinates();
    // const arr = this.dataSource;
    if (arr && arr.length > 0) {
      for (const item of arr) {
        if (item.overscaledZ === 15) {
          // this.drawGrid(item.canonical.x,item.canonical.y,item.canonical.z,'red');
          const x = item.canonical.x;
          const y = item.canonical.y;
          const z = item.canonical.z;
          const bbox = tilebelt.tileToBBOX([x, y, z]);
          bboxArr.push(bbox);
        }
      }
    }
    return bboxArr;
  }
  drawLine(arr) { //画格子的边界线
    const lineArr: any = [];

    if (arr && arr.length > 0) {
      for (const bbox of arr) {
        const line1 = {
          type: 'Feature',
          geometry: {
            type: 'LineString',
            coordinates: [
              [bbox[0], bbox[1]],
              [bbox[2], bbox[1]],
            ],
          },
        };
        const line2 = {
          type: 'Feature',
          geometry: {
            type: 'LineString',
            coordinates: [
              [bbox[0], bbox[1]],

              [bbox[0], bbox[3]],
            ],
          },
        };
        lineArr.push(line1);
        lineArr.push(line2);
      }
      this.renderTaskGrid(this.lineKeys[0], lineArr, {});
    }
  }

  drawPolygon(arr) {//画填充图形
    const featuresArr: any = [];
    const featuresArr2: any = [];
    // const arr = this.getBBox();
    if (arr && arr.length > 0) {
      for (const item of arr) {
        const bbox = tilebelt.tileToBBOX([
          item.grid.x,
          item.grid.y,
          item.grid.z,
        ]);
        const rect = turf.bboxPolygon([bbox[0], bbox[1], bbox[2], bbox[3]]);
        console.log('rect', rect);

        const feature = {
          type: 'Feature',
          geometry: {
            type: 'Polygon',
            coordinates: rect.geometry.coordinates,
          },
        };
        if (item.status === 1) {
          featuresArr.push(feature);
        } else {
          featuresArr2.push(feature);
        }
      }

      this.renderTaskGrid(this.polygonKeys[1], featuresArr, {
        'fill-color': '#0EBC71',
        'fill-opacity': 0.3,
      }); //画图形
      this.renderTaskGrid(this.polygonKeys[2], featuresArr2, {
        'fill-color': '#5A49F6',
        'fill-opacity': 0.3,
      }); //画图形
    }
  }
  clearLine() {
    this.renderTaskGrid(this.lineKeys[0], [], {});
  }
  clearPolygon() {
    for (const key of this.polygonKeys) {
      this.renderTaskGrid(key, [], {}); //画图形
    }
  }
上一篇下一篇

猜你喜欢

热点阅读