leaflet 叠加新标准分幅网格

简述

这些天在一些项目中遇到一些与标准分幅有关的事情。
大概就是这么一回事,遇到很多次需要获取某点或者某些区域属于哪个标准分幅。虽然可以计算,但是算起来比较麻烦,不如直接地图上看方便。但是我手头没有标准分幅边框的矢量,网上有的也需要下载,于是自己用 javascript 写了一个根据地理范围和比例尺生成范围内标准分幅边框的 geojson矢量的实现,然后在 leaflet 上加载显示出来(要改在 openlayers 之类的也很容易),根据不同的缩放级别显示不同比例尺的标准分幅网格。

生成标准分幅边框矢量的代码


// 获取小数部分
const fractional = function(x) {
   x = Math.abs(x);
   return x - Math.floor(x);
}
const formatInt = function(x, len) {
    let result = '' + x;
    len = len - result.length;
    while(len > 0) {
        result = '0'+result;
        len--;
    }
    return result;
}

// 创建标准分幅网格
// west,south,east,north 传入要创建的标准分幅网格的经纬度范围
// scalem 表示比例尺的分母(例如 10000 表示 1:1万)
// 返回一个 geojson 对象
// solym ymwh@foxmail.com 2020年12月13日
function makeStandardMapGrids(west,south,east,north, scalem) {
    let lngDiff = 0;
    let latDiff = 0;
    let scaleCode = '';
    switch (scalem) {
        case 1000000:
            lngDiff = 6;
            latDiff = 4;
            break;
        case 500000:
            lngDiff = 3;
            latDiff = 2;
            scaleCode = 'B';
            break;
        case 250000:
            lngDiff = 1.5;
            latDiff = 1;
            scaleCode = 'C';
            break;
        case 100000:
            lngDiff = 0.5;
            latDiff = 1 / 3;
            scaleCode = 'D';
            break;
        case 50000:
            lngDiff = 0.25;
            latDiff = 1 / 6;
            scaleCode = 'E';
            break;
        case 25000:
            lngDiff = 0.125;
            latDiff = 1 / 12;
            scaleCode = 'F';
            break;
        case 10000:
            lngDiff = 0.0625;
            latDiff = 1 / 24;
            scaleCode = 'G';
            break;
        case 5000:
            lngDiff = 0.03125;
            latDiff = 1 / 48;
            scaleCode = 'H';
            break;
        default:
            return null;
    }
    const GridX0 = -180;
    const GridX1 = 180;
    const GridY0 = -88;
    const GridY1 = 88;
    let x0 = Math.max(GridX0, west);
    let y0 = Math.max(GridY0, south);
    let x1 = Math.min(GridX1, east);
    let y1 = Math.min(GridY1, north);
    if (((x1 - x0) < lngDiff) || ((y1 - y0) < latDiff)) {
        return null;
    }

    
    let features = [];
    // 计算标准分幅网格行列范围
    const col0 = parseInt((x0 - GridX0) / lngDiff);
    const col1 = parseInt((x1 - GridX0) / lngDiff);
    const row0 = parseInt((y0 - GridY0) / latDiff);
    const row1 = parseInt((y1 - GridY0) / latDiff);
    const millionRowCode = 'ABCDEFGHIJKLMNOPQRSTUV';
    for (let row = row0; row <= row1; row++) {
        let gy0 = GridY0 + row * latDiff;
        let gy1 = gy0 + latDiff;
        let gcy = (gy0+gy1)*0.5;    // 分幅中心点 y 坐标
        let millionRow = parseInt((gy0 - 0)/4); // 1:100分幅行号
        let Hemisphere = '';   // 南北半球标志
        if(millionRow < 0){
            millionRow = -1-millionRow;
            Hemisphere = 'S';
        }
        for (let col = col0; col <= col1; col++) {
            let gx0 = GridX0 + col * lngDiff;
            let gx1 = gx0 + lngDiff;
            let gcx = (gx0+gx1)*0.5;    // 分幅中心点 x 坐标
            let millionCol = parseInt((gcx-GridX0)/6)+1;  // 1:100分幅列号(从1开始)
            coordinates = [[
                [gx0, gy0],
                [gx1, gy0],
                [gx1, gy1],
                [gx0, gy1],
                [gx0, gy0]
            ]];
            millionCol = (millionCol<10)?('0'+millionCol):millionCol;
            let gridID = Hemisphere + millionRowCode[millionRow] + millionCol;
            if(scaleCode != '') {
                // 计算当前分幅在 1:100万 分幅内的行列号
                // 注意,这里行列号从左向右,从北向南,从1开始编号
                let colID = parseInt((fractional((gcx -GridX0)/6)*6)/lngDiff) + 1;
                let rowID = parseInt((fractional((GridY1 - gcy)/4)*4)/latDiff) + 1;
                gridID += scaleCode + formatInt(rowID,3)  + formatInt(colID,3);
            }
            let feature = {
                type: "Feature",
                geometry: {
                    type: "Polygon",
                    coordinates: coordinates
                },
                properties: {
                    ID: gridID,
                    fanwei:'西:'+gx0+' 东:' + gx1 + ' 南:'+gy0+' 北:'+gy1
                }
            };
            features.push(feature);
        }
    }
    return {
        type: "FeatureCollection",
        features: features
    };
}

