• 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%;
           100%;
          overflow: hidden;
          margin: 0;
          padding: 0;
        }
    
        #map {
          position: relative;
           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>
    

    效果截图


  • 相关阅读:
    barcode制作条形码及破解
    软件敏捷架构师
    软件需求分析三步走
    GDI+显示GIF动画
    CSpinButtonCtrl的弱智问题
    [C++] STL里面的map
    [C#] 再议Exception
    [C++] unsigned是麻烦制造者
    用GDI+转BMP为WMF、EXIF、EMF格式
    [C++] 编译时的warning
  • 原文地址:https://www.cnblogs.com/oloroso/p/14129066.html
Copyright © 2020-2023  润新知