北斗网格位置码详解:经纬度到二维网格码的转换(非极地)

该文章已生成可运行项目,

在全球定位系统(GPS)和北斗卫星导航系统(BDS)的发展下,经纬度坐标已经成为描述地球表面位置的标准方式。然而,在某些特定的应用场景中,如城市交通管理、物流配送、无人机路径规划等,直接使用经纬度坐标存在一些局限性,例如数据量大、查询效率低等问题。
北斗网格码是一种基于地理位置的编码系统,它将地球表面划分为多个层级的网格,并为每个网格分配唯一的编码。这种编码方式可以有效地压缩地理数据,提高空间索引的效率,便于大规模地理数据的管理和分析。

规范见百度百科内容范围章节图片:北斗网格位置码_百度百科

1. 北斗网格位置码介绍

北斗网格位置码是利用北斗卫星导航系统的定位能力,对地球表面进行分层划分的一种网格编码方式。每个网格都有一个唯一的标识符,该标识符可以表示一定范围内的地理位置信息。其主要特点包括:

  1. 唯一性:每个网格编码在整个地球上是唯一的。
  2. 层级性:网格可以按照精度需求进行多级细分,例如从大区域到小区域逐步细化。
  3. 高效性:通过网格编码,可以快速检索和管理地理数据,提升查询效率。

2. 经纬度与北斗网格编码的关系

经纬度是以地球的经线和纬线为基础的坐标系统,用于描述地球上的任意一点。而北斗网格编码则是将这些点映射到特定的网格中,从而实现更高效的管理和计算。其转换原理为:

  1. 定义网格大小:根据应用需求,定义不同级别的网格大小。
  2. 划分网格:将经纬度范围按照预设的网格大小划分为若干个矩形或正方形网格。
  3. 生成编码:为每个网格分配一个唯一的编码。编码规则可以根据需要设计,例如使用二进制编码、十进制编码或其他自定义编码格式。
  4. 转换算法:根据给定的经纬度,确定其所在的网格,并返回对应的编码。

3. 经纬度转北斗网格编码的实现步骤

北斗二维网格位置码编码规则由最多不超过20个码元组成,按照从左到右的顺序分成十一段,分别对应地球表面南北半球以及第一级至第十级网格,北斗二维网格位置码编码结构与代码取值如下图所示:

下面,我在四个半球各取一个点,分别以西北半球 116°18'45.37"W,39°59'35.38"N ,东北半球 116°18'45.37"E,39°59'35.38"N,西南半球 116°18'45.37"W,39°59'35.38"S,东南半球 116°18'45.37"E,39°59'35.38"S 为例,逐级网格实现。

3.1 第一级网格实现

3.1.1 规范解析

地球表面北斗二维网格的划分原点在赤道面与本初子午面的交点处,地球表面非两极区域(南纬 88° ~ 北纬 88°) 第一级网格根据 GB/T13989-2012 中 1:100 万图幅进行划分,单元大小是 6° × 4°。其对应 4 位码元:

  • 第一位码元,取值 N 或者 S,分别代表地球表面北半球、南半球。
  • 第二位~第四位码元,标识第一级网格。其中,第二位、第三位码元,标识经度方向网格,用01~60编码;第四位码元,标识纬度方向网格,纬分南北半球按照A~V编码,第一级网格码元编码方向如图所示。

因此, 第一位码元可以根据纬度的正负判断。第二、三位码元按经度(0° ~ 360°)按每6°为一个单元,全球划分为60个经度带(01~60),计算公式:经度带编号 = (经度° + 180°) / 6° + 1。第四位码元按纬度每4°为一个单元,南北半球各划分为22个纬度带(A~V),计算公式:纬度带索引 = (纬度绝对值°) / 4°。

3.1.2 代码实现

说明:

后续代码以秒为单位计算,为防止传入 Bigdecimal(double) 导致的精度损失问题。该示例中,直接使用 double 类型直接运算也可以算出同样的结果。

1. 创建网格信息实体类:

import com.zjp.exceptions.BeidouGridException;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.math.MathContext;

