|
|
@@ -0,0 +1,168 @@
|
|
|
+package com.siwei.apply.utils;
|
|
|
+
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import java.net.URI;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.net.http.HttpClient;
|
|
|
+import java.net.http.HttpRequest;
|
|
|
+import java.net.http.HttpResponse;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.sql.*;
|
|
|
+import java.time.Duration;
|
|
|
+import java.util.*;
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * arcgis rest api 工具类,支持分页查询 features、转换 rings 为 PostGIS EWKT。
|
|
|
+ */
|
|
|
+public class ArcGisUtil {
|
|
|
+
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(ArcGisUtil.class);
|
|
|
+ private static final int PAGE_SIZE = 1000;
|
|
|
+
|
|
|
+ private final ObjectMapper mapper = new ObjectMapper();
|
|
|
+
|
|
|
+ private final String base;
|
|
|
+ private final int layerId;
|
|
|
+ private final int srid;
|
|
|
+ private final HttpClient http;
|
|
|
+
|
|
|
+ public ArcGisUtil(String base, int layerId, int srid) {
|
|
|
+ this.base = base.endsWith("/") ? base.substring(0, base.length() - 1) : base;
|
|
|
+ this.layerId = layerId;
|
|
|
+ this.srid = srid;
|
|
|
+ this.http = HttpClient.newBuilder()
|
|
|
+ .connectTimeout(Duration.ofSeconds(15))
|
|
|
+ .version(HttpClient.Version.HTTP_1_1)
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 拉取一页 features
|
|
|
+ *
|
|
|
+ * @param where ArcGIS SQL92 过滤条件;查全部传 "1=1"
|
|
|
+ * @param returnGeometry true=带 geometry;只要属性传 false
|
|
|
+ * @param offset 分页起点 (resultOffset)
|
|
|
+ * @param count 分页大小 (resultRecordCount),不超过服务 maxRecordCount
|
|
|
+ * @return 完整 query 响应(features + exceededTransferLimit)
|
|
|
+ */
|
|
|
+ public JsonNode query(String where, boolean returnGeometry, int offset, int count) throws Exception {
|
|
|
+ String qs = "where=" + encode(where)
|
|
|
+ + "&outFields=*"
|
|
|
+ + "&returnGeometry=" + returnGeometry
|
|
|
+ + "&outSR=" + srid
|
|
|
+ + "&resultOffset=" + offset
|
|
|
+ + "&resultRecordCount=" + count
|
|
|
+ + "&orderByFields=OBJECTID+ASC"
|
|
|
+ + "&f=json";
|
|
|
+
|
|
|
+ HttpRequest req = HttpRequest.newBuilder(URI.create(base + "/" + layerId + "/query?" + qs))
|
|
|
+ .header("Accept", "application/json")
|
|
|
+ .timeout(Duration.ofSeconds(120)).GET().build();
|
|
|
+
|
|
|
+ HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
|
|
+ if (resp.statusCode() != 200) {
|
|
|
+ String body = resp.body();
|
|
|
+ throw new RuntimeException("ArcGIS HTTP " + resp.statusCode()
|
|
|
+ + " : " + body.substring(0, Math.min(400, body.length())));
|
|
|
+ }
|
|
|
+ JsonNode root = mapper.readTree(resp.body());
|
|
|
+ if (root.has("error") && !root.get("error").isNull()) {
|
|
|
+ throw new RuntimeException("ArcGIS error: " + root.get("error"));
|
|
|
+ }
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ArcGIS rings 转换为 EWKT (PostGIS 扩展)。
|
|
|
+ *
|
|
|
+ * <p>ArcGIS rings 结构 {@code [[[lng,lat],...],...]},仅取第一个环并显式闭合。
|
|
|
+ * 通过 JTS WKTReader 重解析几何对象,强制按右手规则确定外环,确保 PostGIS 接受。</p>
|
|
|
+ *
|
|
|
+ * <p>本实现只处理最常见的「单外环、无洞」宗地。带洞或多面的图斑需要在 GIS 工具
|
|
|
+ * 中合并或重绘后再入 ArcGIS 层。</p>
|
|
|
+ *
|
|
|
+ * @return 形如 {@code SRID=4490;POLYGON((x y, x y, ..., x y))},入参为空时返回 null
|
|
|
+ */
|
|
|
+ public String ringsToEwkt(JsonNode rings) throws Exception {
|
|
|
+ if (rings == null || !rings.isArray() || rings.isEmpty()) return null;
|
|
|
+ JsonNode firstRing = rings.get(0);
|
|
|
+ if (!firstRing.isArray() || firstRing.isEmpty()) return null;
|
|
|
+
|
|
|
+ StringBuilder pts = new StringBuilder();
|
|
|
+ for (int i = 0; i < firstRing.size(); i++) {
|
|
|
+ JsonNode pt = firstRing.get(i);
|
|
|
+ double x = pt.get(0).asDouble();
|
|
|
+ double y = pt.get(1).asDouble();
|
|
|
+ if (i > 0) pts.append(", ");
|
|
|
+ pts.append(String.format("%.6f %.6f", x, y));
|
|
|
+ }
|
|
|
+ // 显式闭合:首末相同,便于 WKTReader 严格解析
|
|
|
+ JsonNode head = firstRing.get(0);
|
|
|
+ pts.append(", ").append(String.format("%.6f %.6f", head.get(0).asDouble(), head.get(1).asDouble()));
|
|
|
+
|
|
|
+ String wkt = "POLYGON((" + pts + "))";
|
|
|
+ org.locationtech.jts.geom.Geometry jts =
|
|
|
+ new org.locationtech.jts.io.WKTReader().read(wkt);
|
|
|
+ return "SRID=" + srid + ";" + jts.toText();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 只取要素总数(用于同步前估算总量)。
|
|
|
+ */
|
|
|
+ public long count(String where) throws Exception {
|
|
|
+ String qs = "where=" + encode(where)
|
|
|
+ + "&returnCountOnly=true"
|
|
|
+ + "&f=json";
|
|
|
+ HttpRequest req = HttpRequest.newBuilder(URI.create(base + "/" + layerId + "/query?" + qs))
|
|
|
+ .timeout(Duration.ofSeconds(60)).GET().build();
|
|
|
+ HttpResponse<String> resp = http.send(req, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8));
|
|
|
+ if (resp.statusCode() != 200) {
|
|
|
+ throw new RuntimeException("ArcGIS HTTP " + resp.statusCode());
|
|
|
+ }
|
|
|
+ JsonNode root = mapper.readTree(resp.body());
|
|
|
+ return root.path("count").asLong(-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String encode(String s) {
|
|
|
+ return URLEncoder.encode(s, StandardCharsets.UTF_8);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ *
|
|
|
+ * @param args
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ public static void main(String[] args) throws Exception {
|
|
|
+ String arcgisBase = "http://192.168.100.1:6080/arcgis/rest/services/GJXQ/BDC_DBZD/MapServer";
|
|
|
+ int layerId = 0;
|
|
|
+ int srid = 4490; // CGCS2000 经纬度;如取 4527(3度带)同步改这里
|
|
|
+ ArcGisUtil client = new ArcGisUtil(arcgisBase, layerId, srid);
|
|
|
+ boolean withGeometry = true;
|
|
|
+ //String where = "BDCDYH='360425005005GN00002W00000000'";
|
|
|
+ String where = "BDCDYH='360425005005GB60012W00000000'";
|
|
|
+ JsonNode page = client.query(where, withGeometry, 0, PAGE_SIZE);
|
|
|
+ JsonNode features = page.path("features");
|
|
|
+ //int n = features.size();
|
|
|
+ JsonNode f = features.get(0);
|
|
|
+ JsonNode attrs = f.path("attributes");
|
|
|
+ String ewkt = null;
|
|
|
+ if (withGeometry) {
|
|
|
+ JsonNode rings = f.path("geometry").path("rings");
|
|
|
+ ewkt = client.ringsToEwkt(rings);
|
|
|
+ log.info("ewkt OK: {} ", ewkt);
|
|
|
+ }
|
|
|
+ log.info("ArcGIS OK: {} (layer={}, srid={})", arcgisBase, layerId, srid);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|