WebGIS:前端:给出地理范围计算出地图瓦片的行列号

目录

前端代码实现

根据xml配置文件计算出行列号

1、xml配置文件信息样例

 2、代码实现

运用到的知识

1、像素和米的换算

2、分辨率的计算

3、经纬度和长度米的换算

瓦片行列号的计算逻辑

 1、计算出比例尺分母;

2、 算出层级和比例尺分母

3、计算出分辨率resolution

4、根据上述步骤的取值,计算出行列号


该文档是根据本人做的项目进行的总结,可能存在知识的不准确性,仅做参考;

前端代码实现

功能:根据提供一个瓦片服务地址,解析服务的元数据,获取到范围,根据范围,计算出行列号

根据xml配置文件计算出行列号

1、xml配置文件信息样例

 2、代码实现

1、解析xml数据为json格式的数据,借助插件x2js

2、本例中,只做了3857和4326坐标系的支持;请求用axios进行请求

3、因为文档中的比例尺分母使用的是米,所以4236中,如果没有提供level时,将度数转为米进行匹配层级信息的

4、4326坐标系的地球半径取值为:6371004;3857坐标系的地球半径取值为:6378137;

5、可以直接copy代码使用,使用中注意度和米的单位问题

import X2JS from 'x2js';

// 1、请求配置文件路径,获取元数据,通过插件x2js解析xml数据为json格式
let url = 'http://xxxxx';
axios.get(url).then((res) => {
    if (res.status != 200) {
        alert('请检查您的配置文件路径');
        return false;
    }
    var x2js = new X2JS();
    var json = x2js.xml2js(res.data);
    getColRowLevel({data:json,dpi:90.714});
});

// 2、 根据获取的元数据,获取行列号和层级
const getColRowLevel = (json) => {
    let { data, dpi, level } = json;
    let Contents = data.Capabilities.Contents;
    let TileMatrixSet = Contents.TileMatrixSet;
    // 元数据中的crs坐标系
    let SupportedCRS = TileMatrixSet.SupportedCRS.__text || TileMatrixSet.SupportedCRS;
    
    // 默认crs是3857
    //默认dpi为90.714,dpi一般为90.714或者96;如果不确定dpi请咨询提供服务的后台
    dpi = dpi ? Number(dpi) : 90.714;
    level = level ? Number(level) : null;
    if (SupportedCRS.toLowerCase().includes('epsg::4326')) {
        // 4326
        return colRowLevel4326(Contents, dpi, level);
    } else {
        // 3857
        return colRowLevel3857(Contents, dpi, level);
    }
};



const colRowLevel4326 = (Contents, dpi, level) => {
    let Layer = Contents.Layer;
   
    // 服务范围,如果给指定范围,则去掉下面四行,直接let extentObj = {minX,minY,maxX,maxY}
    let WGS84BoundingBox = Layer.WGS84BoundingBox;
    let LowerCorner = WGS84BoundingBox.LowerCorner.__text;
    let UpperCorner = WGS84BoundingBox.UpperCorner.__text;
    let extentObj = getExtent4326(LowerCorner, UpperCorner);
    let { minX, maxX, minY, maxY } = extentObj;

    let canvasWidth = 1000;
    let tileSize = 256;
    let realTileWidthDegree = (maxX - minX) / (canvasWidth / tileSize);
    let realTileWidthMeter = (realTileWidthDegree / 180) * (Math.PI * 6371004);

    let resolutions = realTileWidthMeter / tileSize;
    let scaleDenominator = resolutions / (0.0254 / dpi);

    // // Identifier  ScaleDenominator TileHeight  TopLeftCorner
    let TileMatrixSet = Contents.TileMatrixSet;
    let TileMatrixArr = TileMatrixSet.TileMatrix;
    // 当前比例尺最接近的对象
    let curScaleObj = getScaleInfo(TileMatrixArr,scaleDenominator,level);
    
    if (curScaleObj) {
        // Identifier  ScaleDenominator TileWidth  TopLeftCorner
        let { ScaleDenominator, TopLeftCorner, Identifier, TileWidth } = curScaleObj;
        Identifier = Identifier.__text;
        ScaleDenominator = eToNumber(ScaleDenominator);
        let originObj = TopLeftCorner.split(' ');
        let originX = originObj[1];
        let originY = originObj[0];

        var resolution = (((0.0254 / dpi) * ScaleDenominator) / (Math.PI * 6371004)) * 180;
        let minCol = Math.floor(Math.abs(originX - minX) / (resolution * tileSize));
        let minRow = Math.floor(Math.abs(originY - maxY) / (resolution * tileSize));
        let maxCol = Math.ceil(Math.abs(originX - maxX) / (resolution * tileSize));
        let maxRow = Math.ceil(Math.abs(originY - minY) / (resolution * tileSize));
        return {
            minCol,
            minRow,
            maxCol,
            maxRow,
            level: Identifier
        };
    }
    return false;
};