@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class GridInfo {
    // 常量定义(1度=3600秒)
    private static final BigDecimal SECONDS_PER_DEGREE = new BigDecimal("3600");
    private static final BigDecimal SECONDS_PER_MINUTE = new BigDecimal("60");
    // 纬度步长常量定义(单位:秒)
    private static final BigDecimal[] LON_STEPS = {
            // level 1: 6°=6*3600=21600秒
            new BigDecimal("6").multiply(SECONDS_PER_DEGREE),
            // level 2: 30'=30*60=1800秒
            new BigDecimal("30").multiply(SECONDS_PER_MINUTE),
            // level 3: 15'=15*60=900秒
            new BigDecimal("15").multiply(SECONDS_PER_MINUTE),
            // level 4: 1'=1*60=60秒
            SECONDS_PER_MINUTE,
            // level 5: 4秒
            new BigDecimal("4"),
            // level 6: 2秒
            new BigDecimal("2"),
            // level 7: 0.25秒
            new BigDecimal("0.25"),
            // level 8: 1/32秒=0.03125秒
            new BigDecimal("1").divide(new BigDecimal("32"), MathContext.DECIMAL128),
            // level 9: 1/256秒=0.00390625秒
            new BigDecimal("1").divide(new BigDecimal("256"), MathContext.DECIMAL128),
            // level 10: 1/2048秒=0.00048828125秒
            new BigDecimal("1").divide(new BigDecimal("2048"), MathContext.DECIMAL128)
    };
    // 经度步长常量定义(单位:秒)
    private static final BigDecimal[] LAT_STEPS = {
            // level 1: 4°=4*3600=14400秒
            new BigDecimal("4").multiply(SECONDS_PER_DEGREE),
            // level 2: 30'=30*60=1800秒
            new BigDecimal("30").multiply(SECONDS_PER_MINUTE),
            // level 3: 10'=10*60=600秒
            new BigDecimal("10").multiply(SECONDS_PER_MINUTE),
            // level 4: 1'=60秒
            SECONDS_PER_MINUTE,
            // level 5: 4秒
            new BigDecimal("4"),
            // level 6: 2秒
            new BigDecimal("2"),
            // level 7: 0.25秒
            new BigDecimal("0.25"),
            // level 8: 1/32秒=0.03125秒
            new BigDecimal("1").divide(new BigDecimal("32"), MathContext.DECIMAL128),
            // level 9: 1/256秒=0.00390625秒
            new BigDecimal("1").divide(new BigDecimal("256"), MathContext.DECIMAL128),
            // level 10: 1/2048秒=0.00048828125秒
            new BigDecimal("1").divide(new BigDecimal("2048"), MathContext.DECIMAL128)
    };

    /**
     * 初始经度
     */
    private BigDecimal baseLon;
    /**
     * 初始纬度
     */
    private BigDecimal baseLat;
    /**
     * 左下角经度
     */
    private BigDecimal currentLon;
    /**
     * 左下角纬度
     */
    private BigDecimal currentLat;
    /**
     * 当前列号
     */
    private int column;
    /**
     * 当前行号
     */
    private int row;
    /**
     * 网格粒度经度
     */
    private BigDecimal gridStepLon;
    /**
     * 网格粒度纬度
     */
    private BigDecimal gridStepLat;
    /**
     * 网格粒度
     */
    private int gridStep = 1;
    /**
     * 网格编码
     */
    private String gridCode;

    public BigDecimal getGridStepLon() {
        if (gridStep < 1 || gridStep > LON_STEPS.length) {
            throw new BeidouGridException("编码级别超出范围: " + gridStep);
        }
        return LON_STEPS[gridStep - 1];
    }

    public BigDecimal getGridStepLat() {
        if (gridStep < 1 || gridStep > LON_STEPS.length) {
            throw new BeidouGridException("编码级别超出范围: " + gridStep);
        }
        return LAT_STEPS[gridStep - 1];
    }
}

2. 计算第一级网格

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;

/**
 * 计算第一级北斗网格编码
 *
 * @param longitude 经度(单位:秒)
 * @param latitude  纬度(单位:秒)
 * @param level     编码级别(1-10)
 * @return 网格信息对象
 */
