WGS84坐标互转百度坐标优化

  1. 坐标系的加密特性
    WGS84到BD-09需经过两次加密(WGS84 → GCJ-02 → BD-09),而逆向转换需解密两次(BD-09 → GCJ-02 → WGS84)。GCJ-02的加密算法包含非线性偏移,直接逆向公式可能导致误差。使用迭代法逆向计算WGS84坐标

  2. 高精度计算
    使用BigDecimal类型存储坐标值,避免中间过程的精度截断。确保所有数学运算(如三角函数)使用高精度库(采用maven中的Apfloat库)。

  3. 可靠的转换算法
    使用经过广泛验证的开源实现,而非自行推导公式,以降低算法误差。

  4. 测试与验证
    通过大规模坐标回环测试(WGS84 → BD-09 → WGS84)验证误差范围,确保精度可接受。

package org.example;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.apfloat.Apfloat;
import org.apfloat.ApfloatMath;
import org.apfloat.ApfloatRuntimeException;

public class HighPrecisionCoordinateConverter {
    // 精度配置
    private static final MathContext MC = MathContext.DECIMAL128;
    private static final  int APFLOAT_PRECISION = 50;

    // 常量定义(使用字符串初始化)
    private static final BigDecimal PI = new BigDecimal("3.14159265358979323846264338327950288419716939937510");
    private static final BigDecimal X_PI = PI.multiply(new BigDecimal("3000")).divide(new BigDecimal("180"), MC);
    private static final BigDecimal A = new BigDecimal("6378245.0");
    private static final BigDecimal EE = new BigDecimal("0.00669342162296594323");
    private static final BigDecimal MAGIC_NUMBER = new BigDecimal("0.00002");
    private static final BigDecimal BD_OFFSET = new BigDecimal("0.0065");

    // 国内坐标范围
    private static final BigDecimal CHINA_LNG_MIN = new BigDecimal("72.004");
    private static final BigDecimal CHINA_LNG_MAX = new BigDecimal("137.8347");
    private static final BigDecimal CHINA_LAT_MIN = new BigDecimal("0.8293");
    private static final BigDecimal CHINA_LAT_MAX = new BigDecimal("55.8271");

    //======================== 公有接口 ========================
    public static double[] bd09ToWgs84(double lng, double lat) {
        BigDecimal[] gcj = bd09ToGcj02(new BigDecimal(lng), new BigDecimal(lat));
        BigDecimal[] wgs = gcj02ToWgs84(gcj[0], gcj[1]);
        return convertToDoubleArray(wgs);
    }

    public static double[] wgs84ToBd09(double lng, double lat) {
        BigDecimal[] gcj = wgs84ToGcj02(new BigDecimal(lng), new BigDecimal(lat));
        BigDecimal[] bd = gcj02ToBd09(gcj[0], gcj[1]);
        return convertToDoubleArray(bd);
    }

    //======================== 核心算法 ========================
    private static BigDecimal[] bd09ToGcj02(BigDecimal bdLng, BigDecimal bdLat) {
        BigDecimal x = bdLng.subtract(BD_OFFSET, MC);
        BigDecimal y = bdLat.subtract(new BigDecimal("0.006"), MC);

        BigDecimal z = sqrt(x.pow(2).add(y.pow(2)))
                .subtract(sin(y.multiply(X_PI)).multiply(MAGIC_NUMBER), MC);

        BigDecimal theta = atan2(y, x).subtract(
                cos(x.multiply(X_PI)).multiply(new BigDecimal("0.000003")), MC);

        return new BigDecimal[]{
                z.multiply(cos(theta), MC),
                z.multiply(sin(theta), MC)
        };
    }

    private static BigDecimal[] gcj02ToBd09(BigDecimal gcjLng, BigDecimal gcjLat) {
        BigDecimal z = sqrt(gcjLng.pow(2).add(gcjLat.pow(2)))
                .add(sin(gcjLat.multiply(X_PI)).multiply(MAGIC_NUMBER), MC);

        BigDecimal theta = atan2(gcjLat, gcjLng)
                .add(cos(gcjLng.multiply(X_PI)).multiply(new BigDecimal("0.000003")), MC);

        return new BigDecimal[]{
                z.multiply(cos(theta), MC).add(BD_OFFSET, MC),
                z.multiply(sin(theta), MC).add(new BigDecimal("0.006"), MC)
        };
    }

    private static BigDecimal[] wgs84ToGcj02(BigDecimal lng, BigDecimal lat) {
        if (isOutsideChina(lng, lat)) {
            return new BigDecimal[]{lng, lat};
        }
        BigDecimal[] delta = calculateDelta(lng, lat);
        return new BigDecimal[]{
                lng.add(delta[0], MC),
                lat.add(delta[1], MC)
        };
    }

