| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package com.zksy.cockpit.geometry;
- import com.zksy.base.domain.GasPipePoint;
- import com.zksy.base.domain.ManholeBase;
- import com.zksy.base.domain.PipeNetworkBase;
- import com.zksy.base.service.GasPipePointService;
- import com.zksy.base.service.ManholeBaseService;
- import com.zksy.base.service.PipeNetworkBaseService;
- import com.zksy.cockpit.foundation.CockpitCoordinateValidation;
- import com.zksy.cockpit.foundation.CockpitCoordinateValidator;
- import com.zksy.cockpit.foundation.CockpitPipeNetworkType;
- import com.zksy.cockpit.foundation.CockpitReadException;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Coverage;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Feature;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Geometry;
- import com.zksy.cockpit.geometry.CockpitFacilityGeometryResponse.Properties;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
- import java.math.BigDecimal;
- import java.time.Clock;
- import java.time.ZoneId;
- import java.time.format.DateTimeFormatter;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.List;
- import java.util.Objects;
- import java.util.stream.Collectors;
- @Service
- public class CockpitFacilityGeometryService {
- private final PipeNetworkBaseService networks;
- private final GasPipePointService gasPoints;
- private final ManholeBaseService manholes;
- private final Clock clock;
- public CockpitFacilityGeometryService(PipeNetworkBaseService networks, GasPipePointService gasPoints,
- ManholeBaseService manholes, Clock clock) {
- this.networks = networks;
- this.gasPoints = gasPoints;
- this.manholes = manholes;
- this.clock = clock;
- }
- @Transactional(readOnly = true)
- public CockpitFacilityGeometryResponse load() {
- try {
- List<Feature> features = new ArrayList<>();
- // IService.list() honours each entity's @TableLogic (del_flag=0).
- for (PipeNetworkBase network : Objects.requireNonNull(networks.list())) {
- String industry = CockpitPipeNetworkType.industryOf(network.getNetworkType());
- features.add(feature("pipe_network_base", network.getNetworkId(), network.getNetworkCode(),
- network.getNetworkName(), industry == null ? "UNKNOWN" : industry,
- CockpitPipeNetworkType.typeCodeOf(network.getNetworkType()), network.getNetworkType(),
- null, network.getNetworkId(), "LineString", Arrays.asList(
- pair(network.getStartLongitude(), network.getStartLatitude()),
- pair(network.getEndLongitude(), network.getEndLatitude()))));
- }
- for (GasPipePoint point : Objects.requireNonNull(gasPoints.list())) {
- features.add(feature("gas_pipe_point", String.valueOf(point.getPointId()), point.getPointCode(),
- point.getPointName(), "GAS", "GAS_PIPE_POINT", null, point.getPointType(),
- point.getNetworkId(), "Point",
- Collections.singletonList(pair(point.getLongitude(), point.getLatitude()))));
- }
- for (ManholeBase manhole : Objects.requireNonNull(manholes.list())) {
- features.add(feature("manhole_base", manhole.getManholeId(), manhole.getManholeCode(),
- manhole.getManholeName(), "MANHOLE", "MANHOLE", null, null,
- manhole.getNetworkId(), "Point",
- Collections.singletonList(pair(manhole.getLongitude(), manhole.getLatitude()))));
- }
- List<Coverage> coverage = new ArrayList<>();
- for (String industry : Arrays.asList("GAS", "WATER_SUPPLY", "DRAINAGE")) {
- coverage.add(coverage(features, industry, "pipe_network_base", "LineString", "TWO_ENDPOINTS_ONLY"));
- }
- coverage.add(coverage(features, "GAS", "gas_pipe_point", "Point", null));
- coverage.add(new Coverage("WATER_SUPPLY", null, "Point", "UNAVAILABLE",
- "NO_COMPONENT_SOURCE", 0, 0, 0));
- coverage.add(new Coverage("DRAINAGE", null, "Point", "UNAVAILABLE",
- "NO_TYPED_COMPONENT_SOURCE", 0, 0, 0));
- coverage.add(coverage(features, "MANHOLE", "manhole_base", "Point", "NO_COMPONENT_TYPE"));
- coverage.add(new Coverage("MANHOLE", null, "LineString", "UNAVAILABLE",
- "NO_PIPELINE_SOURCE", 0, 0, 0));
- coverage.add(coverage(features, "UNKNOWN", "pipe_network_base", "LineString", "UNKNOWN_NETWORK_TYPE"));
- String generatedAt = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(
- clock.instant().atZone(ZoneId.of("Asia/Shanghai")));
- return new CockpitFacilityGeometryResponse(generatedAt, features, coverage);
- } catch (RuntimeException exception) {
- // Never turn a failed source into an empty/partial successful collection.
- org.slf4j.LoggerFactory.getLogger(CockpitFacilityGeometryService.class)
- .error("Facility geometry source read failed", exception);
- throw new CockpitReadException("facility geometry source unavailable");
- }
- }
- private static Feature feature(String source, String sourceId, String code, String name, String industry,
- String typeCode, String networkType, String pointType, String networkId, String geometryType,
- List<List<BigDecimal>> coordinates) {
- List<CockpitCoordinateValidation> validations = coordinates.stream()
- .map(pair -> validate(pair.get(0), pair.get(1)))
- .collect(Collectors.toList());
- CockpitCoordinateValidation overall = validations.stream()
- .filter(validation -> "INVALID".equals(validation.getState()))
- .findFirst().orElse(validations.get(0));
- // Invalid GeoJSON coordinates cannot be represented as an empty or invented geometry.
- Geometry geometry = "INVALID".equals(overall.getState()) ? null : new Geometry(geometryType,
- "Point".equals(geometryType) ? coordinates.get(0) : coordinates);
- Properties properties = new Properties(source, sourceId, code, name, industry, typeCode, networkType,
- pointType, networkId, coordinates, overall, validations);
- return new Feature(source + ":" + sourceId, geometry, properties);
- }
- private static CockpitCoordinateValidation validate(BigDecimal longitude, BigDecimal latitude) {
- // Structural errors must remain distinguishable even without a verification flag.
- CockpitCoordinateValidation structural = CockpitCoordinateValidator.validate(longitude, latitude, true);
- return structural.isRenderable()
- ? CockpitCoordinateValidator.validate(longitude, latitude, false) : structural;
- }
- private static List<BigDecimal> pair(BigDecimal longitude, BigDecimal latitude) {
- return Arrays.asList(longitude, latitude);
- }
- private static Coverage coverage(List<Feature> features, String industry, String source,
- String geometryType, String reason) {
- List<Feature> records = features.stream().filter(feature ->
- industry.equals(feature.getProperties().getIndustry())
- && source.equals(feature.getProperties().getSource())).collect(Collectors.toList());
- int geometryCount = (int) records.stream().filter(feature -> feature.getGeometry() != null).count();
- int renderableCount = (int) records.stream()
- .filter(feature -> feature.getProperties().getCoordinateValidation().isRenderable()).count();
- return new Coverage(industry, source, geometryType, records.isEmpty() ? "EMPTY" : "AVAILABLE",
- reason, records.size(), geometryCount, renderableCount);
- }
- }
|