public static GridInfo calculateFirstLevelCode(BigDecimal longitude, BigDecimal latitude) {
    // 校验经纬度是否在有效范围内(经度:-180°~180°,纬度:-88°~88°),非两级区域
    if (longitude.doubleValue() < -180 * 3600 || longitude.doubleValue() > 180 * 3600 ||
            latitude.doubleValue() < -88 * 3600 || latitude.doubleValue() > 88 * 3600) {
        throw new BeidouGridException("经纬度超出范围");
    }


    // 创建初始网格信息对象
    GridInfo gridInfo = GridInfo.builder()
                      .baseLon(longitude)
                      .baseLat(latitude)
                      .currentLon(longitude)
                      .currentLat(latitude)
                      .gridStep(1)
                      .build();

    // 北斗网格编码使用的32进制字符集
    char[] rowChars = "ABCDEFGHIJKLMNOPQRSTUV".toCharArray();

    // 计算第一级码元
    char hemisphere = latitude.compareTo(BigDecimal.ZERO) >= 0 ? 'N' : 'S';

    // 计算经度方向编号(列号,第二位码元)
    int column = longitude
            .add(BigDecimal.valueOf(180 * 3600)) // 加上东经180°的偏移量
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .add(BigDecimal.ONE)
            .intValue();

    // 计算纬度方向编号(行号)
    int row = latitude.abs()
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .add(BigDecimal.ONE)
            .intValue();

    // 将纬度编号映射到对应的字母(第三位码元)
    char rowChar;
    rowChar = rowChars[row - 1];

    // 返回更新后的网格信息
    return gridInfo.setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%c%02d%c", hemisphere, column, rowChar));
}

3.2 第二级网格实现

3.2.1 规范解析

第二级网格是将第一级 6° x 4° 网格,按照经纬度等分,分成 12 x 8 个第二级网格,对应于 30' x 30' 网格,约等于地球赤道处 55.66 km x 55.66 km 网格。其对应第五、六位码元。其中,第五位码元标识经度方向网格,用0~B编码;第六位码元标识纬度方向网格,用0~7编码。

因此,先求出第一级网格码定位角的位置,即经度为第二、三位码元(东半球则减一) × 6°,纬度为(第四位码元(映射到数字)- 1)× 4°,由此得出一级网格码位置为:西北半球(-114°, 36°),东北半球(114°, 36°),西南半球(-114°, -36°),东南半球(114°, -36°)。再将一级网格码分成 12 x 8 个第二级网格,则他们所在二级网格码的位置如图所示:

最后再用经(纬)度减去第一级网格码的定位角经(纬)度的差,除以网格步长(30' x 30'),加一,则得出最终的行列号。

3.2.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第二级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateSecondLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789AB".toCharArray();
    char[] rowChars = "01234567".toCharArray();
    
    // 计算第一级网格码定位角的坐标
    BigDecimal baseLongitude = (longitude.compareTo(BigDecimal.ZERO) < 0 ?
            new BigDecimal(gridInfo.getColumn()) :
            new BigDecimal(gridInfo.getColumn() - 1))
            .multiply(gridInfo.getGridStepLon())
            .subtract(new BigDecimal(180 * 3600));

    BigDecimal baseLatitude = new BigDecimal(gridInfo.getRow())
            .subtract(BigDecimal.ONE)
            .multiply(gridInfo.getGridStepLat())
            .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO)));

    // 更新网格粒度级别
    gridInfo.setGridStep(2);

    // 计算第二级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE).intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    // 更新网格信息
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(),
                    columnChars[column - 1], rowChars[row - 1]));
}

3.3 第三级网格实现

3.3.1 规范解析

第三级网格是将第二级网格按照经纬度等分,分成2x3个第三级网格,对应于 1:5 万地图图幅 15'x10' 网格,约等于地球赤道处 27.83 km x 18.55 km 网格。其对应第七位码元,编码顺序按照Z序采用0~5编码,Z编码方向与第三级网格所在半球相关,详见下图:

 因此,先求出第二级网格码定位角的位置,即用二级网格列(行)号减一的差,乘二级网格粒度(根据所在的半球取正负),最后再加上第一级网格码定位角经(纬)度。下一步将经(纬)度与第二级网格码定位角的经(纬)度作差,再除以第三级网格粒度得出最终的行列号。

3.3.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第三级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateThirdLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 构建Z序编码矩阵(3列×2行),用于空间填充曲线编码
    int[][] zOrderMatrix = {
            {0, 2, 4},
            {1, 3, 5}
    };

    // 计算第二级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon())
                    .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat())
                    .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

    // 设置当前网格粒度级别为3
    gridInfo.setGridStep(3);

    // 计算第三级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%d", gridInfo.getGridCode(), zOrderMatrix[column][row]));
}

3.4 第四级网格实现

3.4.1 规范解析

第四级网格是将第三级网格按照经纬度等分,划分成 15 × 10 个第四级网格,约等于地球赤道处 1.85 km × 1.85 km 网格,其对应第八、九级码元。其中,第八位码元标识经度方向网格,用0~E编码;第九位码元标识纬度方向网格,用 0 ~ 9 编码。第四级网格码元编码方向与该网格所在半球相关,如图所示:

因此,先求出第三级网格码定位角的位置,思路同第三级网格。下一步将经(纬)度与第二级网格码定位角的经(纬)度作差,再除以第三级网格粒度得出最终的行列号。

