• jts空间索引全球行政区


    我们需要在项目中引入com.vividsolutions.jts这个包,**JTS**是加拿大的 Vivid Solutions公司做的一套开放源码的 Java API。它提供了一套空间数据操作的核心算法。为在兼容OGC标准的空间对象模型中进行基础的几何操作提供2D空间谓词API。

    全球的行政区划分文件可以在 gadm官网https://gadm.org/download_world.html上下载,

    maven仓库为

    ```xml
    <!-- https://mvnrepository.com/artifact/com.vividsolutions/jts -->
    <dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts</artifactId>
    <version>1.13</version>
    </dependency>
    <dependency>
    <groupId>com.vividsolutions</groupId>
    <artifactId>jts-io</artifactId>
    <version>1.14.0</version>
    </dependency>
    ```

    ```java
    @Component
    public class GlobalSearch{
    private static final Logger logger = LoggerFactory.getLogger(GlobalSearch.class);

    // only used for world
    private static Map<IndexedPointInAreaLocator, Geometry> worldMap = new HashMap<>();
    private static STRtree worldStrTree = new STRtree();

    static {
    init();
    }


    public GlobalDistrict queryGlobalDistrict(CoordinatePoint coordinate, String coordinateType) throws Exception {
    if(coordinate==null){
    logger.error("coordinate should not be null");
    return null;
    }
    if (coordinateType != null ) {
    //GCJ-02 to WGS-84
    coordinate = CoordinateUtils.gcj_decrypt(coordinate.getLat(), coordinate.getLon());
    }

    return queryGlobalDistrict(coordinate);
    }

    private GlobalDistrict queryGlobalDistrict(CoordinatePoint coordinate) throws Exception {
    final Coordinate co = new Coordinate(coordinate.getLon(), coordinate.getLat());
    final List<GlobalDistrict> region = new ArrayList<>();
    worldStrTree.query(new Envelope(co), new ItemVisitor() {

    @Override
    public void visitItem(Object item) {
    IndexedPointInAreaLocator g = (IndexedPointInAreaLocator) item;
    int location = g.locate(co);
    if (location == 0 || location == 1) {
    region.add((GlobalDistrict) worldMap.get(g).getUserData());
    }
    }
    });

    if (region.isEmpty()) {
    return null;
    }
    if (region.size() > 1) {
    logger.error("More than one region is found. Coordinate=" + coordinate + ", region=" + region);
    }
    return region.get(0);

    }

    private static void init() {
    logger.info("JVM max: " + Runtime.getRuntime().maxMemory() / 1024 / 1024 + " MB");
    try {
    // init world
    initWorld();
    } catch (Exception e) {
    logger.error("init error", e);
    System.exit(1);
    }
    }


    private static void initWorld() throws Exception {
    GeometryFactory f = new GeometryFactory();
    int count = 0;

    String file = "/home/admin/gadm36.geojson";

    //文件比较大,通过JSONReader加载
    JSONReader r;

    BufferedReader in = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream(new File(file))), "utf-8"), 5 * 1024 * 1024);
    r = new JSONReader(in);
    // r=new JSONReader(new FileReader(file));
    r.startObject();
    //解析gadm的文件格式
    while (r.hasNext()) {
    String key = r.readString();
    switch (key) {
    case "features":
    r.startArray();
    while (r.hasNext()) {
    GlobalDistrict gDistrict = new GlobalDistrict();
    Geometry polygon = null;

    r.startObject();
    while (r.hasNext()) {
    String itemKey = r.readString();
    switch (itemKey) {
    case "properties":
    JSONObject properties = (JSONObject) r.readObject();

    int objectId = properties.getInteger("UID");
    String iso = properties.getString("GID_0");
    String code = "-1";
    gDistrict.setId(objectId);
    gDistrict.setIso(iso);
    gDistrict.setCode(code);
    String[] upLevelNames = null;
    for (int nameIndex = 5; nameIndex >= 0; nameIndex --) {
    if (properties.getString("NAME_" + nameIndex) != null) {
    gDistrict.setLevel(nameIndex);
    gDistrict.setName(properties.getString("NAME_" + nameIndex));
    upLevelNames = new String[nameIndex];
    break;
    }
    if (nameIndex == 0) {
    Utils.throwException(ExceptionCode.INTERNAL_ERROR, "All of NAME_x fields are null");
    }
    }
    for (int level = 0; level < upLevelNames.length; level ++) {
    upLevelNames[level] = properties.getString("NAME_" + level);
    }
    gDistrict.setUpLevelNames(upLevelNames);

    break;
    case "geometry":
    r.startObject();
    String type = "";
    while (r.hasNext()) {
    String geometryKey = r.readString();
    switch (geometryKey) {
    case "type":
    type = r.readString();
    break;
    case "coordinates":
    // first list, only 1 element
    r.startArray();
    if (type.equals("Polygon")) {
    boolean hasHoles = false;
    List<LinearRing> holes = new ArrayList<>();
    LinearRing outerRing = null;
    while (r.hasNext()) {
    List<Coordinate> coordinates = new ArrayList<Coordinate>();
    r.startArray();
    while (r.hasNext()) {
    // coordinate
    Coordinate c = new Coordinate();
    int i = 0;
    r.startArray();
    while (r.hasNext()) {
    c.setOrdinate(i, Double.parseDouble(r.readString()));
    i++;
    }
    r.endArray();
    coordinates.add(c);
    }
    r.endArray();
    if (!hasHoles) {
    outerRing = f.createLinearRing(coordinates.toArray(new Coordinate[] {}));
    } else {
    holes.add(f.createLinearRing(coordinates.toArray(new Coordinate[] {})));
    }
    hasHoles = true;
    }
    if (holes.size() == 0) {
    polygon = f.createPolygon(outerRing);
    } else {
    polygon = f.createPolygon(outerRing, holes.toArray(new LinearRing[] {}));
    }
    } else if (type.equals("MultiPolygon")) {
    List<Polygon> polygons = new ArrayList<>();
    while (r.hasNext()) {
    r.startArray();
    boolean hasHoles = false;
    List<LinearRing> holes = new ArrayList<>();
    LinearRing outerRing = null;
    while (r.hasNext()) {
    List<Coordinate> coordinates = new ArrayList<Coordinate>();
    r.startArray();
    while (r.hasNext()) {
    // coordinate
    Coordinate c = new Coordinate();
    int i = 0;
    r.startArray();
    while (r.hasNext()) {
    c.setOrdinate(i, Double.parseDouble(r.readString()));
    i++;
    }
    r.endArray();
    coordinates.add(c);
    }
    r.endArray();
    if (!hasHoles) {
    outerRing = f.createLinearRing(coordinates.toArray(new Coordinate[] {}));
    } else {
    holes.add(f.createLinearRing(coordinates.toArray(new Coordinate[] {})));
    }
    hasHoles = true;
    }
    if (holes.size() == 0) {
    polygons.add(f.createPolygon(outerRing));
    } else {
    polygons.add(f.createPolygon(outerRing, holes.toArray(new LinearRing[] {})));
    }
    r.endArray();
    }
    polygon = f.createMultiPolygon(polygons.toArray(new Polygon[] {}));
    } else {
    Utils.throwException(ExceptionCode.INTERNAL_ERROR, "unknown polygon type: " + type);
    }
    r.endArray();
    break;
    default:
    System.out.println(r.readObject().toString());
    break;
    }
    }
    r.endObject();
    break;
    default:
    r.readObject();
    break;
    }
    }
    r.endObject();

    polygon.setUserData(gDistrict);
    IndexedPointInAreaLocator locator = new IndexedPointInAreaLocator(polygon);
    worldMap.put(locator, polygon);
    worldStrTree.insert(polygon.getEnvelopeInternal(), locator);

    count ++;
    if (count % 10000 == 0) {
    logger.info(count + " --- " + Runtime.getRuntime().totalMemory() / 1024 / 1024 + " MB");
    }
    }
    r.endArray();
    break;
    default:
    r.readObject();
    break;
    }
    }
    r.endObject();
    r=null;
    logger.info("World polygon size: " + worldMap.size());

    // query a point to the strTree for trigger it
    final Coordinate co = new Coordinate(12.4, 51.2);
    worldStrTree.query(new Envelope(co), new ItemVisitor() {

    @Override
    public void visitItem(Object item) {
    IndexedPointInAreaLocator g = (IndexedPointInAreaLocator) item;
    g.locate(co);
    }
    });
    logger.info("World strTree is triggered");
    }

    }
    ```

    ```java
    /**
    * 坐标系转换
    */
    public class CoordinateUtils {
    private static final double PI = 3.14159265358979324d;
    private static final double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    private static CoordinatePoint delta(double lat, double lon) {
    // Krasovsky 1940
    //
    // a = 6378245.0, 1/f = 298.3
    // b = a * (1 - f)
    // ee = (a^2 - b^2) / a^2;
    double a = 6378245.0; //
    double ee = 0.00669342162296594323; //
    double dLat = transformLat(lon - 105.0, lat - 35.0);
    double dLon = transformLon(lon - 105.0, lat - 35.0);
    double radLat = lat / 180.0 * PI;
    double magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = Math.sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
    dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
    CoordinatePoint point = new CoordinatePoint();
    point.setLat(dLat);
    point.setLon(dLon);
    return point;
    }

    //WGS-84 to GCJ-02
    public static CoordinatePoint gcj_encrypt(double wgsLat, double wgsLon) {
    CoordinatePoint point = new CoordinatePoint();
    if (outOfChina(wgsLat, wgsLon)) {
    point.setLat(wgsLat);
    point.setLon(wgsLon);
    return point;
    }

    CoordinatePoint d = delta(wgsLat, wgsLon);
    point.setLat(wgsLat + d.getLat());
    point.setLon(wgsLon + d.getLon());
    return point;
    }

    //GCJ-02 to WGS-84
    public static CoordinatePoint gcj_decrypt(double gcjLat, double gcjLon) {
    CoordinatePoint point = new CoordinatePoint();
    if (outOfChina(gcjLat, gcjLon)) {
    point.setLat(gcjLat);
    point.setLon(gcjLon);
    return point;
    }

    CoordinatePoint d = delta(gcjLat, gcjLon);
    point.setLat(gcjLat - d.getLat());
    point.setLon(gcjLon - d.getLon());
    return point;
    }

    //GCJ-02 to WGS-84 exactly
    public static CoordinatePoint gcj_decrypt_exact(double gcjLat, double gcjLon) {
    double initDelta = 0.01;
    double threshold = 0.000000001;
    double dLat = initDelta, dLon = initDelta;
    double mLat = gcjLat - dLat, mLon = gcjLon - dLon;
    double pLat = gcjLat + dLat, pLon = gcjLon + dLon;
    double wgsLat, wgsLon, i = 0;
    while (true) {
    wgsLat = (mLat + pLat) / 2;
    wgsLon = (mLon + pLon) / 2;
    CoordinatePoint tmp = gcj_encrypt(wgsLat, wgsLon);
    dLat = tmp.getLat() - gcjLat;
    dLon = tmp.getLon() - gcjLon;
    if ((Math.abs(dLat) < threshold) && (Math.abs(dLon) < threshold))
    break;

    if (dLat > 0)
    pLat = wgsLat;
    else
    mLat = wgsLat;
    if (dLon > 0)
    pLon = wgsLon;
    else
    mLon = wgsLon;

    if (++i > 10000)
    break;
    }
    //console.log(i);
    CoordinatePoint point = new CoordinatePoint();
    point.setLat(wgsLat);
    point.setLon(wgsLon);
    return point;
    }

    //GCJ-02 to BD-09
    public CoordinatePoint bd_encrypt(double gcjLat, double gcjLon) {
    double x = gcjLon, y = gcjLat;
    double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * this.x_pi);
    double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * this.x_pi);
    double bdLon = z * Math.cos(theta) + 0.0065;
    double bdLat = z * Math.sin(theta) + 0.006;
    CoordinatePoint point = new CoordinatePoint();
    point.setLat(bdLat);
    point.setLon(bdLon);
    return point;
    }

    //BD-09 to GCJ-02
    public static CoordinatePoint bd_decrypt(double bdLat, double bdLon) {
    double x = bdLon - 0.0065, y = bdLat - 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 gcjLon = z * Math.cos(theta);
    double gcjLat = z * Math.sin(theta);
    CoordinatePoint point = new CoordinatePoint();
    point.setLat(gcjLat);
    point.setLon(gcjLon);
    return point;
    }

    private static boolean outOfChina(double lat, double lon) {
    if (lon < 72.004 || lon > 137.8347)
    return true;
    if (lat < 0.8293 || lat > 55.8271)
    return true;
    return false;
    }

    private static double transformLat(double x, double y) {
    double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0;
    return ret;
    }

    private static double transformLon(double x, double y) {
    double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0;
    return ret;
    }

    public static Coordinate toJts(CoordinatePoint p) {
    Coordinate c = new Coordinate();
    c.setOrdinate(0, p.getLon());
    c.setOrdinate(1, p.getLat());
    return c;
    }

    }
    ```

    ```java
    /**
    * 坐标点
    */
    public static class CoordinatePoint {
    private double lon;
    private double lat;

    public CoordinatePoint() {
    }

    public CoordinatePoint(double lon, double lat) {
    this.lon = lon;
    this.lat = lat;
    }

    public double getLon() {
    return this.lon;
    }

    public void setLon(double lon) {
    this.lon = lon;
    }

    public double getLat() {
    return this.lat;
    }

    public void setLat(double lat) {
    this.lat = lat;
    }

    @Override
    public String toString() {
    return "(" + this.lon + ", " + this.lat + ")";
    }

    public static <T> T fromJson(String json, Class<T> clazz) {
    return JSON.parseObject(json, clazz);
    }

    public String toJson() {
    return JSON.toJSONString(this);
    }

    }
    ```

  • 相关阅读:
    JS事件冒泡
    iis设置Gzip后,无后缀的url无法压缩解决 MVC iis GZIP
    SQL中的循环、for循环、游标
    采用CDN加速后,如何在程序里获取用户IP地址
    Resharper 的快捷键
    JS防后退跳转
    ASP.NET MVC如何实现自定义验证(服务端验证+客户端验证)
    Log4net创建日志及简单扩展
    统治世界的十大算法
    ffmpeg save rtsp stream
  • 原文地址:https://www.cnblogs.com/ZhengQiZHou/p/12660730.html
Copyright © 2020-2023  润新知