const colRowLevel3857 = (Contents, dpi, level) => {
    let Layer = Contents.Layer;
    
    // 服务范围,如果给指定范围,则去掉下面四行,直接let extentObj = {minX,minY,maxX,maxY}
    let WGS84BoundingBox = Layer.WGS84BoundingBox;
    let LowerCorner = WGS84BoundingBox.LowerCorner.__text;
    let UpperCorner = WGS84BoundingBox.UpperCorner.__text;
    let extentObj = getExtent3857(LowerCorner, UpperCorner);
    let { minX, maxX, minY, maxY } = extentObj;

    let canvasWidth = 1000;
    let tileSize = 256;
    let realTileWidth = (maxX - minX) / (canvasWidth / tileSize);
    let resolutions = realTileWidth / tileSize;
    let scaleDenominator = resolutions / (0.0254 / dpi);

    // Identifier  ScaleDenominator TileHeight  TopLeftCorner
    let TileMatrixSet = Contents.TileMatrixSet;
    let TileMatrixArr = TileMatrixSet.TileMatrix;
    // 当前比例尺最接近的对象
    let curScaleObj = getScaleInfo(TileMatrixArr,scaleDenominator,level);

    if (curScaleObj) {
        // Identifier  ScaleDenominator TileWidth  TopLeftCorner
        let { ScaleDenominator, TopLeftCorner, Identifier, TileWidth } = curScaleObj;
        Identifier = Identifier.__text;
        ScaleDenominator = eToNumber(ScaleDenominator);
        let originObj = getOrigin(TopLeftCorner);
        let originX = originObj.x;
        let originY = originObj.y;
       
        let resolution = (0.0254 / dpi) * ScaleDenominator;

        let minCol = Math.floor(Math.abs(originX - minX) / (resolution * tileSize));
        let minRow = Math.floor(Math.abs(originY - maxY) / (resolution * tileSize));
        let maxCol = Math.ceil(Math.abs(originX - maxX) / (resolution * tileSize));
        let maxRow = Math.ceil(Math.abs(originY - minY) / (resolution * tileSize));
        return {
            minCol,
            minRow,
            maxCol,
            maxRow,
            level: Identifier
        };
    }
    return false;
};

// 得到当前比例尺信息
const getScaleInfo = (TileMatrixArr,scaleDenominator,level)=>{
    let curScaleObj = null;
    let neastScaleD = undefined;
    if (level) {
        curScaleObj = TileMatrixArr.find((item) => item.Identifier.__text == level);
    }
    if (!curScaleObj) {
        TileMatrixArr.forEach((item) => {
            let ScaleDenominator = eToNumber(item.ScaleDenominator);
            let dist = Math.abs(scaleDenominator - ScaleDenominator);
            if (neastScaleD == undefined) {
                neastScaleD = dist;
                curScaleObj = item;
            } else if (dist < neastScaleD) {
                neastScaleD = dist;
                curScaleObj = item;
            }
        });
    }
    return curScaleObj;
}

// 得到origin
const getOrigin = (TopLeftCorner) => {
    let arr = TopLeftCorner.split(' ');
    return { x: eToNumber(arr[0]), y: eToNumber(arr[1]) };
};
// 将自然计数转化为数字
const eToNumber = (eNum) => {
    eNum = (eNum + '').toLowerCase();
    if (eNum.includes('e')) {
        let arr = eNum.split('e');
        return Number(arr[0]) * Math.pow(10, arr[1]);
    }
    return Number(eNum);
};