3.4.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第四级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateForthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789".toCharArray();

    // 计算第三级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn())
                    .multiply(gridInfo.getGridStepLon()
                            .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO)))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow())
                    .multiply(gridInfo.getGridStepLat()
                            .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO)))));

    // 设置当前网格粒度级别为4
    gridInfo.setGridStep(4);

    // 计算第四级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE).intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE).intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

3.5 第五级网格实现

3.5.1 规范解析

第五级网格是将第四级网格按照经纬度等分,划分成 15 × 15 个第五级网格,约等于地球赤道处 123.69 m × 123.69 m 网格,其对应第十、十一位码元,均用 0 ~ E 编码,编码方向同第四级网格。

因此,代码实现思路同第四级网格。

3.5.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第五级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateFifthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789ABCDE".toCharArray();

    // 计算第四级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon()
                            .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO)))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat()
                            .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO)))));

    // 设置当前网格粒度级别为5
    gridInfo.setGridStep(5);

    // 计算第五级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE).intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE).intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

3.6 第六级网格实现

3.6.1 规范解析

第六级网格是将第五级网格按照经纬度等分,划分成 2 × 2 个第六级网格,约等于地球赤道处 61.84 m × 61.84 m 网格,其对应第十二位码元。编码顺序按照 Z 序采用 0 ~ 3 编码,Z 编码方向与第六级网格所在半球相关,如下图所示:

因此,代码实现思路同第三级网格。

 

 

3.6.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第六级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateSixthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 构建 2x2 Z序编码矩阵
    int[][] zOrderMatrix = {
            {0, 2},
            {1, 3}
    };

    // 计算第五级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon()
                            .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO)))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat()
                            .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO)))));

    // 更新网格粒度级别为6
    gridInfo.setGridStep(6);

    // 计算第六级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs().intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs().intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%d", gridInfo.getGridCode(), zOrderMatrix[column][row]));
}

3.7 第七级网格实现

3.7.1 规范解析

第六级网格是将第五级网格按照经纬度等分,划分成 8 × 8 个第七级网格,约等于地球赤道处 7.73 m × 7.73 m网格,其对应第十三、十四位码元,均用 0 ~ 7 编码,编码方向与该网格所在半球相关,如下图所示:

因此,代码实现思路同第四级网格。

 

3.7.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第七级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
public static GridInfo calculateSeventhLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789ABCDE".toCharArray();

    // 计算第六级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn())
                    .multiply(gridInfo.getGridStepLon())
                    .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow())
                    .multiply(gridInfo.getGridStepLat())
                    .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

    // 设置当前网格粒度级别为7
    gridInfo.setGridStep(7);

    // 计算第七级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

3.8 第八 ~ 十级网格实现

3.8.1 规范解析

均基于上一级网格,按照经纬度等分,划分成 8 × 8 个网格,各级各占两个码元。

因此,代码实现思路同第五级网格。

3.8.2 代码实现

import com.zjp.entity.GridInfo;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 计算第八级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
private static GridInfo calculateEighthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789ABCDE".toCharArray();

    // 计算第七级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon())
                    .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat())
                    .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

    // 更新网格粒度级别为8
    gridInfo.setGridStep(8);

    // 计算第八级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

/**
 * 计算第九级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
private static GridInfo calculateNinthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789ABCDE".toCharArray();

    // 计算第八级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon())
                    .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat())
                    .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

    // 更新网格粒度级别为9
    gridInfo.setGridStep(9);

    // 计算第九级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

/**
 * 计算第十级北斗网格编码
 *
 * @param gridInfo 网格信息对象
 * @return 网格信息对象
 */
private static GridInfo calculateTenthLevelCode(GridInfo gridInfo) {
    BigDecimal longitude = gridInfo.getBaseLon();
    BigDecimal latitude = gridInfo.getBaseLat();

    // 定义编码字符集
    char[] columnChars = "0123456789ABCDE".toCharArray();
    char[] rowChars = "0123456789ABCDE".toCharArray();

    // 计算第九级网格码定位角的坐标
    BigDecimal baseLongitude = gridInfo.getCurrentLon()
            .add(new BigDecimal(gridInfo.getColumn() - 1)
                    .multiply(gridInfo.getGridStepLon())
                    .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

    BigDecimal baseLatitude = gridInfo.getCurrentLat()
            .add(new BigDecimal(gridInfo.getRow() - 1)
                    .multiply(gridInfo.getGridStepLat())
                    .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

    // 设置当前网格粒度级别为10
    gridInfo.setGridStep(10);

    // 计算第十级网格列号和行号
    int column = longitude
            .subtract(baseLongitude)
            .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    int row = latitude
            .subtract(baseLatitude)
            .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
            .abs()
            .add(BigDecimal.ONE)
            .intValue();

    // 更新网格信息并返回
    return gridInfo.setCurrentLon(baseLongitude)
            .setCurrentLat(baseLatitude)
            .setColumn(column)
            .setRow(row)
            .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(), columnChars[column - 1], rowChars[row - 1]));
}

