1
0

ArcGisUtil.java 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package com.siwei.apply.utils;
  2. import com.fasterxml.jackson.databind.JsonNode;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import java.net.URI;
  7. import java.net.URLEncoder;
  8. import java.net.http.HttpClient;
  9. import java.net.http.HttpRequest;
  10. import java.net.http.HttpResponse;
  11. import java.nio.charset.StandardCharsets;
  12. import java.sql.*;
  13. import java.time.Duration;
  14. import java.util.*;
  15. /**
  16. * arcgis rest api 工具类,支持分页查询 features、转换 rings 为 PostGIS EWKT。
  17. */
  18. public class ArcGisUtil {
  19. private static final Logger log = LoggerFactory.getLogger(ArcGisUtil.class);
  20. public static final int PAGE_SIZE = 1000;
  21. public static final int LAYER_ID = 0;
  22. public static final int SRID = 4490;
  23. public static final boolean WITH_GEOMETRY = true;
  24. public static final String ARCGIS_BASE_URL = "http://192.168.100.1:6080/arcgis/rest/services/GJXQ/BDC_DBZD/MapServer";
  25. private final ObjectMapper mapper = new ObjectMapper();
  26. private final String base;
  27. private final int layerId;
  28. private final int srid;
  29. private final HttpClient http;
  30. public ArcGisUtil(String base, int layerId, int srid) {
  31. this.base = base.endsWith("/") ? base.substring(0, base.length() - 1) : base;
  32. this.layerId = layerId;
  33. this.srid = srid;
  34. this.http = HttpClient.newBuilder()
  35. .connectTimeout(Duration.ofSeconds(15))
  36. .version(HttpClient.Version.HTTP_1_1)
  37. .build();
  38. }
  39. /**
  40. * 拉取一页 features
  41. *
  42. * @param where ArcGIS SQL92 过滤条件;查全部传 "1=1"
  43. * @param returnGeometry true=带 geometry;只要属性传 false
  44. * @param offset 分页起点 (resultOffset)
  45. * @param count 分页大小 (resultRecordCount),不超过服务 maxRecordCount
  46. * @return 完整 query 响应(features + exceededTransferLimit)
  47. */
  48. public JsonNode query(String where, boolean returnGeometry, int offset, int count) throws Exception {
  49. String qs = "where=" + encode(where)
  50. + "&outFields=*"
  51. + "&returnGeometry=" + returnGeometry
  52. + "&outSR=" + srid
  53. + "&resultOffset=" + offset
  54. + "&resultRecordCount=" + count
  55. + "&orderByFields=OBJECTID+ASC"
  56. + "&f=json";
  57. HttpRequest req = HttpRequest.newBuilder(URI.create(base + "/" + layerId + "/query?" + qs))
  58. .header("Accept", "application/json")
  59. .timeout(Duration.ofSeconds(120)).GET().build();
  60. HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
  61. if (resp.statusCode() != 200) {
  62. String body = resp.body();
  63. throw new RuntimeException("ArcGIS HTTP " + resp.statusCode()
  64. + " : " + body.substring(0, Math.min(400, body.length())));
  65. }
  66. JsonNode root = mapper.readTree(resp.body());
  67. if (root.has("error") && !root.get("error").isNull()) {
  68. throw new RuntimeException("ArcGIS error: " + root.get("error"));
  69. }
  70. return root;
  71. }
  72. /**
  73. * ArcGIS rings 转换为 EWKT (PostGIS 扩展)。
  74. *
  75. * <p>ArcGIS rings 结构 {@code [[[lng,lat],...],...]},仅取第一个环并显式闭合。
  76. * 通过 JTS WKTReader 重解析几何对象,强制按右手规则确定外环,确保 PostGIS 接受。</p>
  77. *
  78. * <p>本实现只处理最常见的「单外环、无洞」宗地。带洞或多面的图斑需要在 GIS 工具
  79. * 中合并或重绘后再入 ArcGIS 层。</p>
  80. *
  81. * @return 形如 {@code SRID=4490;POLYGON((x y, x y, ..., x y))},入参为空时返回 null
  82. */
  83. public String ringsToEwkt(JsonNode rings) throws Exception {
  84. if (rings == null || !rings.isArray() || rings.isEmpty()) return null;
  85. JsonNode firstRing = rings.get(0);
  86. if (!firstRing.isArray() || firstRing.isEmpty()) return null;
  87. StringBuilder pts = new StringBuilder();
  88. for (int i = 0; i < firstRing.size(); i++) {
  89. JsonNode pt = firstRing.get(i);
  90. double x = pt.get(0).asDouble();
  91. double y = pt.get(1).asDouble();
  92. if (i > 0) pts.append(", ");
  93. pts.append(String.format("%.6f %.6f", x, y));
  94. }
  95. // 显式闭合:首末相同,便于 WKTReader 严格解析
  96. JsonNode head = firstRing.get(0);
  97. pts.append(", ").append(String.format("%.6f %.6f", head.get(0).asDouble(), head.get(1).asDouble()));
  98. // ========= 修改1:外层改为 MULTIPOLYGON 包裹 =========
  99. String wkt = "MULTIPOLYGON(((" + pts + ")))";
  100. org.locationtech.jts.geom.Geometry jts = new org.locationtech.jts.io.WKTReader().read(wkt);
  101. // ========= 修改2:固定SRID=4490 =========
  102. return "SRID=" + srid + ";" + jts.toText();
  103. }
  104. /**
  105. * 只取要素总数(用于同步前估算总量)。
  106. */
  107. public long count(String where) throws Exception {
  108. String qs = "where=" + encode(where)
  109. + "&returnCountOnly=true"
  110. + "&f=json";
  111. HttpRequest req = HttpRequest.newBuilder(URI.create(base + "/" + layerId + "/query?" + qs))
  112. .timeout(Duration.ofSeconds(60)).GET().build();
  113. HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
  114. if (resp.statusCode() != 200) {
  115. throw new RuntimeException("ArcGIS HTTP " + resp.statusCode());
  116. }
  117. JsonNode root = mapper.readTree(resp.body());
  118. return root.path("count").asLong(-1);
  119. }
  120. private static String encode(String s) {
  121. return URLEncoder.encode(s, StandardCharsets.UTF_8);
  122. }
  123. /**
  124. *
  125. * @param args
  126. * @throws Exception
  127. */
  128. public static void main(String[] args) throws Exception {
  129. String arcgisBase = "http://192.168.100.1:6080/arcgis/rest/services/GJXQ/BDC_DBZD/MapServer";
  130. int layerId = 0;
  131. int srid = 4490; // CGCS2000 经纬度;如取 4527(3度带)同步改这里
  132. ArcGisUtil client = new ArcGisUtil(arcgisBase, layerId, srid);
  133. boolean withGeometry = true;
  134. //String where = "BDCDYH='360425005005GN00002W00000000'";
  135. //String where = "BDCDYH='360425005005GB60012W00000000'";
  136. //String where = "BDCDYH='360122014004GB00091F00040022'";
  137. String where = "BDCDYH='360122011007GB00322W00000000'";
  138. JsonNode page = client.query(where, withGeometry, 0, PAGE_SIZE);
  139. JsonNode features = page.path("features");
  140. //int n = features.size();
  141. JsonNode f = features.get(0);
  142. log.info("result OK: {} ", f);
  143. JsonNode attrs = f.path("attributes");
  144. String ewkt = null;
  145. if (withGeometry) {
  146. JsonNode rings = f.path("geometry").path("rings");
  147. ewkt = client.ringsToEwkt(rings);
  148. log.info("ewkt OK: {} ", ewkt);
  149. }
  150. log.info("ArcGIS OK: {} (layer={}, srid={})", arcgisBase, layerId, srid);
  151. }
  152. }