leaflet 上加载显示代码

<!DOCTYPE html>
<html>

<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
  <title>标准分幅测试页面</title>
  <style>
    html,
    body {
      height: 100%;
      width: 100%;
      overflow: hidden;
      margin: 0;
      padding: 0;
    }

    #map {
      position: relative;
      width: 100%;
      height: 100%;
      background-color: dimgrey;
    }
    #nav{
      position: fixed; 
      top: 5px;
       left: 35%;
       z-index:9999;
       color:lightgreen;
       background-color: rgba(255, 182, 193, 0.384);
    }
  </style>


  <!-- 1.0.3-->
  <script type='text/javascript' src='leaflet_1.7.1.js'></script>
  <link type='text/css' rel='stylesheet' href='leaflet_1.7.1.css' />
</head>

<body>
  <div id="nav"></div>
  <table>
    <tr>
      <div id="map"></div>
    </tr>
  </table>

  <script>
var map = L.map('map', {
    crs: L.CRS.EPSG4326,
    minZoom: 1,
    maxZoom: 25,
    maxBounds: L.latLngBounds([
        [-90, -180],
        [90, 180]
    ]),
    attributionControl: false,
    keyboard: false,
});
map.setView([32.9, 105.4], 4);

var tianditu = L.tileLayer(
    "http://t1.tianditu.com/img_c/wmts?layer=img&style=default&tilematrixset=c&Service=WMTS&Request=GetTile&Version=1.0.0&Format=tiles&TileMatrix={z}&TileCol={x}&TileRow={y}&tk=93724b915d1898d946ca7dc7b765dda5", {
        maxZoom: 17,
        tileSize: 256,
        zoomOffset: 1
    })
map.addLayer(tianditu);
var gridLayer = null;
map.on('moveend', function () {
    let bounds = map.getBounds();
    document.getElementById('nav').innerText = bounds.toBBoxString();
    if (gridLayer != null) {
        gridLayer.remove();
        gridLayer = null;
    }
    const zoom = map.getZoom();
    let scale = 1000000;
    if (zoom > 11) {
        scale = 5000;
    } else if (zoom > 10) {
        scale = 10000;
    } else if (zoom > 9) {
        scale = 25000;
    } else if (zoom > 8) {
        scale = 50000;
    } else if (zoom > 7) {
        scale = 100000;
    } else if (zoom > 6) {
        scale = 250000;
    } else if (zoom > 4) {
        scale = 500000;
    }
    console.log('zoom=', zoom, ' scale=', scale);
    
    let grids = makeStandardMapGrids(bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth(), scale);
    if (grids == null) {
        return;
    }
    // console.log(JSON.stringify(grids));

    gridLayer = L.geoJSON(grids, {
        style: function (feature) {
            return {
                color: 'red',
                weight:1,
                fillColor:'green',
                fillOpacity:0
            };
        }
    }).bindPopup(function (layer) {
        return layer.feature.properties.ID + '<br>' + layer.feature.properties.fanwei;
    });
    gridLayer.addTo(map);

});
map.panTo([32, 116])
  </script>
</body>

</html>

效果截图


posted @ 2020-12-13 16:44  乌合之众  阅读(1461)  评论(4编辑  收藏  举报
clear