3.9 代码汇总

1. 创建异常类:

public class BeidouGridException extends RuntimeException{
    private static final long serialVersionUID = 1L;

    public BeidouGridException() {
    }

    public BeidouGridException(String message) {
        super(message);
    }

    public BeidouGridException(String message, Throwable cause) {
        super(message, cause);
    }

    public BeidouGridException(Throwable cause) {
        super(cause);
    }

    public BeidouGridException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

2. 创建网格信息实体类:

import com.zjp.exceptions.BeidouGridException;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.math.BigDecimal;
import java.math.MathContext;

@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class GridInfo {
    // 常量定义(1度=3600秒)
    private static final BigDecimal SECONDS_PER_DEGREE = new BigDecimal("3600");
    private static final BigDecimal SECONDS_PER_MINUTE = new BigDecimal("60");
    // 纬度步长常量定义(单位:秒)
    private static final BigDecimal[] LON_STEPS = {
            // level 1: 6°=6*3600=21600秒
            new BigDecimal("6").multiply(SECONDS_PER_DEGREE),
            // level 2: 30'=30*60=1800秒
            new BigDecimal("30").multiply(SECONDS_PER_MINUTE),
            // level 3: 15'=15*60=900秒
            new BigDecimal("15").multiply(SECONDS_PER_MINUTE),
            // level 4: 1'=1*60=60秒
            SECONDS_PER_MINUTE,
            // level 5: 4秒
            new BigDecimal("4"),
            // level 6: 2秒
            new BigDecimal("2"),
            // level 7: 0.25秒
            new BigDecimal("0.25"),
            // level 8: 1/32秒=0.03125秒
            new BigDecimal("1").divide(new BigDecimal("32"), MathContext.DECIMAL128),
            // level 9: 1/256秒=0.00390625秒
            new BigDecimal("1").divide(new BigDecimal("256"), MathContext.DECIMAL128),
            // level 10: 1/2048秒=0.00048828125秒
            new BigDecimal("1").divide(new BigDecimal("2048"), MathContext.DECIMAL128)
    };
    // 经度步长常量定义(单位:秒)
    private static final BigDecimal[] LAT_STEPS = {
            // level 1: 4°=4*3600=14400秒
            new BigDecimal("4").multiply(SECONDS_PER_DEGREE),
            // level 2: 30'=30*60=1800秒
            new BigDecimal("30").multiply(SECONDS_PER_MINUTE),
            // level 3: 10'=10*60=600秒
            new BigDecimal("10").multiply(SECONDS_PER_MINUTE),
            // level 4: 1'=60秒
            SECONDS_PER_MINUTE,
            // level 5: 4秒
            new BigDecimal("4"),
            // level 6: 2秒
            new BigDecimal("2"),
            // level 7: 0.25秒
            new BigDecimal("0.25"),
            // level 8: 1/32秒=0.03125秒
            new BigDecimal("1").divide(new BigDecimal("32"), MathContext.DECIMAL128),
            // level 9: 1/256秒=0.00390625秒
            new BigDecimal("1").divide(new BigDecimal("256"), MathContext.DECIMAL128),
            // level 10: 1/2048秒=0.00048828125秒
            new BigDecimal("1").divide(new BigDecimal("2048"), MathContext.DECIMAL128)
    };

    /**
     * 初始经度
     */
    private BigDecimal baseLon;
    /**
     * 初始纬度
     */
    private BigDecimal baseLat;
    /**
     * 左下角经度
     */
    private BigDecimal currentLon;
    /**
     * 左下角纬度
     */
    private BigDecimal currentLat;
    /**
     * 当前列号
     */
    private int column;
    /**
     * 当前行号
     */
    private int row;
    /**
     * 网格粒度经度
     */
    private BigDecimal gridStepLon;
    /**
     * 网格粒度纬度
     */
    private BigDecimal gridStepLat;
    /**
     * 网格粒度
     */
    private int gridStep = 1;
    /**
     * 网格编码
     */
    private String gridCode;

    public BigDecimal getGridStepLon() {
        if (gridStep < 1 || gridStep > LON_STEPS.length) {
            throw new BeidouGridException("编码级别超出范围: " + gridStep);
        }
        return LON_STEPS[gridStep - 1];
    }

    public BigDecimal getGridStepLat() {
        if (gridStep < 1 || gridStep > LON_STEPS.length) {
            throw new BeidouGridException("编码级别超出范围: " + gridStep);
        }
        return LAT_STEPS[gridStep - 1];
    }
}

 3. 创建工具类:

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class GridEncoder<T> {
    private final Map<Integer, Function<T, T>> encoders = new HashMap<>();

    public void register(int level, Function<T, T> encoder) {
        encoders.put(level, encoder);
    }

    public Function<T, T> get(int level) {
        return encoders.get(level);
    }
}

4. 北斗网格位置码工具类:

import cn.hutool.core.lang.Assert;
import com.zjp.exceptions.BeidouGridException;
import com.zjp.pojo.GridInfo;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.function.Function;

@Slf4j
@UtilityClass
public class BeidouGridUtil {
    // 2D编码策略
    private static final GridEncoder<GridInfo> GRID_ENCODER_2D = new GridEncoder<>();

    static {
        // 注册2D编码器
        register2DEncoders();
    }

    /**
     * 注册所有2D编码器
     */
    private void register2DEncoders() {
        GRID_ENCODER_2D.register(1, BeidouGridUtil::calculate2DFirstLevelCode);
        GRID_ENCODER_2D.register(2, BeidouGridUtil::calculate2DSecondLevelCode);
        GRID_ENCODER_2D.register(3, BeidouGridUtil::calculate2DThirdLevelCode);
        GRID_ENCODER_2D.register(4, BeidouGridUtil::calculate2DForthLevelCode);
        GRID_ENCODER_2D.register(5, BeidouGridUtil::calculate2DFifthLevelCode);
        GRID_ENCODER_2D.register(6, BeidouGridUtil::calculate2DSixthLevelCode);
        GRID_ENCODER_2D.register(7, BeidouGridUtil::calculate2DSeventhLevelCode);
        GRID_ENCODER_2D.register(8, BeidouGridUtil::calculate2DEighthLevelCode);
        GRID_ENCODER_2D.register(9, BeidouGridUtil::calculate2DNinthLevelCode);
        GRID_ENCODER_2D.register(10, BeidouGridUtil::calculate2DTenthLevelCode);
    }

    /**
     * 生成二维北斗网格码
     *
     * @param longitude 经度(单位:秒)
     * @param latitude  纬度(单位:秒)
     * @param level     编码级别(1-10)
     * @return 北斗网格编码
     */
    public String generate2DGridCode(BigDecimal longitude, BigDecimal latitude, int level) {
        validateCoordinates(longitude.doubleValue(), latitude.doubleValue());
        validateLevel(level);

        GridInfo gridInfo = createInitialGridInfo(longitude, latitude);
        for (int i = 1; i <= level; i++) {
            Function<GridInfo, GridInfo> encoder = GRID_ENCODER_2D.get(i);
            if (encoder == null) {
                throw new BeidouGridException("不支持的编码级别: " + i);
            }
            gridInfo = encoder.apply(gridInfo);
        }
        return gridInfo.getGridCode();
    }


    /**
     * 校验经纬度是否在有效范围内(经度:-180°~180°,纬度:-88°~88°),不包含极地
     *
     * @param longitude 经度(单位:秒)
     * @param latitude  纬度(单位:秒)
     */
    private void validateCoordinates(double longitude, double latitude) {
        if (longitude < -180 * 3600 || longitude > 180 * 3600 ||
                latitude < -88 * 3600 || latitude > 88 * 3600) {
            throw new BeidouGridException("经纬度超出范围");
        }
    }

    /**
     * 校验编码级别是否在有效范围内(1-10)
     *
     * @param level 编码级别
     */
    private void validateLevel(int level) {
        if (level < 1 || level > 10) {
            throw new BeidouGridException("编码级别超出范围: " + level);
        }
    }

    /**
     * 创建初始网格信息对象(用于二维编码)
     *
     * @param longitude 初始经度(单位:秒)
     * @param latitude  初始纬度(单位:秒)
     * @return 初始化后的 GridInfo 对象
     */
    private GridInfo createInitialGridInfo(BigDecimal longitude, BigDecimal latitude) {
        return GridInfo.builder()
                .baseLon(longitude)
                .baseLat(latitude)
                .currentLon(longitude)
                .currentLat(latitude)
                .gridStep(1)
                .build();
    }