// 得到范围的坐标点
const getExtent3857 = (LowerCorner, UpperCorner) => {
    let lowerArr = LowerCorner.split(' ');
    let upperArr = UpperCorner.split(' ');
    let min = WGS84ToMercator({ lng: lowerArr[0], lat: lowerArr[1] });
    let max = WGS84ToMercator({ lng: upperArr[0], lat: upperArr[1] });
    return {
        minX: min.lng,
        minY: min.lat,
        maxX: max.lng,
        maxY: max.lat
    };
};
const getExtent4326 = (LowerCorner, UpperCorner) => {
    let lowerArr = LowerCorner.split(' ');
    let upperArr = UpperCorner.split(' ');
    return {
        minX: lowerArr[0],
        minY: lowerArr[1],
        maxX: upperArr[0],
        maxY: upperArr[1]
    };
};

运用到的知识

1、像素和米的换算

1英寸 = 2.54厘米 = 0.0254米

1英寸 = 96像素

所以:1像素 = (0.0254/dpi)米

dpi一般取值96或者90.714,dpi的值不确定的话,需要咨询提供瓦片服务的人

2、分辨率的计算

分辨率 =  (0.0254/dpi)*比例尺分母;

3、经纬度和长度米的换算

这种换算方式,我测试后验证应该是在4326坐标系下的换算;

换算时,注意地球半径的取值:4326坐标系的地球半径取值为:6371004;3857坐标系的地球半径取值为:6378137;

长度转经纬度:
degree = meter / (2 * Math.PI * 6371004) * 360 = meter / (Math.PI * 6371004) * 180;

degree =  meter / (Math.PI * 6371004) * 180;

meter = degree/180* (Math.PI * 6371004)

米转度 粗暴:
degree = meter/111194

米转度 6371000 为地球赤道半径(待证)
Math.asin(radius / 6371000) * 180 / Math.PI

1度=π/180弧度

瓦片行列号的计算逻辑

已知数据:通过各种途径可以获取到的 

originX,originY:原点坐标,(可以在配置文件中获取,一般3857的为[-2.003750834E7 2.003750834E7],4326的为[-180,90])

canvasWidth,canvasHeight:画布的宽高

tileSize: 瓦片的宽,在画布上的瓦片的宽

minGeoX,minGeoY,maxGeoX,maxGeoY:要计算的实际坐标的范围,两个坐标点的最大值和最小值 (要计算行列号给的范围)

 1、计算出比例尺分母;

(如果告知了层级(level),就可以直接获取比例尺分母,忽略此步骤)

realTileWidth:实际瓦片的宽度

realTileWidth = (minGeoX - originX) / (canvasWidth / tileSize);

realTileWidth = 分辨率 * tileSize;

分辨路 =  (0.0254/dpi)*比例尺分母;

上述三个公式,可以推到比例尺分母

比例尺分母 = (minGeoX - originX) / (canvasWidth / tileSize)/tileSize/(0.0254/dpi);

2、 算出层级和比例尺分母

根据步骤1中获取的比例尺分母,循环配置中的瓦片的层级矩阵数据,得到最接近层级(level),并取得那个层级对应的比例尺分母ScaleDenominator

3、计算出分辨率resolution

分辨路的公式是: (0.0254 / dpi) * 比例尺分母

使用中,注意 单位 度和米的转换;

// 4326中,如果获取的比例尺分母单位是米,需要这样转化一下,如果是度,和3857一样就可以了

 var resolution = (((0.0254 / dpi) * ScaleDenominator) / (Math.PI * 6371004)) * 180;

// 3857中

let resolution = (0.0254 / dpi) * ScaleDenominator;

4、根据上述步骤的取值,计算出行列号

最小行列号数据向下取整最大行列号数据向上取整,以获取包含完整坐标范围的行列号

         // 最小列号

        let minCol = Math.floor(Math.abs(originX - minX) / (resolution * tileSize));

         // 最小行号

        let minRow = Math.floor(Math.abs(originY - maxY) / (resolution * tileSize));

         // 最大列号   

        let maxCol = Math.ceil(Math.abs(originX - maxX) / (resolution * tileSize));

         // 最大行号

        let maxRow = Math.ceil(Math.abs(originY - minY) / (resolution * tileSize));

下面这张是在网上看到的图,标注的非常清楚,可以帮助理解 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪落满地香

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值