| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- 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);
- public static final int PAGE_SIZE = 1000;
- public static final int LAYER_ID = 0;
- public static final int SRID = 4490;
- public static final boolean WITH_GEOMETRY = true;
- public static final String ARCGIS_BASE_URL = "http://192.168.100.1:6080/arcgis/rest/services/GJXQ/BDC_DBZD/MapServer";
- 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()));
- // ========= 修改1:外层改为 MULTIPOLYGON 包裹 =========
- String wkt = "MULTIPOLYGON(((" + pts + ")))";
- org.locationtech.jts.geom.Geometry jts = new org.locationtech.jts.io.WKTReader().read(wkt);
- // ========= 修改2:固定SRID=4490 =========
- 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'";
- //String where = "BDCDYH='360122014004GB00091F00040022'";
- String where = "BDCDYH='360122011007GB00322W00000000'";
- JsonNode page = client.query(where, withGeometry, 0, PAGE_SIZE);
- JsonNode features = page.path("features");
- //int n = features.size();
- JsonNode f = features.get(0);
- log.info("result OK: {} ", f);
- 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);
- }
- }
|