
org.coodex.jts.JTSUtil Maven / Gradle / Ivy
/*
* Copyright (c) 2016 - 2021 coodex.org ([email protected])
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.coodex.jts;
import org.coodex.util.Common;
import org.coodex.util.LazySelectableServiceLoader;
import org.coodex.util.SelectableServiceLoader;
import org.locationtech.jts.geom.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class JTSUtil {
public static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
private static final
SelectableServiceLoader> CONVERT_SERVICE_LOADER
= new LazySelectableServiceLoader>() {
};
/**
* 地球赤道半径
*/
private static final double EARTH_RADIUS = 6378137.0d;
public static double[] lngLat2Mercator(double[] lngLat) {
return lngLat2Mercator(lngLat[0], lngLat[1]);
}
public static double[] lngLat2Mercator(double lng, double lat) {
double x = lng * Math.PI / 180 * EARTH_RADIUS;
double a = lat * Math.PI / 180;
double y = EARTH_RADIUS / 2 * Math.log((1.0d + Math.sin(a)) / (1.0d - Math.sin(a)));
return new double[]{x, y};
}
public static double[] mercator2LngLat(double[] mercator) {
return mercator2LngLat(mercator[0], mercator[1]);
}
public static double[] mercator2LngLat(double x, double y) {
double lng = x / (EARTH_RADIUS * Math.PI / 180);
double lat = y / (EARTH_RADIUS * Math.PI / 180);
lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);
return new double[]{lng, lat};
}
public static double distance(double x1, double y1, double x2, double y2) {
double dx = x2 - x1;
double dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
public static T mercator2LngLat(T mercator) {
return Common.cast(Objects.requireNonNull(CONVERT_SERVICE_LOADER.select(mercator)).toLngLat(mercator));
}
public static T lngLat2Mercator(T lngLat) {
return Common.cast(Objects.requireNonNull(CONVERT_SERVICE_LOADER.select(lngLat)).toMercator(lngLat));
}
public static Coordinate mercator2LngLat(Coordinate mercator) {
double[] g = mercator2LngLat(mercator.getX(), mercator.getY());
return new Coordinate(g[0], g[1]);
}
public static Coordinate lngLat2Mercator(Coordinate lngLat) {
double[] g = lngLat2Mercator(lngLat.getX(), lngLat.getY());
return new Coordinate(g[0], g[1]);
}
public static Coordinate[] mercator2LngLat(Coordinate[] mercator) {
Coordinate[] coordinates = new Coordinate[mercator.length];
for (int i = 0; i < coordinates.length; i++) {
coordinates[i] = mercator2LngLat(mercator[i]);
}
return coordinates;
}
public static Coordinate[] lngLat2Mercator(Coordinate[] lngLat) {
Coordinate[] coordinates = new Coordinate[lngLat.length];
for (int i = 0; i < coordinates.length; i++) {
coordinates[i] = lngLat2Mercator(lngLat[i]);
}
return coordinates;
}
/**
* 根据阈值裁剪内孔
*
* @param geometry geometry
* @param threshold 阈值,内孔占外边框面积的比例
* @return 裁剪后的几何图形
*/
public static T crop(T geometry, double threshold) {
if (geometry instanceof MultiPolygon) {
Geometry g = null;
for (int i = 0, l = geometry.getNumGeometries(); i < l; i++) {
if (g != null) {
g = g.union(crop(geometry.getGeometryN(i), threshold));
} else {
g = geometry.getGeometryN(i);
}
}
return Common.cast(g);
} else if (geometry instanceof Polygon) {
Polygon polygon = (Polygon) geometry;
if (polygon.getNumInteriorRing() == 0) {
return geometry;
}
double outer = areaOf(polygon.getExteriorRing());
List holes = new ArrayList<>();
for (int i = 0, l = polygon.getNumInteriorRing(); i < l; i++) {
LinearRing hole = polygon.getInteriorRingN(i);
if (areaOf(hole) / outer > threshold) {
holes.add(hole);
}
}
return Common.cast(holes.size() == 0 ? GEOMETRY_FACTORY.createPolygon(polygon.getExteriorRing()) :
GEOMETRY_FACTORY.createPolygon(
polygon.getExteriorRing(),
holes.toArray(new LinearRing[0])
));
} else {
return geometry;
}
}
public static double areaOf(Geometry lngLat) {
if (lngLat instanceof MultiPolygon) {
return areaOf((MultiPolygon) lngLat);
} else if (lngLat instanceof Polygon) {
return areaOf((Polygon) lngLat);
} else if (lngLat instanceof LinearRing) {
return areaOf((LinearRing) lngLat);
} else {
return 0d;
}
}
private static double areaOf(MultiPolygon multiPolygon) {
double area = 0d;
for (int i = 0, size = multiPolygon.getNumGeometries(); i < size; i++) {
area += areaOf(multiPolygon.getGeometryN(i));
}
return area;
}
private static double areaOf(Polygon polygon) {
double area = areaOf(polygon.getExteriorRing());
for (int i = 0, holes = polygon.getNumInteriorRing(); i < holes; i++) {
area -= areaOf(polygon.getInteriorRingN(i));
}
return Math.max(0d, area);
}
private static boolean isLngLat(Geometry geometry) {
Coordinate c = geometry.getCoordinate();
return c == null || isLngLat(c);
}
private static boolean isLngLat(Coordinate coordinate) {
return coordinate.getX() < 180d && coordinate.getX() > -180d &&
coordinate.getY() < 90d && coordinate.getY() > -90;
}
private static double areaOf(LinearRing lngLatRing) {
if (!isLngLat(lngLatRing)) {
lngLatRing = mercator2LngLat(lngLatRing);
}
Coordinate[] coordinates = lngLatRing.getCoordinates();
// 找到最小矩阵的左下角点作为原点
double x = 180d, y = 180d;
for (Coordinate c : coordinates) {
x = Math.min(x, c.getX());
y = Math.min(y, c.getY());
}
// 令,任意点(x1,y1),投影坐标为(d((x1,y),(x,y)), d((x,y1),(x,y)))
Coordinate[] newPoints = new Coordinate[coordinates.length];
for (int i = 0, l = coordinates.length; i < l; i++) {
Coordinate c = coordinates[i];
newPoints[i] = new Coordinate(distanceLngLat(x, y, c.getX(), y), distanceLngLat(x, y, x, c.getY()));
}
return JTSUtil.GEOMETRY_FACTORY.createPolygon(newPoints).getArea();
}
private static double rad(double d) {
return d * Math.PI / 180.0d;
}
public static double distanceLngLat(double x1, double y1, double x2, double y2) {
double radLat1 = rad(y1);
double radLat2 = rad(y2);
double a = radLat1 - radLat2;
double b = rad(x1) - rad(x2);
double s = 2 * Math.asin(
Math.sqrt(
Math.pow(Math.sin(a / 2), 2)
+ Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)
)
);
return s * EARTH_RADIUS;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy