package com.qingjian.common.utils; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import com.qingjian.common.constants.PathConst; import com.sun.jna.ptr.DoubleByReference; import lombok.extern.slf4j.Slf4j; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicBoolean; /** * 基站定位工具类 *

* 通过调用动态链接库实现基站信息查询和逆地理编码功能 * 支持将WGS84坐标转换为百度BD09坐标 *

* * @author cc */ @Slf4j public class TowerUtils { private static final int ADDRESS_BUFFER_SIZE = 2048; private static final AtomicBoolean FLAG = new AtomicBoolean(false); /** * 根据操作系统类型返回对应的动态链接库路径 * Windows系统使用DLL,Linux系统使用SO * * @return 动态链接库路径字符串 */ public static String dll() { return LicenseUtil.isWin() ? PathConst.CELL_TOWER_PATH.toString() : PathConst.CELL_TOWER_PATH_SO.toString(); } /** * 初始化基站数据库 *

* 在首次使用前必须调用此方法进行初始化 * 加载基站数据文件到内存中 *

*/ public static void init() { if (FLAG.get()) { log.info("基站数据库路径已经加载,无需再次初始化!"); return; } String cellLocal = SystemConfManager.INSTANCE.getCellLocal(); if (StrUtil.isEmpty(cellLocal)) { log.info("请先设置基站数据库路径!"); return; } Path path = Paths.get(cellLocal); if (Files.exists(path)) { String name = FileUtil.getName(path); if (StrUtil.endWith(name, ".bin")) { log.info("基站数据库文件名不正确!"); return; } CellDll.INSTANCE_DLL.cell_init(cellLocal); FLAG.set(true); log.info("基站数据库路径已经加载!"); return; } log.info("基站数据库路径不存在!"); } /** * 根据基站信息查询位置和地址 *

* 查询流程: * 1. 调用cell_query获取基站的经纬度信息(WGS84坐标系) * 2. 将WGS84坐标转换为百度BD09坐标 * 3. 调用regeo_simple进行逆地理编码获取详细地址 *

* * @param mcc 移动国家代码(Mobile Country Code),中国代码为460 * @param mnc 移动网络代码(Mobile Network Code),0-移动,1-联通,电信对应sid(十进制) * @param lac 位置区码(Location Area Code),电信对应nid(十进制) * @param ci 小区标识(Cell Identity),电信对应bid(十进制) * @return 包含位置信息的数组,格式为:[纬度, 经度, 覆盖半径, 详细地址] * - obj[0]: 百度纬度(BD09) * - obj[1]: 百度经度(BD09) * - obj[2]: 覆盖半径(米) * - obj[3]: 详细地址描述 * 示例调用:CellUtils.getCells(460, 0, 1, 2) * getCells(460, 0, 32796, 64265); *

* 返回示例:[35.994564, 12.468795, 123, "北京市朝阳xxx4号楼;望京街xx163米"] *

*/ public static Object[] getCells(int mcc, int mnc, int lac, int ci) { Object[] result = new Object[4]; if (!FLAG.get()) { return result; } // 创建用于接收查询结果的引用对象 DoubleByReference latitude = new DoubleByReference(0.0); // WGS84 纬度 DoubleByReference longitude = new DoubleByReference(0.0); // WGS84 经度 byte[] combined = new byte[ADDRESS_BUFFER_SIZE]; // 调用本地库查询基站信息 int queryResult = CellDll.INSTANCE_DLL.cell_query_combined(mcc, mnc, lac, ci, CellDll.COORD_WGS84, latitude, longitude, combined, combined.length); // 如果查询失败,返回空数组 if (queryResult != 0) { return result; } double sourceLatitude = latitude.getValue(); double sourceLongitude = longitude.getValue(); // 查询结果的原始坐标来自 WGS84,需要先转换为 BD09 再返回给上层。 //double[] bd09 = CoordinateTransform.transformWGS84ToBD09(sourceLongitude, sourceLatitude); result[0] = sourceLatitude; result[1] = sourceLongitude; result[2] = 0; // 逆地理编码依旧使用原始 WGS84 坐标,保持与现有 native 接口行为一致。 result[3] = StrUtil.str(combined, StandardCharsets.UTF_8); return result; } }