java jts获取线上任意一点到起点的距离
近期项目要求计算某段公路上一辆车的运行轨迹,通过路上的设备实时获取车辆的经纬度信息并发送到后台接收。
抽象出来就是获取线上任意一点到起点的距离,按照一定每秒一次的频率去计算就获取该点的运动轨迹了。
主要通过使用jts的工具类来计算。
<dependency>
<groupId>com.vividsolutions</groupId>
<artifactId>jts</artifactId>
<version>1.13</version>
</dependency>
jts使用样例
jts官方API文档
期间需要将经纬度转换成平面坐标。这里使用墨卡托进行坐标转换。
具体参考了如下文章,本文就不再引用了直接使用其工具类。
墨卡托经纬度转换平面坐标
正文代码如下:
@Data
@NoArgsConstructor
public class ExtLine {
private String wkt;//原始的线的wkt信息
private JSONObject geojson;
private Coordinate[] coordinates;//组成线的每个端点的信息(经纬度已转换成平面坐标)
private LineString line;//经过墨卡托转换后的线的对象
private Double[] distances;//保存每个节点距离起点的距离
}
/**
* @param wkt 原始线的wkt信息 需要经过墨卡托转换
* @return ExtLine
* 通过wkt创建ExtLine 该类中包含了各个点位的距离起点信息
* */
public static ExtLine createExtLine(String wkt) throws ParseException {
WKTReader reader = new WKTReader();
LineString lineString = (LineString) reader.read(wkt);
ExtLine extLine = new ExtLine();
extLine.setWkt(wkt);
Coordinate[] row = lineString.getCoordinates();
Coordinate[] result = new Coordinate[row.length];
Double[] distance = new Double[row.length];
for (int i = 0; i < row.length; i++) {
double lng = row[i].x;
double lat = row[i].y;
double rate = Math.cos(Math.toRadians(lat));//墨卡托转成平面坐标计算距离时会拉伸长度使得结果偏大,需要对结果乘以当前纬度的cos值
Map<String, Double> tmp = new HashMap<>();
tmp = CoordConverter.convertLL2MC(lng, lat);//经过墨卡托转换
Coordinate coordinate = new Coordinate(tmp.get("x"), tmp.get("y"));
result[i] = coordinate;
if (i == 0) {
//起点位置
distance[0] = 0.0D;
} else {
//计算并保存每个节点距离起点的长度
LineString tempLine = geometryFactory.createLineString(Arrays.copyOf(result,i+1));
distance[i] = tempLine.getLength() * rate;
}
}
extLine.setDistances(distance);
extLine.setCoordinates(result);
extLine.setLine(geometryFactory.createLineString(result));
System.out.println(distance[row.length-1]);
return extLine;
}
/**
* @param extLine 将上一步的计算好距离的信息对象传入
* @param wkt 某点的wkt信息
* @return double 返回某条线上的起点到某点的距离
* */
public static double getDistance(ExtLine extLine,String wkt) throws ParseException {
WKTReader reader = new WKTReader();
Point point = (Point) reader.read(wkt);//获取某点的信息(不一定要在线上)
Coordinate coordinate = point.getCoordinate();
double lng = coordinate.x;
double lat = coordinate.y;
Map<String,Double> map = CoordConverter.convertLL2MC(lng,lat);//将经纬度转换成平面坐标
Coordinate transCoordinate = new Coordinate(map.get("x"),map.get("y"));
LineString lineString = extLine.getLine();
LocationIndexedLine indexedLine = new LocationIndexedLine(lineString);
LinearLocation linearLocation = indexedLine.indexOf(transCoordinate);
int index = linearLocation.getSegmentIndex();//获取某点距离线上最近的节点的index
Coordinate[] coordinates = extLine.getCoordinates();
Coordinate lastPoint = coordinates[index];
System.out.println(index);
LineString temp = geometryFactory.createLineString(new Coordinate[]{lastPoint,transCoordinate});//计算线上距离某点最近的点和某点的距离
double distance = temp.getLength()+extLine.getDistances()[index];//两者相加即所求距离
return distance;
}
代码的图片说明如下:
线由节点1,2,3,4,5组成,需要计算点6到起点的距离(非计算点1和点6的直线距离 这样误差太大了) 需要计算点1-4的距离 这个步骤在createExtLine时就完成了 后续只需传入ExtLine 和点6的wkt 计算点4和点6的距离 d1 最后把点1-4的距离d2 return d1+d2即可。
如何计算点6距离线上某个节点最近通过linearLocation.getSegmentIndex();即可得到最近的前一个节点。
第一次写博客感觉写的不是很清楚,希望对这方面有需求的朋友有所帮助吧。