    /**
     * 计算第一级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DFirstLevelCode(GridInfo gridInfo) {
        // 获取基础经纬度(单位:秒)
        BigDecimal longitude = gridInfo.getBaseLon();
        BigDecimal latitude = gridInfo.getBaseLat();

        // 北斗网格编码使用的32进制字符集
        char[] rowChars = "ABCDEFGHIJKLMNOPQRSTUV".toCharArray();

        // 判断半球并计算绝对纬度值
        char hemisphere = latitude.compareTo(BigDecimal.ZERO) >= 0 ? 'N' : 'S';

        // 计算经度方向编号(列号)
        int column = longitude
                .add(BigDecimal.valueOf(180 * 3600)) // 加上东经180°的偏移量
                .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
                .add(BigDecimal.ONE)
                .intValue();

        // 计算纬度方向编号(行号)
        int row = latitude.abs()
                .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
                .add(BigDecimal.ONE)
                .intValue();

        // 将纬度编号映射到对应的字母(1->A, 2->B,...)
        char rowChar;
        rowChar = rowChars[row - 1];

        // 返回更新后的网格信息
        return gridInfo.setColumn(column)
                .setRow(row)
                .setGridCode(String.format("%c%02d%c", hemisphere, column, rowChar));
    }

    /**
     * 计算第二级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DSecondLevelCode(GridInfo gridInfo) {
        BigDecimal longitude = gridInfo.getBaseLon();
        BigDecimal latitude = gridInfo.getBaseLat();

        // 定义编码字符集
        char[] columnChars = "0123456789AB".toCharArray();
        char[] rowChars = "01234567".toCharArray();

        // 计算基准经度λ₁和纬度φ₁
        BigDecimal baseLongitude = (longitude.compareTo(BigDecimal.ZERO) < 0 ?
                new BigDecimal(gridInfo.getColumn()) :
                new BigDecimal(gridInfo.getColumn() - 1))
                .multiply(gridInfo.getGridStepLon())
                .subtract(new BigDecimal(180 * 3600));

        BigDecimal baseLatitude = new BigDecimal(gridInfo.getRow())
                .subtract(BigDecimal.ONE)
                .multiply(gridInfo.getGridStepLat())
                .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO)));

        // 更新网格粒度级别
        gridInfo.setGridStep(2);

        // 计算子网格列号和行号
        int column = longitude
                .subtract(baseLongitude)
                .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
                .abs()
                .add(BigDecimal.ONE).intValue();

        int row = latitude
                .subtract(baseLatitude)
                .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
                .abs()
                .add(BigDecimal.ONE)
                .intValue();

        // 更新网格信息
        return gridInfo.setCurrentLon(baseLongitude)
                .setCurrentLat(baseLatitude)
                .setColumn(column)
                .setRow(row)
                .setGridCode(String.format("%s%c%c", gridInfo.getGridCode(),
                        columnChars[column - 1], rowChars[row - 1]));
    }

    /**
     * 计算第三级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DThirdLevelCode(GridInfo gridInfo) {
        int[][] zOrderMatrix = {
                {0, 2, 4},
                {1, 3, 5}
        };
        return calculate2DLevelCode(gridInfo, 3, zOrderMatrix);
    }

    /**
     * 计算第四级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DForthLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 4, false, false, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789".toCharArray());
    }

    /**
     * 计算第五级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DFifthLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 5, true, true, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789ABCDE".toCharArray());
    }

    /**
     * 计算第六级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DSixthLevelCode(GridInfo gridInfo) {
        int[][] zOrderMatrix = {
                {0, 2},
                {1, 3}
        };
        return calculate2DLevelCode(gridInfo, 6, zOrderMatrix);
    }

    /**
     * 计算第七级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DSeventhLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 7, false, false, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789ABCDE".toCharArray());
    }

    /**
     * 计算第八级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DEighthLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 8, true, true, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789ABCDE".toCharArray());
    }

    /**
     * 计算第九级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DNinthLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 9, true, true, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789ABCDE".toCharArray());
    }

    /**
     * 计算第十级北斗网格编码(二维)
     *
     * @param gridInfo 网格信息对象
     * @return 包含计算结果的网格信息对象
     */
    private GridInfo calculate2DTenthLevelCode(GridInfo gridInfo) {
        return calculate2DLevelCode(gridInfo, 10, true, true, true,
                "0123456789ABCDE".toCharArray(),
                "0123456789ABCDE".toCharArray());
    }