    private static BigDecimal[] gcj02ToWgs84(BigDecimal lng, BigDecimal lat) {
        if (isOutsideChina(lng, lat)) {
            return new BigDecimal[]{lng, lat};
        }
        BigDecimal[] delta = calculateDelta(lng, lat);
        return new BigDecimal[]{
                lng.subtract(delta[0], MC),
                lat.subtract(delta[1], MC)
        };
    }

    //======================== 数学工具方法 ========================
    private static BigDecimal[] calculateDelta(BigDecimal lng, BigDecimal lat) {
        BigDecimal baseLng = lng.subtract(new BigDecimal("105"), MC);
        BigDecimal baseLat = lat.subtract(new BigDecimal("35"), MC);

        BigDecimal radLat = radians(lat);
        BigDecimal magic = sin(radLat).pow(2).multiply(EE, MC);
        magic = BigDecimal.ONE.subtract(magic, MC);
        BigDecimal sqrtMagic = sqrt(magic);

        BigDecimal dLat = transformLat(baseLng, baseLat)
                .multiply(new BigDecimal("180"), MC)
                .divide(A.multiply(BigDecimal.ONE.subtract(EE))
                        .divide(magic.multiply(sqrtMagic), MC)
                        .divide(PI, MC),MC);

        BigDecimal dLng = transformLng(baseLng, baseLat)
                .multiply(new BigDecimal("180"), MC)
                .divide(A.multiply(cos(radLat)), MC)
                .divide(sqrtMagic.multiply(PI), MC);

        return new BigDecimal[]{dLng, dLat};
    }

