工具类

创建日期:2024-06-21
更新日期:2025-01-20

{{{ArcGISUtils.java

}}}

package com.hylab.websiteforgis.utils;

import com.esri.arcgisruntime.ArcGISRuntimeException;
import com.esri.arcgisruntime.arcgisservices.ArcGISFeatureServiceInfo;
import com.esri.arcgisruntime.arcgisservices.IdInfo;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.*;
import com.esri.arcgisruntime.geometry.*;
import com.esri.arcgisruntime.loadable.LoadStatus;
import com.esri.arcgisruntime.security.Credential;
import com.esri.arcgisruntime.security.UserCredential;
import com.esri.arcgisruntime.utilitynetworks.*;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hylab.websiteforgis.model.R;
import com.hylab.websiteforgis.utils.trace.ResultElement;
import com.hylab.websiteforgis.utils.trace.TraceLocation;
import com.hylab.websiteforgis.utils.trace.TraceType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * ArcGIS工具类
 *
 * @author liteng
 * @since 2022-11-01
 */
public class ArcGISUtils {
    private final static SpatialReference spatialReference = SpatialReference.create(3857);
    private final static ObjectMapper mapper = new ObjectMapper();
    private final static Logger logger = LoggerFactory.getLogger(ArcGISUtils.class);

    /**
     * 获取Geodatabase
     *
     * @param serviceUrl 服务地址
     * @param timeout    超时时间
     * @return 加载结果
     */
    public static R<ServiceGeodatabase> getGeodatabase(String serviceUrl, String username, String password, long timeout) {
        ServiceGeodatabase db = new ServiceGeodatabase(serviceUrl);

        Credential credential = new UserCredential(username, password);
        db.setCredential(credential);

        db.loadAsync();

        CountDownLatch countDownLatch = new CountDownLatch(1);
        R<ServiceGeodatabase> result = new R<>();

        db.addDoneLoadingListener(() -> {
            if (db.getLoadStatus() == LoadStatus.LOADED) {
                result.setOk(db);
            } else {
                ArcGISRuntimeException exception = db.getLoadError();
                exception.printStackTrace();
                result.setFail(exception.getMessage());
            }
            countDownLatch.countDown();
        });

        try {
            if (!countDownLatch.await(timeout, TimeUnit.SECONDS)) {
                result.setFail("请求服务超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            result.setFail(e.getMessage());
        }

        return R.ok(db);
    }

    /**
     * 根据图层名称获取图层
     *
     * @param db    服务数据库
     * @param names 图层名称
     * @return 结果
     */
    public static List<IdInfo> getLayerByNames(ServiceGeodatabase db, List<String> names) {
        ArcGISFeatureServiceInfo info = db.getServiceInfo();
        List<IdInfo> layers = info.getLayerInfos();

        List<IdInfo> infos = new ArrayList<>();

        names.forEach(name -> {
            Optional<IdInfo> layerVal = layers.stream().filter(n -> n.getName().equals(name)).findFirst();
            if (layerVal.isPresent()) {
                infos.add(layerVal.get());
            } else {
                logger.warn("图层{}不存在", name);
            }
        });

        return infos;
    }

    /**
     * 获取图层数据表
     *
     * @param db      数据库
     * @param layerId 图层id
     * @param timeout 超时时间
     * @return 结果
     */
    public static R<FeatureTable> getFeatureTable(ServiceGeodatabase db, long layerId, long timeout) {
        FeatureTable table = db.getTable(layerId);
        table.loadAsync();

        CountDownLatch countDownLatch = new CountDownLatch(1);
        R<FeatureTable> result = new R<>();

        table.addDoneLoadingListener(() -> {
            if (table.getLoadStatus() == LoadStatus.LOADED) {
                result.setOk(table);
            } else {
                ArcGISRuntimeException exception = table.getLoadError();
                exception.printStackTrace();
                result.setFail(exception.getMessage());
            }
            countDownLatch.countDown();
        });

        try {
            if (!countDownLatch.await(timeout, TimeUnit.SECONDS)) {
                result.setFail("请求服务超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            result.setFail(e.getMessage());
        }

        return result;
    }

    /**
     * 获取相交要素
     *
     * @param table    数据表
     * @param geometry 几何
     * @param timeout  超时时间
     * @return 结果
     */
    public static R<List<Feature>> getIntersectFeatures(FeatureTable table, Geometry geometry, long timeout) {
        QueryParameters query = new QueryParameters();
        query.setSpatialRelationship(QueryParameters.SpatialRelationship.INTERSECTS);
        query.setGeometry(geometry);
        query.setMaxFeatures(100000);
        query.setOutSpatialReference(spatialReference);
        query.setReturnGeometry(true);

        R<List<Feature>> result = new R<>();

        ListenableFuture<FeatureQueryResult> featuresAsync = ((ServiceFeatureTable) table).queryFeaturesAsync(query, ServiceFeatureTable.QueryFeatureFields.LOAD_ALL);
        try {
            List<Feature> features = new ArrayList<>();
            featuresAsync.get(timeout, TimeUnit.SECONDS).forEach(feature -> {
                features.add(feature);
            });
            result.setOk(features);
        } catch (Exception e) {
            e.printStackTrace();
            result.setFail(e.getMessage());
        }

        return result;
    }

    /**
     * 获取某个位置附近要素
     *
     * @param table   数据表
     * @param x       横坐标
     * @param y       纵坐标
     * @param timeout 超时时间
     * @return 附近要素
     */
    public static R<List<Feature>> getNearFeatures(FeatureTable table, double x, double y, long timeout) {
        double offset = 0.1;
        Envelope envelope = new Envelope(x - offset, y - offset, x + offset, y + offset, spatialReference);
        return getIntersectFeatures(table, envelope, timeout);
    }

    /**
     * 获取某个表所有要素
     *
     * @param table   数据表
     * @param timeout 超时时间
     * @return 附近要素
     */
    public static R<List<Feature>> getAllFeatures(FeatureTable table, long timeout) {
        Envelope envelope = new Envelope(-180, -90, 180, 90, SpatialReference.create(4326));
        return getIntersectFeatures(table, envelope, timeout);
    }

    /**
     * 获取公共设施网络
     *
     * @param serviceUrl 服务地址
     * @param timeout    超时时间
     * @return 加载结果
     */
    public static R<UtilityNetwork> getUtilityNetwork(String serviceUrl, long timeout) {
        UtilityNetwork utilityNetwork = new UtilityNetwork(serviceUrl);
        utilityNetwork.loadAsync();

        CountDownLatch countDownLatch = new CountDownLatch(1);
        R<UtilityNetwork> result = new R<>();

        utilityNetwork.addDoneLoadingListener(() -> {
            if (utilityNetwork.getLoadStatus() == LoadStatus.LOADED) {
                result.setOk(utilityNetwork);
            } else {
                ArcGISRuntimeException exception = utilityNetwork.getLoadError();
                exception.printStackTrace();
                result.setFail(exception.getMessage());
            }
            countDownLatch.countDown();
        });

        try {
            if (!countDownLatch.await(timeout, TimeUnit.SECONDS)) {
                result.setFail("请求服务超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            result.setFail(e.getMessage());
        }

        return result;
    }

    /**
     * 获取路径分析结果
     *
     * @param utilityNetwork 公共设施网络
     * @param timeout        超时时间
     * @return 路径要素
     */
    public static R<List<ArcGISFeature>> getTraceFeatures(UtilityNetwork utilityNetwork, long timeout) {
        // get a trace configuration from a tier
        UtilityNetworkDefinition networkDefinition = utilityNetwork.getDefinition();
        UtilityDomainNetwork domainNetwork = networkDefinition.getDomainNetwork("Pipeline");
        UtilityTier tier = domainNetwork.getTier("Pipe Distribution System");
        UtilityTraceConfiguration traceConfiguration = tier.getTraceConfiguration();

        // create a trace filter
        traceConfiguration.setFilter(new UtilityTraceFilter());

        // get a default starting location
        UtilityNetworkSource networkSource = networkDefinition.getNetworkSource("Gas Device");
        UtilityAssetGroup assetGroup = networkSource.getAssetGroup("Meter");
        UtilityAssetType assetType = assetGroup.getAssetType("Customer");
        UtilityElement startingLocation = utilityNetwork.createElement(assetType, UUID.fromString("98A06E95-70BE-43E7-91B7-E34C9D3CB9FF"));
        // create new base trace parameters
        UtilityTraceParameters utilityTraceParameters = new UtilityTraceParameters(UtilityTraceType.CONNECTED, Collections.singleton(startingLocation));

        // get the first feature for the starting location, and get its geometry
        ListenableFuture<List<ArcGISFeature>> elementFeaturesFuture = utilityNetwork.fetchFeaturesForElementsAsync(Collections.singletonList(startingLocation));

        CountDownLatch countDownLatch = new CountDownLatch(1);
        R<List<ArcGISFeature>> result = new R<>();

        elementFeaturesFuture.addDoneListener(() -> {
            try {
                List<ArcGISFeature> startingLocationFeatures = elementFeaturesFuture.get();

                List<ArcGISFeature> features = new ArrayList<>(elementFeaturesFuture.get());
                result.setOk(features);
                countDownLatch.countDown();
            } catch (Exception e) {
                e.printStackTrace();
                result.setFail(e.getMessage());
                countDownLatch.countDown();
            }
        });

        try {
            if (!countDownLatch.await(timeout, TimeUnit.SECONDS)) {
                result.setFail("请求服务超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            result.setFail(e.getMessage());
        }

        return result;
    }

    /**
     * 计算两点距离
     *
     * @param x1               第一个点横坐标
     * @param y1               第一个点纵坐标
     * @param x2               第二个点横坐标
     * @param y2               第二个点纵坐标
     * @param spatialReference 空间坐标系
     * @return 距离
     */
    public static double distance(double x1, double y1, double x2, double y2, SpatialReference spatialReference) {
        Point point1 = new Point(x1, y1, spatialReference);
        Point point2 = new Point(x2, y2, spatialReference);
        PointCollection points = new PointCollection(spatialReference);
        points.add(point1);
        points.add(point2);
        return GeometryEngine.length(new Polyline(points));
    }

    /**
     * 追踪
     *
     * @param traceServer    追踪网络服务器
     * @param traceType      追踪类型
     * @param traceLocations 追踪位置
     * @return 分析结果
     */
    public static R<List<ResultElement>> trace(String traceServer, TraceType traceType, List<TraceLocation> traceLocations) {
        String url = String.format("%s/trace", traceServer);

        Map<String, Object> params = new HashMap<>();
        params.put("gdbVersion", "");
        params.put("sessionId", "");
        params.put("moment", "");
        params.put("traceType", traceType);
        try {
            params.put("traceLocations", mapper.writeValueAsString(traceLocations));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        params.put("traceConfigurationGlobalId", "");
        params.put("traceConfiguration", "");
        params.put("resultTypes", "[{\"type\":\"elements\"}]");
        params.put("f", "json");

        String resultVal = HttpUtils.post(url, params, 60000, 60000, "utf-8");
        Map result = null;
        try {
            result = mapper.readValue(resultVal, Map.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            return R.fail(e.getMessage());
        }

        Boolean success = (Boolean) result.get("success");
        if (!success) {
            Map error = (Map) result.get("error");
            return R.fail((String) error.get("message"));
        }

        Map traceResults = (Map) result.get("traceResults");
        List elements = (List) traceResults.get("elements");

        List<ResultElement> list = new ArrayList<>();

        for (Object elementVal : elements) {
            Map element = (Map) elementVal;

            ResultElement item = new ResultElement();
            item.setNetworkSourceId((Integer) element.get("networkSourceId"));
            item.setGlobalId((String) element.get("globalId"));
            item.setObjectId((Integer) element.get("objectId"));
            list.add(item);
        }

        return R.ok(list);
    }

    /**
     * 根据图层id获取所有数据globalId跟layerName对应关系
     *
     * @param db     数据库
     * @param layers 图层
     * @return globalId跟layerName对应关系
     */
    public static Map<String, String> getGlobalIdLayerNames(ServiceGeodatabase db, List<IdInfo> layers, long timeout) {
        Map<String, String> result = new HashMap<>();

        for (IdInfo layer : layers) {
            R<FeatureTable> tableVal = getFeatureTable(db, layer.getId(), timeout);
            if (!tableVal.isOk()) {
                logger.warn(tableVal.getMsg());
                continue;
            }
            FeatureTable table = tableVal.getData();

            R<List<Feature>> featuresVal = getAllFeatures(table, timeout);
            if (!featuresVal.isOk()) {
                logger.warn(featuresVal.getMsg());
                continue;
            }
            List<Feature> features = featuresVal.getData();

            for (Feature feature : features) {
                Map<String, Object> attributes = feature.getAttributes();
                String globalID = attributes.get("GlobalID").toString().toUpperCase();
                if (!result.containsKey(globalID)) {
                    result.put(globalID, layer.getName());
                }
            }
        }

        return result;
    }
}