    /**
     * 通用的二维北斗网格编码计算方法
     *
     * @param gridInfo     网格信息对象,包含网格的基本信息和当前状态
     * @param level        粒度级别,用于确定网格的细分程度
     * @param zOrderMatrix Z字形矩阵,用于确定网格在二维平面上的编码
     * @return 返回更新后的网格信息对象
     */
    private GridInfo calculate2DLevelCode(GridInfo gridInfo, int level, int[][] zOrderMatrix) {
        // 获取网格基本信息
        BigDecimal longitude = gridInfo.getBaseLon();
        BigDecimal latitude = gridInfo.getBaseLat();

        // 计算当前粒度级别的基准经纬度 λ₁, φ₁
        BigDecimal baseLongitude = gridInfo.getCurrentLon()
                .add(new BigDecimal(gridInfo.getColumn() - 1)
                        .multiply(gridInfo.getGridStepLon())
                        .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

        BigDecimal baseLatitude = gridInfo.getCurrentLat()
                .add(new BigDecimal(gridInfo.getRow() - 1)
                        .multiply(gridInfo.getGridStepLat())
                        .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));

        // 设置当前网格粒度级别
        gridInfo.setGridStep(level);

        // 计算子网格列号和行号
        int column = longitude
                .subtract(baseLongitude)
                .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
                .abs()
                .intValue();

        int row = latitude
                .subtract(baseLatitude)
                .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
                .abs()
                .intValue();

        // 更新网格信息并返回
        return gridInfo.setCurrentLon(baseLongitude)
                .setCurrentLat(baseLatitude)
                .setColumn(column)
                .setRow(row)
                .setGridCode(String.format("%s%d",
                        gridInfo.getGridCode(),
                        zOrderMatrix[column][row]));
    }


    /**
     * 通用的二维北斗网格编码计算方法
     *
     * @param gridInfo             网格信息对象
     * @param level                当前网格粒度级别
     * @param useMinusOneForColumn 是否使用 column - 1 计算基准经度
     * @param useMinusOneForRow    是否使用 row - 1 计算基准纬度
     * @param addOneForSubGridCalc 是否在子网格列号和行号计算时加 1
     * @param columnChars          列号字符集
     * @param rowChars             行号字符集
     * @return 更新后的 GridInfo 对象
     */
    private GridInfo calculate2DLevelCode(GridInfo gridInfo,
                                          int level,
                                          boolean useMinusOneForColumn,
                                          boolean useMinusOneForRow,
                                          boolean addOneForSubGridCalc,
                                          char[] columnChars,
                                          char[] rowChars) {
        BigDecimal longitude = gridInfo.getBaseLon();
        BigDecimal latitude = gridInfo.getBaseLat();

        // 计算当前粒度级别的基准经纬度 λ₁, φ₁
        BigDecimal baseLongitude = gridInfo.getCurrentLon()
                .add(new BigDecimal(useMinusOneForColumn ? gridInfo.getColumn() - 1 : gridInfo.getColumn())
                        .multiply(gridInfo.getGridStepLon())
                        .multiply(BigDecimal.valueOf(longitude.compareTo(BigDecimal.ZERO))));

        BigDecimal baseLatitude = gridInfo.getCurrentLat()
                .add(new BigDecimal(useMinusOneForRow ? gridInfo.getRow() - 1 : gridInfo.getRow())
                        .multiply(gridInfo.getGridStepLat())
                        .multiply(BigDecimal.valueOf(latitude.compareTo(BigDecimal.ZERO))));



        // 设置当前网格粒度级别
        gridInfo.setGridStep(level);

        // 计算子网格列号和行号
        int column = longitude
                .subtract(baseLongitude)
                .divide(gridInfo.getGridStepLon(), 0, RoundingMode.DOWN)
                .abs()
                .intValue();

        int row = latitude
                .subtract(baseLatitude)
                .divide(gridInfo.getGridStepLat(), 0, RoundingMode.DOWN)
                .abs()
                .intValue();

        // 是否加 1
        if (addOneForSubGridCalc) {
            column = column + 1;
            row = row + 1;
        }

        // 更新网格信息并返回
        return gridInfo.setCurrentLon(baseLongitude)
                .setCurrentLat(baseLatitude)
                .setColumn(column)
                .setRow(row)
                .setGridCode(String.format("%s%c%c",
                        gridInfo.getGridCode(),
                        columnChars[column - 1],
                        rowChars[row - 1]));
    }
本文章已经生成可运行项目
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

可儿·四系桜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值