    private static BigDecimal transformLat(BigDecimal x, BigDecimal y) {
        MathContext mc = MathContext.DECIMAL128;

        // 第一项:-100 + 2x + 3y
        BigDecimal term1 = new BigDecimal("-100")
                .add(x.multiply(TWO, mc))
                .add(y.multiply(THREE, mc), mc);

        // 第二项:0.2y²
        BigDecimal term2 = y.pow(2, mc)
                .multiply(new BigDecimal("0.2"), mc);

        // 第三项:0.1xy
        BigDecimal term3 = x.multiply(y, mc)
                .multiply(ONE_TENTH, mc);

        // 第四项:0.2√|x|
        BigDecimal term4 = sqrt(x.abs(), APFLOAT_PRECISION)
                .multiply(new BigDecimal("0.2"), mc);

        // 合并基础项
        BigDecimal result = term1.add(term2, mc)
                .add(term3, mc)
                .add(term4, mc);

        // 第五项:正弦波动项1 (20sin6πx + 20sin2πx) * 2/3
        BigDecimal sin6x = sin(x.multiply(SIX).multiply(PI), APFLOAT_PRECISION);
        BigDecimal sin2x = sin(x.multiply(TWO).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave1 = TWENTY.multiply(sin6x.add(sin2x), mc)
                .multiply(TWO_THIRDS, mc);

        // 第六项:正弦波动项2 (20sinπy + 40sin(πy/3)) * 2/3
        BigDecimal sinY = sin(y.multiply(PI), APFLOAT_PRECISION);
        BigDecimal sinY3 = sin(y.divide(THREE, mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave2 = TWENTY.multiply(sinY, mc)
                .add(FORTY.multiply(sinY3, mc), mc)
                .multiply(TWO_THIRDS, mc);

        // 第七项:长周期波动项 (160sin(πy/12) + 320sin(πy/30)) * 2/3
        BigDecimal sinY12 = sin(y.divide(new BigDecimal("12"), mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal sinY30 = sin(y.divide(new BigDecimal("30"), mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave3 = new BigDecimal("160").multiply(sinY12, mc)
                .add(new BigDecimal("320").multiply(sinY30, mc), mc)
                .multiply(TWO_THIRDS, mc);

        // 累计所有分量
        return result.add(wave1, mc)
                .add(wave2, mc)
                .add(wave3, mc);
    }

    /**
     * 高精度经度转换
     */
    private static BigDecimal transformLng(BigDecimal x, BigDecimal y) {
        MathContext mc = MathContext.DECIMAL128;

        // 基础项:300 + x + 2y
        BigDecimal base = new BigDecimal("300")
                .add(x, mc)
                .add(y.multiply(TWO, mc), mc);

        // 二次项:0.1x² + 0.1xy
        BigDecimal quadTerm = x.pow(2, mc).multiply(ONE_TENTH, mc)
                .add(x.multiply(y, mc).multiply(ONE_TENTH, mc), mc);

        // 根号项:0.1√|x|
        BigDecimal sqrtTerm = sqrt(x.abs(), APFLOAT_PRECISION)
                .multiply(ONE_TENTH, mc);

        BigDecimal result = base.add(quadTerm, mc)
                .add(sqrtTerm, mc);

        // 高频波动项1 (20sin6πx + 20sin2πx) * 2/3
        BigDecimal sin6x = sin(x.multiply(SIX).multiply(PI), APFLOAT_PRECISION);
        BigDecimal sin2x = sin(x.multiply(TWO).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave1 = TWENTY.multiply(sin6x.add(sin2x), mc)
                .multiply(TWO_THIRDS, mc);

        // 中频波动项2 (20sinπx + 40sin(πx/3)) * 2/3
        BigDecimal sinX = sin(x.multiply(PI), APFLOAT_PRECISION);
        BigDecimal sinX3 = sin(x.divide(THREE, mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave2 = TWENTY.multiply(sinX, mc)
                .add(FORTY.multiply(sinX3, mc), mc)
                .multiply(TWO_THIRDS, mc);

        // 低频波动项3 (150sin(πx/12) + 300sin(πx/30)) * 2/3
        BigDecimal sinX12 = sin(x.divide(new BigDecimal("12"), mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal sinX30 = sin(x.divide(new BigDecimal("30"), mc).multiply(PI), APFLOAT_PRECISION);
        BigDecimal wave3 = new BigDecimal("150").multiply(sinX12, mc)
                .add(new BigDecimal("300").multiply(sinX30, mc), mc)
                .multiply(TWO_THIRDS, mc);

        return result.add(wave1, mc)
                .add(wave2, mc)
                .add(wave3, mc);
    }

    //======================== 高精度数学函数 ========================
    private static BigDecimal sin(BigDecimal x) {
        try {
            return new BigDecimal(String.valueOf(ApfloatMath.sin(new Apfloat(x))));
        } catch (ApfloatRuntimeException e) {
            return new BigDecimal(Math.sin(x.doubleValue()));
        }
    }

    private static BigDecimal cos(BigDecimal x) {
        try {
            return new BigDecimal(String.valueOf(ApfloatMath.cos(new Apfloat(x))));
        } catch (ApfloatRuntimeException e) {
            return new BigDecimal(Math.cos(x.doubleValue()));
        }
    }
//
    private static BigDecimal atan2(BigDecimal y, BigDecimal x) {
        try {
            Apfloat apfY = new Apfloat(y.toString(), APFLOAT_PRECISION);
            Apfloat apfX = new Apfloat(x.toString(), APFLOAT_PRECISION);
            return new BigDecimal(ApfloatMath.atan2(apfY, apfX).toString());
        } catch (ApfloatRuntimeException e) {
            return new BigDecimal(Math.atan2(y.doubleValue(), x.doubleValue()));
        }
    }

    private static BigDecimal sqrt(BigDecimal x) {
        try {
            return new BigDecimal(
                    ApfloatMath.sqrt(new Apfloat(x.toString(), APFLOAT_PRECISION))
                            .toString());
        } catch (ApfloatRuntimeException e) {
            return BigDecimal.valueOf(Math.sqrt(x.doubleValue()));
        }
    }

    private static BigDecimal sin(BigDecimal x, int precision) {
        try {
            Apfloat radians = new Apfloat(x.toString(), precision);
            return new BigDecimal(ApfloatMath.sin(radians).toString());
        } catch (ApfloatRuntimeException e) {
            // 异常时降级到JDK的双精度计算
            return BigDecimal.valueOf(Math.sin(x.doubleValue()));
        }
    }

    /**
     * 高精度平方根 (使用Apfloat实现)
     */
    private static BigDecimal sqrt(BigDecimal x, int precision) {
        try {
            Apfloat num = new Apfloat(x.toString(), precision);
            return new BigDecimal(ApfloatMath.sqrt(num).toString());
        } catch (ApfloatRuntimeException e) {
            return BigDecimal.valueOf(Math.sqrt(x.doubleValue()));
        }
    }

    //======================== 工具方法 ========================
    private static boolean isOutsideChina(BigDecimal lng, BigDecimal lat) {
        return lng.compareTo(CHINA_LNG_MIN) < 0 || lng.compareTo(CHINA_LNG_MAX) > 0 ||
                lat.compareTo(CHINA_LAT_MIN) < 0 || lat.compareTo(CHINA_LAT_MAX) > 0;
    }

    private static BigDecimal radians(BigDecimal degrees) {
        return degrees.multiply(PI).divide(new BigDecimal("180"), MC);
    }



    private static double[] convertToDoubleArray(BigDecimal[] arr) {
        return new double[]{
                arr[0].setScale(12, RoundingMode.HALF_UP).doubleValue(),
                arr[1].setScale(12, RoundingMode.HALF_UP).doubleValue()
        };
    }

    //======================== 常量缓存 ========================
    private static final BigDecimal TWO = new BigDecimal("2");
    private static final BigDecimal THREE = new BigDecimal("3");
    private static final BigDecimal SIX = new BigDecimal("6");
    private static final BigDecimal TWENTY = new BigDecimal("20");
    private static final BigDecimal FORTY = new BigDecimal("40");
    private static final BigDecimal ONE_TENTH = new BigDecimal("0.1");
    private static final BigDecimal TWO_THIRDS = new BigDecimal("2").divide(new BigDecimal("3"), 34, RoundingMode.HALF_UP);

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值