package bianjie;
|
|
import java.io.*;
|
import java.nio.file.Files;
|
import java.nio.file.Paths;
|
|
public class bianjieguihua {
|
|
/**
|
* 静态方法:处理边界数据并返回边界点坐标
|
*
|
* @param filePath GNGGA数据文件路径(从根目录的GNGGA.txt文件获取)
|
* @param baseStation 基准站坐标,格式:"纬度,N/S,经度,E/W" 例如:"2324.194945,N,11330.938547,E"
|
* @param interval 边界点间隔(米)
|
* @param angleThreshold 角度阈值(度)
|
* @return 边界点坐标字符串,格式:"x1,y1;x2,y2;xn,yn"
|
*/
|
public static String processBoundary(String filePath, String baseStation,
|
double interval, double angleThreshold) {
|
try {
|
// 1. 从TXT文件读取GNGGA数据
|
String gnggaData = readGNGGAFromTxtFile(filePath);
|
|
if (gnggaData == null || gnggaData.trim().isEmpty()) {
|
throw new RuntimeException("无法从文件读取GNGGA数据或数据为空");
|
}
|
|
// 2. 使用BoundaryProcessor处理数据
|
String boundaryCoordinates = BoundaryProcessor.processBoundaryData(
|
gnggaData, baseStation, interval, angleThreshold);
|
|
return boundaryCoordinates;
|
|
} catch (Exception e) {
|
throw new RuntimeException("处理边界数据时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 从TXT文件读取GNGGA数据
|
* 文件格式:多个$GNGGA数据记录,用$GNGGA分隔
|
*/
|
private static String readGNGGAFromTxtFile(String filePath) {
|
try {
|
// 首先尝试从当前工作目录(根目录)加载
|
File file = new File(filePath);
|
|
// 如果文件不存在,尝试在用户当前目录下查找
|
if (!file.exists()) {
|
file = new File(System.getProperty("user.dir") + File.separator + filePath);
|
}
|
|
// 如果还是不存在,尝试在类路径中查找
|
if (!file.exists()) {
|
InputStream input = bianjieguihua.class.getClassLoader().getResourceAsStream(filePath);
|
if (input != null) {
|
// 从类路径读取
|
return readFromInputStream(input);
|
} else {
|
throw new RuntimeException("文件未找到: " + filePath +
|
" (搜索路径: " + new File(".").getAbsolutePath() + ")");
|
}
|
}
|
|
// 从文件系统读取
|
String content = new String(Files.readAllBytes(Paths.get(file.getAbsolutePath())));
|
|
// 清理数据:移除多余的空格和换行,确保格式正确
|
content = content.replaceAll("\\r\\n|\\r|\\n", ""); // 移除换行符
|
content = content.replaceAll("\\s+", ""); // 移除空白字符
|
content = content.trim();
|
|
// 确保数据以$GNGGA开头(如果不是,可能文件格式不对)
|
if (!content.startsWith("$GNGGA") && content.contains("$GNGGA")) {
|
// 如果包含$GNGGA但不以它开头,可能需要处理
|
System.out.println("警告: GNGGA数据格式可能需要调整");
|
}
|
|
return content;
|
|
} catch (Exception e) {
|
throw new RuntimeException("读取GNGGA TXT文件时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 从输入流读取数据(用于类路径资源)
|
*/
|
private static String readFromInputStream(InputStream inputStream) throws IOException {
|
StringBuilder resultStringBuilder = new StringBuilder();
|
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
|
String line;
|
while ((line = br.readLine()) != null) {
|
resultStringBuilder.append(line.trim());
|
}
|
}
|
return resultStringBuilder.toString();
|
}
|
|
/**
|
* 计算坐标点数量
|
*/
|
private static int countCoordinates(String coordinates) {
|
if (coordinates == null || coordinates.trim().isEmpty()) {
|
return 0;
|
}
|
// 按分号分割坐标点
|
String[] points = coordinates.split(";");
|
return points.length;
|
}
|
|
/**
|
* 高级处理方法 - 允许使用自定义参数
|
*
|
* @param filePath GNGGA数据文件路径
|
* @param baseStation 基准站坐标
|
* @param params 边界参数对象
|
* @return 边界点坐标字符串
|
*/
|
public static String processBoundaryAdvanced(String filePath, String baseStation,
|
BoundaryAlgorithm.BoundaryParameters params) {
|
try {
|
// 从文件读取GNGGA数据
|
String gnggaData = readGNGGAFromTxtFile(filePath);
|
|
if (gnggaData == null || gnggaData.trim().isEmpty()) {
|
throw new RuntimeException("无法从文件读取GNGGA数据或数据为空");
|
}
|
|
// 使用BoundaryProcessor的高级处理方法
|
String boundaryCoordinates = BoundaryProcessor.processBoundaryDataAdvanced(
|
gnggaData, baseStation, params);
|
return boundaryCoordinates;
|
|
} catch (Exception e) {
|
throw new RuntimeException("处理边界数据时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 仅进行场景分析,不生成边界
|
*
|
* @param filePath GNGGA数据文件路径
|
* @param baseStation 基准站坐标
|
* @return 场景分析结果
|
*/
|
public static BoundaryAlgorithm.SceneAnalysis analyzeScene(String filePath, String baseStation) {
|
try {
|
// 从文件读取GNGGA数据
|
String gnggaData = readGNGGAFromTxtFile(filePath);
|
|
if (gnggaData == null || gnggaData.trim().isEmpty()) {
|
throw new RuntimeException("无法从文件读取GNGGA数据或数据为空");
|
}
|
|
// 使用BoundaryProcessor进行场景分析
|
return BoundaryProcessor.analyzeScene(gnggaData, baseStation);
|
|
} catch (Exception e) {
|
throw new RuntimeException("场景分析时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 完整处理流程:场景分析 + 自动参数选择 + 边界生成
|
*
|
* @param filePath GNGGA数据文件路径
|
* @param baseStation 基准站坐标
|
* @return 边界点坐标字符串
|
*/
|
public static String processBoundaryAuto(String filePath, String baseStation) {
|
try {
|
// 1. 场景分析
|
BoundaryAlgorithm.SceneAnalysis analysis = analyzeScene(filePath, baseStation);
|
|
// 2. 根据场景分析结果获取参数
|
BoundaryAlgorithm algorithm = new BoundaryAlgorithm();
|
BoundaryAlgorithm.BoundaryParameters params =
|
algorithm.getParametersForPreset(analysis.suggestedPreset);
|
|
// 3. 使用高级处理方法生成边界
|
return processBoundaryAdvanced(filePath, baseStation, params);
|
|
} catch (Exception e) {
|
throw new RuntimeException("自动边界处理时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 解析GNGGA数据并提取有效的经纬度点集合(度分格式保存)
|
*
|
* @param gnggaData 包含多条GNGGA记录的字符串
|
* @return 有效的经纬度点集合,格式为"纬度方向,经度方向;纬度方向,经度方向;..."
|
*/
|
public static String parseGNGGAToCoordinates(String gnggaData) {
|
try {
|
if (gnggaData == null || gnggaData.trim().isEmpty()) {
|
return "";
|
}
|
|
StringBuilder coordinatesBuilder = new StringBuilder();
|
|
// 按$GNGGA分割记录
|
String[] records = gnggaData.split("\\$GNGGA");
|
|
for (String record : records) {
|
try {
|
String trimmedRecord = record.trim();
|
if (trimmedRecord.isEmpty()) continue;
|
|
// 确保记录以逗号开头以便正确分割字段
|
if (!trimmedRecord.startsWith(",")) {
|
trimmedRecord = "," + trimmedRecord;
|
}
|
|
String[] fields = trimmedRecord.split(",");
|
if (fields.length < 7) {
|
continue; // 字段不足,跳过
|
}
|
|
// 检查定位质量 (第7个字段,索引6)
|
String fixQualityStr = fields[6];
|
if (fixQualityStr.isEmpty()) {
|
continue;
|
}
|
|
int fixQuality;
|
try {
|
fixQuality = Integer.parseInt(fixQualityStr);
|
} catch (NumberFormatException e) {
|
continue; // 定位质量格式错误,跳过
|
}
|
|
// 只采用高精度定位点 (4 = RTK固定解)
|
if (fixQuality != 4) {
|
continue;
|
}
|
|
// 提取纬度度分格式 (第3个字段,索引2) 和方向 (第4个字段,索引3)
|
String latitudeStr = fields[2];
|
String latDirection = fields[3];
|
|
// 提取经度度分格式 (第5个字段,索引4) 和方向 (第6个字段,索引5)
|
String longitudeStr = fields[4];
|
String lonDirection = fields[5];
|
|
if (latitudeStr.isEmpty() || longitudeStr.isEmpty() ||
|
latDirection.isEmpty() || lonDirection.isEmpty()) {
|
continue; // 坐标数据不完整,跳过
|
}
|
|
// 直接保存度分格式和方向
|
if (coordinatesBuilder.length() > 0) {
|
coordinatesBuilder.append(";");
|
}
|
coordinatesBuilder.append(latitudeStr)
|
.append(",")
|
.append(latDirection)
|
.append(",")
|
.append(longitudeStr)
|
.append(",")
|
.append(lonDirection);
|
|
} catch (Exception e) {
|
// 单条记录解析失败,继续处理下一条
|
System.err.println("解析GNGGA记录失败: " + record + ", 错误: " + e.getMessage());
|
}
|
}
|
|
return coordinatesBuilder.toString();
|
|
} catch (Exception e) {
|
throw new RuntimeException("解析GNGGA数据时发生错误: " + e.getMessage(), e);
|
}
|
}
|
|
/**
|
* 将度分格式坐标转换为十进制度格式
|
*
|
* @param dmCoord 度分格式坐标 (DDMM.MMMMM)
|
* @param direction 方向 (N/S/E/W)
|
* @return 十进制度格式坐标
|
*/
|
private static double parseDMToDecimal(String dmCoord, String direction) {
|
try {
|
if (dmCoord == null || dmCoord.isEmpty()) {
|
return 0;
|
}
|
|
int dotIndex = dmCoord.indexOf('.');
|
if (dotIndex < 2) {
|
return 0;
|
}
|
|
// 提取度部分和分部分
|
int degrees = Integer.parseInt(dmCoord.substring(0, dotIndex - 2));
|
double minutes = Double.parseDouble(dmCoord.substring(dotIndex - 2));
|
|
// 转换为十进制度
|
double decimal = degrees + minutes / 60.0;
|
|
// 根据方向调整正负
|
if ("S".equals(direction) || "W".equals(direction)) {
|
decimal = -decimal;
|
}
|
|
return decimal;
|
|
} catch (Exception e) {
|
throw new IllegalArgumentException("度分坐标解析错误: " + dmCoord, e);
|
}
|
}
|
|
|
|
/**
|
* 测试方法
|
*/
|
public static void main(String[] args) {
|
try {
|
// 创建示例文件(如果不存在)
|
File testFile = new File("GNGGA.txt");
|
|
|
// 测试数据
|
String filePath = "GNGGA.txt";
|
String baseStation = "3949.91202005,N,11616.85440851,E";
|
|
|
// 方法2: 自动处理(推荐)
|
String result2 = processBoundaryAuto(filePath, baseStation);
|
System.out.println("自动处理方法结果: " + result2);
|
|
} catch (Exception e) {
|
System.err.println("测试失败: " + e.getMessage());
|
e.printStackTrace();
|
}
|
}
|
}
|