1.maven依赖
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-geojson</artifactId>
<version>21.0</version>
</dependency>
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.18.2</version>
</dependency>
<dependency>
<groupId>com.vividsolutions</groupId>
<artifactId>jts</artifactId>
<version>1.13</version>
</dependency>
2.rest接口及参数
2.1参数对象
@ApiModel("坐标转换参数")
public class CoordinateTransformBO {
@ApiModelProperty(value = "经度",required = true)
private Double lon;
@ApiModelProperty(value = "纬度",required = true)
private Double lat;
@ApiModelProperty(value = "转换方式",required = true,example = "BD2WGS、GCJ2BD")
private String type;
public Double getLon() {
return lon;
}
public void setLon(Double lon) {
this.lon = lon;
}
public Double getLat() {
return lat;
}
public void setLat(Double lat) {
this.lat = lat;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
@ApiModel("辖区参数")
public class GeomBO {
@ApiModelProperty(value = "wkt格式空间字段")
private String geometry ;
@ApiModelProperty(value = "geomJson格式空间字段")
private String geomJson ;
public String getGeometry() {
return geometry;
}
public void setGeometry(String geometry) {
this.geometry = geometry;
}
public String getGeomJson() {
return geomJson;
}
public void setGeomJson(String geomJson) {
this.geomJson = geomJson;
}
}
@ApiModel("区域坐标抽稀参数")
public class GeomSimplifyBO {
@ApiModelProperty(value = "空间字段",required = true)
private String geometry ;
@ApiModelProperty(value = "抽稀距离",required = true,example = "0.0003")
private Double tolerance;
public String getGeometry() {
return geometry;
}
public void setGeometry(String geometry) {
this.geometry = geometry;
}
public Double getTolerance() {
return tolerance;
}
public void setTolerance(Double tolerance) {
this.tolerance = tolerance;
}
}
@ApiModel("区域坐标转换参数")
public class GeomTransformBO {
@ApiModelProperty(value = "空间字段")
private String geometry ;
@ApiModelProperty(value = "转换方式")
private String type;
public String getGeometry() {
return geometry;
}
public void setGeometry(String geometry) {
this.geometry = geometry;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
/**
* 转换类型枚举
*/
public enum GisTransformTypeEnum {
BD2WGS("BD2WGS", "百度坐标系(BD-09)转WGS(WGS84)坐标"),
WGS2BD("WGS2BD", "WGS(WGS84)坐标转百度坐标系(BD-09)"),
GCJ2BD("GCJ2BD", "火星坐标系(GCJ-02)转百度坐标系(BD-09)"),
BD2GCJ("BD2GCJ", "百度坐标系(BD-09)转火星坐标系(GCJ-02)"),
WGS2GCJ("WGS2GCJ", "WGS(WGS84)坐标转火星坐标系(GCJ02)"),
GCJ2WGS("GCJ2WGS", "火星坐标系(GCJ02)转WGS(WGS84)坐标"),
WGS2MCT("WGS2MCT", "WGS(WGS84)坐标转墨卡托投影坐标(3857)"),
MCT2WGS("MCT2WGS", "墨卡托投影坐标(3857)转WGS(WGS84)坐标"),
BD2MCT("BD2MCT", "百度坐标系(BD-09)转墨卡托投影坐标(3857)"),
MCT2BD("MCT2BD", "墨卡托投影坐标(3857)转百度坐标系(BD-09)"),
GCJ2MCT("GCJ2MCT", "火星坐标系(GCJ-02)转墨卡托投影坐标(3857)"),
MCT2GCJ("MCT2GCJ", "墨卡托投影坐标(3857)转火星坐标系(GCJ-02)"),
;
private String name;
private String description;
GisTransformTypeEnum(String name, String description) {
this.name = name;
this.description = description;
}
public String getDescription() {
return description;
}
public String getName() {
return name;
}
}
//经纬度点位类
public class Point {
private double lon;
private double lat;
public Point(double lon, double lat) {
this.lon = lon;
this.lat = lat;
}
public double getLon() {
return lon;
}
public double getLat() {
return lat;
}
}
2.2 工具调用service
部分引入包名被删除,自行修改添加即可
package gis.svc.gis;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.vividsolutions.jts.geom.*;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.geo.GeoPoint;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geojson.geom.GeometryJSON;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Collection;
/**
* @Author
* @Description
* @Date 2023/6/1 16:17
*/
@Service
public class GisToolService {
//坐标转换
public Point transform(CoordinateTransformBO coordinateTransformBO) throws Exception {
if (coordinateTransformBO == null || coordinateTransformBO.getLon() == null || coordinateTransformBO.getLat() == null || StringUtils.isBlank(coordinateTransformBO.getType())) {
throw new Exception("缺少必填参数");
}
Point point = new Point(coordinateTransformBO.getLon(), coordinateTransformBO.getLat());
return transformByType(point, coordinateTransformBO.getType());
}
//坐标抽稀
public String simplify(GeomSimplifyBO geomSimplifyBO) throws Exception {
if (geomSimplifyBO == null || StringUtils.isBlank(geomSimplifyBO.getGeometry()) || geomSimplifyBO.getTolerance() == null || geomSimplifyBO.getTolerance() < 0) {
throw new Exception("缺少必填参数");
}
return RarefyUtil.simplifyByGeom(geomSimplifyBO.getGeometry(), geomSimplifyBO.getTolerance()).toString();
}
//获取多边形内心
public GeoPoint getCenterPoint(GeomBO geomBO) throws Exception {
if (geomBO == null || StringUtils.isBlank(geomBO.getGeometry())) {
throw new Exception("缺少必填参数");
}
return GisUtil.getCenterPointByGeometry(geomBO.getGeometry());
}
//区域坐标转换
public String wktTransform(GeomTransformBO geomTransformBO) throws Exception {
if (geomTransformBO == null || StringUtils.isBlank(geomTransformBO.getGeometry()) || StringUtils.isBlank(geomTransformBO.getType())) {
throw new Exception("缺少必填参数!");
}
Geometry geom = GisUtil.getGeomByStr(geomTransformBO.getGeometry());
if (geom != null) {
if (geom instanceof com.vividsolutions.jts.geom.Point) {
com.vividsolutions.jts.geom.Point p = (com.vividsolutions.jts.geom.Point) geom;
Point point = transformByType(new Point(p.getX(), p.getY()), geomTransformBO.getType());
return GisUtil.geometryFactory.createPoint(new Coordinate(point.getLon(), point.getLat())).toString();
} else if (geom instanceof Polygon) {
Polygon polygon = (Polygon) geom;
return transformPolygon(polygon.getCoordinates(), geomTransformBO.getType()).toString();
} else if (geom instanceof MultiPolygon) {
MultiPolygon multiPolygon = (MultiPolygon) geom;
Polygon[] polygons = new Polygon[multiPolygon.getNumGeometries()];
for (int i = 0; i < multiPolygon.getNumGeometries(); i++) {
Geometry geometryN = multiPolygon.getGeometryN(i);
Coordinate[] coordinates = geometryN.getCoordinates();
Polygon polygon = transformPolygon(coordinates, geomTransformBO.getType());
polygons[i] = polygon;
}
return GisUtil.geometryFactory.createMultiPolygon(polygons).toString();
} else if (geom instanceof LineString) {
LineString lineString = (LineString) geom;
Coordinate[] coordinates = lineString.getCoordinates();
Coordinate[] newCoordinates = transformCoordinate(coordinates, geomTransformBO.getType());
return GisUtil.geometryFactory.createLineString(newCoordinates).toString();
} else if (geom instanceof MultiLineString) {
MultiLineString multiLineString = (MultiLineString) geom;
LineString[] lineStrings = new LineString[multiLineString.getNumGeometries()];
for (int i = 0; i < multiLineString.getNumGeometries(); i++) {
Geometry geometryN = multiLineString.getGeometryN(i);
Coordinate[] coordinates = geometryN.getCoordinates();
Coordinate[] newCoordinates = transformCoordinate(coordinates, geomTransformBO.getType());
lineStrings[i] = GisUtil.geometryFactory.createLineString(newCoordinates);
}
return GisUtil.geometryFactory.createMultiLineString(lineStrings).toString();
}
}
return null;
}
private Coordinate[] transformCoordinate(Coordinate[] coordinates, String type) throws Exception {
if (coordinates != null && coordinates.length > 0) {
Coordinate[] newCoordinates = new Coordinate[coordinates.length];
for (int i = 0; i < coordinates.length; i++) {
Coordinate coordinate = coordinates[i];
Point p = new Point(coordinate.x, coordinate.y);
Point newPoint = transformByType(p, type);
Coordinate c = new Coordinate(newPoint.getLon(), newPoint.getLat());
newCoordinates[i] = c;
}
return newCoordinates;
}
return null;
}
private Polygon transformPolygon(Coordinate[] coordinates, String type) throws Exception {
Coordinate[] newCoordinates = transformCoordinate(coordinates, type);
if (newCoordinates != null && newCoordinates.length > 0) {
return GisUtil.geometryFactory.createPolygon(newCoordinates);
}
return null;
}
private Point transformByType(Point point, String type) throws Exception {
switch (GisTransformTypeEnum.valueOf(type)) {
case BD2GCJ:
return TransformUtils.bd09toGcj02(point);
case GCJ2BD:
return TransformUtils.gcj02toBd09(point);
case WGS2BD:
return TransformUtils.wgs84toBd09(point);
case BD2WGS:
return TransformUtils.bd09toWgs84(point);
case WGS2GCJ:
return TransformUtils.wgs84toGcj02(point);
case GCJ2WGS:
return TransformUtils.gcj02toWgs84(point);
case WGS2MCT:
return TransformUtils.wgs84ToMercator(point);
case MCT2WGS:
return TransformUtils.mercatorToWgs84(point);
case BD2MCT:
return TransformUtils.wgs84ToMercator(TransformUtils.bd09toWgs84(point));
case MCT2BD:
return TransformUtils.wgs84toBd09(TransformUtils.mercatorToWgs84(point));
case GCJ2MCT:
return TransformUtils.wgs84ToMercator(TransformUtils.gcj02toWgs84(point));
case MCT2GCJ:
return TransformUtils.wgs84toGcj02(TransformUtils.mercatorToWgs84(point));
default:
throw new Exception("暂不支持的转换类型:" + type);
}
}
//wkt转geomJson
public GeomBO wktToGeomJson(GeomBO geomBO) throws Exception {
if (geomBO != null && StringUtils.isNotBlank(geomBO.getGeometry())) {
Geometry geometry = LayerFactory.getReader().read(geomBO.getGeometry());
if (geometry != null) {
JSONObject geometryJson = new JSONObject();
if (geometry instanceof GeometryCollection) {
JSONArray geometryCollectionArray = new JSONArray();
GeometryCollection geometryCollection = (GeometryCollection) geometry;
for (int i = 0; i < geometryCollection.getNumGeometries(); i++) {
Geometry geometryN = geometryCollection.getGeometryN(i);
JSONArray geometryJsonArray = getGeometryJsonArray(geometryN.getCoordinates());
geometryCollectionArray.add(geometryJsonArray);
}
geometryJson.put("coordinates", geometryCollectionArray);
} else {
JSONArray geometryArray = getGeometryJsonArray(geometry.getCoordinates());
geometryJson.put("coordinates", geometryArray);
}
geometryJson.put("type", geometry.getGeometryType());
JSONObject featureJson = new JSONObject();
featureJson.put("geometry", geometryJson);
featureJson.put("type", "Feature");
geomBO.setGeomJson(featureJson.toJSONString());
}
}
return geomBO;
}
private JSONArray getGeometryJsonArray(Coordinate[] coordinates) {
JSONArray geometryArray = new JSONArray();
if (coordinates != null && coordinates.length > 0) {
for (int i = 0; i < coordinates.length; i++) {
Coordinate coordinate = coordinates[i];
JSONArray coordinateArray = new JSONArray();
coordinateArray.add(coordinate.x);
coordinateArray.add(coordinate.y);
geometryArray.add(coordinateArray);
}
}
return geometryArray;
}
//geomJson转wkt
public JSONArray geomJsonToWkt(GeomBO geomBO) throws Exception {
JSONArray jsonArray = new JSONArray();
if (geomBO != null && StringUtils.isNotBlank(geomBO.getGeomJson())) {
JSONObject dataJson = JSONObject.parseObject(geomBO.getGeomJson());
if (dataJson != null) {
String type = dataJson.getString("type");
if ("FeatureCollection".equals(type)) {
JSONArray features = dataJson.getJSONArray("features");
for (Object feature : features) {
JSONObject jsonObject = analyzeFeature(feature);
jsonArray.add(jsonObject);
}
} else if ("Feature".equals(type)) {
JSONObject jsonObject = analyzeFeature(geomBO.getGeomJson());
jsonArray.add(jsonObject);
} else {
JSONObject jsonObject = new JSONObject();
GeometryJSON geometryJSON = new GeometryJSON();
org.locationtech.jts.geom.Geometry geometry = geometryJSON.read(geomBO.getGeomJson());
jsonObject.put("geometry", geometry.toString());
}
}
}
return jsonArray;
}
private JSONObject analyzeFeature(Object feature) throws IOException {
JSONObject jsonObject = new JSONObject();
FeatureJSON featureJSON = new FeatureJSON();
SimpleFeature simpleFeature = featureJSON.readFeature(feature.toString());
Collection<Property> properties = simpleFeature.getProperties();
for (Property property : properties) {
jsonObject.put(property.getName().toString(), property.getValue());
}
if (simpleFeature != null && simpleFeature.getDefaultGeometry() != null) {
//org.locationtech.jts.geom.GeometryCollection和com.vividsolutions.jts.geom.GeometryCollection格式不一样,
//统一转成com.vividsolutions.jts.geom.GeometryCollection格式
String geometryStr = simpleFeature.getDefaultGeometry().toString();
if (geometryStr.contains("MULTI") || geometryStr.contains("multi") || geometryStr.contains("Multi")) {
geometryStr = geometryStr.replace(") ,(", "),(").replace(") , (", "),(")
.replace("), (", "),(").replace("),(", ")), ((");
}
jsonObject.put("geometry", geometryStr);
}
return jsonObject;
}
//geomJson文件转wkt
public JSONArray geomJsonToWktByFile(MultipartFile file) throws IOException {
JSONArray jsonArray = new JSONArray();
InputStream ins = null;
try {
ins = file.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(ins));
StringBuffer bs = new StringBuffer();
String l = null;
while ((l = buffer.readLine()) != null) {
bs.append(l);
}
String jsonStr = bs.toString();
GeomBO geomBO = new GeomBO();
geomBO.setGeomJson(jsonStr);
jsonArray = geomJsonToWkt(geomBO);
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭流
if (ins != null) {
ins.close();
}
}
return jsonArray;
}
}
2.3操作工具类
/**
*坐标转换工具
*/
public class TransformUtils {
private static Logger log = LoggerFactory.getLogger(TransformUtils.class);
private static final double X_PI = 3.14159265358979324 * 3000.0 / 180.0;
// π
private static final double PI = Math.PI;
// 长半轴
private static final double MAJOR_AXIS = 6378245.0;
// 扁率
private static final double FLATNESS = 0.00669342162296594323;
private static final double EARTH_RAD = 6378137.0; //地球半径
/**
* 百度坐标系(BD-09)转WGS坐标
* @param point 百度坐标点
* @return WGS84坐标数组
*/
public static Point bd09toWgs84(Point point) {
Point gcj = bd09toGcj02(point);
Point wgs84 = gcj02toWgs84(gcj);
return wgs84;
}
/**
* WGS坐标转百度坐标系(BD-09)
*
* @param lng WGS84坐标系的经度
* @param lat WGS84坐标系的纬度
* @return 百度坐标数组
*/
public static Point wgs84toBd09(Point point) {
Point gcj = wgs84toGcj02(point);
Point bd09 = gcj02toBd09(gcj);
return bd09;
}
/**
* 火星坐标系(GCJ-02)转百度坐标系(BD-09)
* <p>
* 谷歌、高德——>百度
*
* @param lng 火星坐标经度
* @param lat 火星坐标纬度
* @return 百度坐标数组
*/
public static Point gcj02toBd09(Point point) {
double lon = point.getLon();
double lat = point.getLat();
double z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * X_PI);
double theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * X_PI);
double bd_lon = z * Math.cos(theta) + 0.0065;
double bd_lat = z * Math.sin(theta) + 0.006;
return new Point(bd_lon, bd_lat);
}
/**
* 百度坐标系(BD-09)转火星坐标系(GCJ-02)
* <p>
* 百度——>谷歌、高德
*
* @param bd_lon 百度坐标纬度
* @param bd_lat 百度坐标经度
* @return 火星坐标数组
*/
public static Point bd09toGcj02(Point point) {
double x = point.getLon() - 0.0065;
double y = point.getLat() - 0.006;
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);
double gg_lon = z * Math.cos(theta);
double gg_lat = z * Math.sin(theta);
return new Point(gg_lon,gg_lat);
}
/**
* WGS84转GCJ02(火星坐标系)
*
* @param lng WGS84坐标系的经度
* @param lat WGS84坐标系的纬度
* @return 火星坐标数组
*/
public static Point wgs84toGcj02(Point point) {
if (out_of_china(point.getLon(), point.getLat())) {
return point;
}
double dLat = transformLat(point.getLon()- 105.0, point.getLat() - 35.0);
double dLon= transformLon(point.getLon() - 105.0, point.getLat() - 35.0);
double radLat = point.getLat() / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - FLATNESS * magic * magic;
double sqrtmagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((MAJOR_AXIS * (1 - FLATNESS)) / (magic * sqrtmagic) * PI);
dLon = (dLon * 180.0) / (MAJOR_AXIS / sqrtmagic * Math.cos(radLat) * PI);
double mgLat = point.getLat() + dLat;
double mgLng = point.getLon() + dLon;
return new Point(mgLng, mgLat);
}
/**
* GCJ02(火星坐标系)转GPS84
*
* @param lng 火星坐标系的经度
* @param lat 火星坐标系纬度
* @return WGS84坐标数组
*/
public static Point gcj02toWgs84(Point gcj) {
if (out_of_china(gcj.getLon(), gcj.getLat())) {
return gcj;
}
double dLat = transformLat(gcj.getLon() - 105.0, gcj.getLat() - 35.0);
double dLon = transformLon(gcj.getLon() - 105.0, gcj.getLat() - 35.0);
double radLat = gcj.getLat() / 180.0 * PI;
double magic = Math.sin(radLat);
magic = 1 - FLATNESS * magic * magic;
double sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((MAJOR_AXIS * (1 - FLATNESS)) / (magic * sqrtMagic) * PI);
dLon = (dLon * 180.0) / (MAJOR_AXIS / sqrtMagic * Math.cos(radLat) * PI);
double mgLat = gcj.getLat() + dLat;
double mgLon= gcj.getLon() + dLon;
return new Point(gcj.getLon() * 2 - mgLon, gcj.getLat() * 2 - mgLat);
}
/**
* 纬度转换
*
* @param lng
* @param lat
* @return
*/
public static double transformLat(double lng, double lat) {
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
return ret;
}
/**
* 经度转换
*
* @param lng
* @param lat
* @return
*/
public static double transformLon(double lng, double lat) {
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
return ret;
}
/**
* 判断是否在国内,不在国内不做偏移
*
* @param lng
* @param lat
* @return
*/
public static boolean out_of_china(double lng, double lat) {
if (lng < 72.004 || lng > 137.8347) {
return true;
} else if (lat < 0.8293 || lat > 55.8271) {
return true;
}
return false;
}
/**
* WGS坐标转墨卡托
*
* @return WGS84坐标数组
*/
public static Point wgs84ToMercator(Point wgs) {
Double lon = wgs.getLon()*PI/180*EARTH_RAD;
Double param =wgs.getLat()*PI/180;
Double lat = EARTH_RAD / 2 * Math.log((1.0 + Math.sin(param)) / (1.0 - Math.sin(param)));
return new Point(lon,lat);
}
/**
* WGS坐标转墨卡托
* @return WGS84坐标数组
*/
public static Point mercatorToWgs84(Point mercator) {
Double lon = mercator.getLon()*180/EARTH_RAD;
Double param =mercator.getLat()*180/EARTH_RAD;
Double lat = 180 / Math.PI * (2 * Math.atan(Math.exp(param * Math.PI / 180)) - Math.PI / 2);
return new Point(lon,lat);
}
}
public class GisUtil {
public final static GeometryFactory geometryFactory = new GeometryFactory();
public static GeoPoint getCenterPointByGeometry(String wkt) throws ParseException {
Geometry geom = new WKTReader(geometryFactory).read(wkt);
return getGeomCenterPoint(geom);
}
public static GeoPoint getGeomCenterPoint(Geometry geom) {
if (geom == null) {
return null;
}
if (geom instanceof MultiPolygon) {
//判断是否多边形
//先默认第一个最大
int index = 0;
if (geom.getNumGeometries() > 1) {
//找出最大的面积
//记录第一个面积大小
double area = geom.getGeometryN(0).getArea();
System.out.println(area);
for (int i = 1; i < geom.getNumGeometries(); i++) {
if (geom.getGeometryN(i).getArea() > area) {
area = geom.getGeometryN(i).getArea();
index = i;
}
}
}
return new GeoPoint(geom.getGeometryN(index).getInteriorPoint().getY(),geom.getGeometryN(index).getInteriorPoint().getX());
} else if (geom instanceof Polygon) {
return new GeoPoint(geom.getInteriorPoint().getY(),geom.getInteriorPoint().getX());
}
return null;
}
public static Geometry getGeomByStr(String geomStr) {
if (StringUtils.isBlank(geomStr)) {
return null;
}
WKTReader reader = new WKTReader(geometryFactory);
try {
return reader.read(geomStr);
} catch (ParseException e) {
e.printStackTrace();
}
return null;
}
}
/**
* @Author
* @Description 抽稀工具类
* @Date 2023/6/2 14:34
*/
public class RarefyUtil {
private static Logger logger = LoggerFactory.getLogger(RarefyUtil.class);
private static final GeometryFactory geometryFactory = new GeometryFactory();
private static final WKTReader wktReader = new WKTReader(geometryFactory);
public static Geometry simplifyByGeom(String geom, double tolerance) throws ParseException {
return simplifyByGeom(wktReader.read(geom), tolerance);
}
/**
* 方法说明: 将空间对象,按照一定的距离进行抽稀
*
* @param geom 空间对象
* @param tolerance 抽稀距离
* @return com.vividsolutions.jts.geom.Geometry
* @date 2023/6/2 14:50
* @author
*/
public static Geometry simplifyByGeom(Geometry geom, double tolerance) throws ParseException {
if (geom == null || geom.isEmpty()) {
return geom;
} else if (geom instanceof Point) {
return geom;
} else if (geom instanceof MultiLineString) {
MultiLineString multiLine =(MultiLineString)geom;
LineString[] lines = new LineString[multiLine.getNumGeometries()];
for (int i = 0; i < multiLine.getNumGeometries(); i++) {
LineString lineString = (LineString) multiLine.getGeometryN(i);
lines[i] = (LineString) simplifyByGeom(lineString, tolerance);
}
return geom.getFactory().createMultiLineString(lines);
} else if (geom instanceof LineString) {
LineString line = (LineString) geom;
Coordinate[] coords = line.getCoordinates();
if (coords.length <= 2) {
return line;
}
List<Coordinate> simplify = simplify(coords, tolerance);
return geom.getFactory().createLineString(simplify.toArray(new Coordinate[simplify.size()]));
} else if (geom instanceof Polygon) {
Polygon poly = (Polygon) geom;
Coordinate[] coordinates = poly.getCoordinates();
//取出最后一个点
Coordinate lastCoordinate = coordinates[coordinates.length - 1];
List<Coordinate> coordinateList = new ArrayList<>();
//闭合多边形,coordinateList的首尾点是同一个
for (int i = 0; i < coordinates.length - 1; i++) {
//取出多边形的所有散点,不包含最后一个点
coordinateList.add(coordinates[i]);
}
List<Coordinate> simplify = simplify(coordinateList, tolerance);
//抽稀之后,无法形成闭合多边形,返回原有多边形
if(simplify.size()<3){
//返回的是一条直线,重新抽稀,按照最大距离点抽稀,
simplify = maxDistanceSimplify(coordinateList);
}
//抽稀完成,将最后一个点加回去,形成闭合多边形
simplify.add(lastCoordinate);
return geom.getFactory().createPolygon(simplify.toArray(new Coordinate[simplify.size()]));
} else if (geom instanceof MultiPolygon) {
MultiPolygon multiPoly = (MultiPolygon) geom;
Polygon[] polys = new Polygon[multiPoly.getNumGeometries()];
for (int i = 0; i < multiPoly.getNumGeometries(); i++) {
Polygon poly = (Polygon) multiPoly.getGeometryN(i);
polys[i] = (Polygon) simplifyByGeom(poly, tolerance);
}
return geom.getFactory().createMultiPolygon(polys);
} else {
throw new IllegalArgumentException("Unsupported geometry type: " + geom.getClass());
}
}
private static List<Coordinate> maxDistanceSimplify(List<Coordinate> points) {
List<Coordinate> result = new ArrayList<>();
int n = points.size();
if (n < 3) {
return points; // 如果点的数量小于3,无法抽稀,直接返回原点集
}
// 找到距离起点和终点最远的点,默认为中间点
Coordinate maxDistanceCoordinate=points.get(n/2+1);
double maxDistance = 0;
for (int i = 1; i < n - 1; i++) {
double distance = perpendicularDistance(points.get(i), points.get(0), points.get(n - 1));
if (distance > maxDistance) {
maxDistanceCoordinate = points.get(i);
maxDistance = distance;
}
}
result.add(points.get(0));
result.add(maxDistanceCoordinate);
result.add(points.get(n - 1));
return result;
}
/**
* 方法说明: 计算曲线中的每个点到曲线首尾点所在直线的距离
*
* @param p
* @param p1
* @param p2
* @return double
* @date 2023/6/2 14:51
* @author
*/
private static double perpendicularDistance2(Coordinate p, Coordinate p1, Coordinate p2) {
double area = Math.abs(0.5 * ((p1.x - p.x) * (p2.y - p.y) - (p2.x - p.x) * (p1.y - p.y)));
double base = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
return area / base;
}
/**
* 方法说明: 按坐标点抽稀
*
* @param points 坐标点
* @param tolerance 距离
* @return java.util.List<com.vividsolutions.jts.geom.Coordinate>
* @date 2023/6/2 14:54
* @author
*/
public static List<Coordinate> simplify(Coordinate[] points, double tolerance) {
int n = points.length;
boolean[] keep = new boolean[n];
keep[0] = keep[n - 1] = true;
simplify(points, keep, 0, n - 1, tolerance);
List<Coordinate> result = new ArrayList<>();
for (int i = 0; i < n; i++) {
if (keep[i]) {
result.add(points[i]);
}
}
return result;
}
/**
* 方法说明: 抽希算法
*
* @param points 坐标点
* @param keep 是否保留
* @param start 开始位置索引
* @param end 结束位置索引
* @param tolerance 距离
* @return void
* @date 2023/6/2 14:56
* @author
*/
private static void simplify(Coordinate[] points, boolean[] keep, int start, int end, double tolerance) {
if (end <= start + 1) {
return;
}
double maxDistance = 0;
int index = 0;
Coordinate p1 = points[start];
Coordinate p2 = points[end];
for (int i = start + 1; i < end; i++) {
Coordinate p = points[i];
double distance = perpendicularDistance(p, p1, p2);
if (distance > maxDistance) {
maxDistance = distance;
index = i;
}
}
if (maxDistance > tolerance) {
keep[index] = true;
simplify(points, keep, start, index, tolerance);
simplify(points, keep, index, end, tolerance);
}
}
/**
* 方法说明: 道格拉斯抽希算法
*
* @param points 坐标点
* @param tolerance 距离
* @return java.util.List<com.vividsolutions.jts.geom.Coordinate>
* @date 2023/6/2 14:57
* @author
*/
public static List<Coordinate> simplify(List<Coordinate> points, double tolerance) {
List<Coordinate> result = new ArrayList<>();
int n = points.size();
if (n < 3) {
return points; // 如果点的数量小于3,无法抽稀,直接返回原点集
}
// 找到距离起点和终点最远的点
int index = 0;
double maxDistance = 0;
for (int i = 1; i < n - 1; i++) {
double distance = perpendicularDistance(points.get(i), points.get(0), points.get(n - 1));
if (distance > maxDistance) {
index = i;
maxDistance = distance;
}
}
// 如果最远点与起点或终点重合,则直接返回原点集
if (maxDistance <= tolerance) {
//最大距离小于目标值,说明符合抽稀的规则,取首尾点(抽稀)
result.add(points.get(0));
result.add(points.get(n - 1));
} else {
// 分割成左右两部分,对每部分进行抽稀
List<Coordinate> leftPoints = new ArrayList<>();
for (int i = 0; i <= index; i++) {
leftPoints.add(points.get(i));
}
List<Coordinate> rightPoints = new ArrayList<>();
for (int i = index; i < n; i++) {
rightPoints.add(points.get(i));
}
List<Coordinate> leftResult = simplify(leftPoints, tolerance);
List<Coordinate> rightResult = simplify(rightPoints, tolerance);
// 合并左右两部分的抽稀结果
result.addAll(leftResult.subList(0, leftResult.size() - 1));
result.addAll(rightResult);
}
return result;
}
/**
* 方法说明: 计算点p到直线ab的垂直距离
*
* @param p
* @param a
* @param b
* @return double
* @date 2023/6/2 14:58
* @author
*/
private static double perpendicularDistance(Coordinate p, Coordinate a, Coordinate b) {
double x = p.x;
double y = p.y;
double x1 = a.x;
double y1 = a.y;
double x2 = b.x;
double y2 = b.y;
double dx = x2 - x1;
double dy = y2 - y1;
double d = dx * dx + dy * dy;
double u = ((x - x1) * dx + (y - y1) * dy) / d;
double px, py;
if (u < 0) {
px = x1;
py = y1;
} else if (u > 1) {
px = x2;
py = y2;
} else {
px = x1 + u * dx;
py = y1 + u * dy;
}
return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py));
}
}