package com.revolsys.geometry.graph.visitor; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.function.Predicate; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import com.revolsys.geometry.filter.LineEqualIgnoreDirectionFilter; import com.revolsys.geometry.graph.Edge; import com.revolsys.geometry.graph.Graph; import com.revolsys.geometry.graph.RecordGraph; import com.revolsys.geometry.graph.filter.EdgeObjectFilter; import com.revolsys.geometry.graph.filter.EdgeTypeNameFilter; import com.revolsys.geometry.model.LineString; import com.revolsys.record.Record; import com.revolsys.record.RecordLog; import com.revolsys.record.filter.RecordGeometryFilter; import com.revolsys.util.ObjectProcessor; import com.revolsys.util.count.LabelCountMap; import com.revolsys.visitor.AbstractVisitor; public class EqualTypeAndLineEdgeCleanupVisitor extends AbstractVisitor<Edge<Record>> implements ObjectProcessor<RecordGraph> { /** Flag indicating that the edge has been processed. */ private static final String EDGE_PROCESSED = EqualTypeAndLineEdgeCleanupVisitor.class.getName() + ".processed"; private LabelCountMap duplicateStatistics; private Set<String> equalExcludeFieldNames = new HashSet<>( Arrays.asList(Record.EXCLUDE_ID, Record.EXCLUDE_GEOMETRY)); @Override public void accept(final Edge<Record> edge) { if (edge.getProperty(EDGE_PROCESSED) == null) { final String typePath = edge.getTypeName(); final Graph<Record> graph = edge.getGraph(); final LineString line = edge.getLine(); Predicate<Edge<Record>> attributeAndGeometryFilter = new EdgeTypeNameFilter<>(typePath); final Predicate<Edge<Record>> filter = getPredicate(); if (filter != null) { attributeAndGeometryFilter = attributeAndGeometryFilter.and(filter); } final Predicate<Record> equalLineFilter = new RecordGeometryFilter<>( new LineEqualIgnoreDirectionFilter(line, 2)); final EdgeObjectFilter<Record> edgeFilter = new EdgeObjectFilter<>(equalLineFilter); attributeAndGeometryFilter = attributeAndGeometryFilter.and(edgeFilter); final List<Edge<Record>> equalEdges; if (getComparator() == null) { equalEdges = graph.getEdges(line, attributeAndGeometryFilter); } else { equalEdges = graph.getEdges(line, attributeAndGeometryFilter, getComparator()); } processEqualEdges(equalEdges); } } @PreDestroy public void destroy() { if (this.duplicateStatistics != null) { this.duplicateStatistics.disconnect(); } this.duplicateStatistics = null; } public boolean fixMissingZValues(final LineString line1, final LineString line2) { final LineString points1 = line1; final LineString points2 = line2; final int axisCount = points1.getAxisCount(); if (axisCount > 2) { final int vertexCount = points1.getVertexCount(); final boolean reverse = isReverse(line1, line2); if (reverse) { int j = vertexCount - 1; for (int i = 0; i < vertexCount; i++) { if (!fixZValues(points1, j, points2, i)) { return false; } j--; } } else { for (int i = 0; i < vertexCount; i++) { if (!fixZValues(points1, i, points2, i)) { return false; } } } return true; } else { return true; } } public boolean fixZValues(final LineString points1, final int index1, final LineString points2, final int index2) { // TODO // final double z1 = points1.getZ(index2); // final double z2 = points2.getZ(index1); // if (Double.isNaN(z1) || z1 == 0) { // if (!Double.isNaN(z2)) { // points1.setValue(index2, 2, z2); // } // return true; // } else if (Double.isNaN(z2) || z2 == 0) { // if (!Double.isNaN(z1)) { // points2.setValue(index1, 2, z1); // } return true; // } else { // return z1 == z2; // } } public Set<String> getEqualExcludeFieldNames() { return this.equalExcludeFieldNames; } @PostConstruct public void init() { this.duplicateStatistics = new LabelCountMap("Duplicate equal lines"); this.duplicateStatistics.connect(); } public boolean isReverse(final LineString points1, final LineString points2) { final int numPoints = points1.getVertexCount(); if (points1.equalsVertex(2, 0, points2, numPoints - 1)) { if (points1.equalsVertex(2, 0, points1, numPoints - 1)) { int j = numPoints - 1; for (int i = 1; i < numPoints; i++) { if (!points1.equalsVertex(2, i, points2, j)) { return false; } j++; } return true; } else { return true; } } else { return false; } } @Override public void process(final RecordGraph graph) { graph.forEachEdge(this); } private void processEqualEdge(final Edge<Record> edge1, final Edge<Record> edge2) { final Record record1 = edge1.getObject(); final Record record2 = edge2.getObject(); final boolean equalAttributes = record1.equalValuesExclude(record2, this.equalExcludeFieldNames); final LineString line1 = edge1.getLine(); int compare = 0; final Comparator<Edge<Record>> comparator = getComparator(); if (comparator != null) { compare = comparator.compare(edge1, edge2); } if (compare == 0) { if (equalAttributes) { boolean equalExcludedAttributes = true; for (final String name : this.equalExcludeFieldNames) { if (!record1.equalValue(record2, name)) { equalExcludedAttributes = false; } } final LineString line2 = edge2.getLine(); final boolean equalZ = fixMissingZValues(line1, line2); if (equalExcludedAttributes) { if (equalZ) { removeDuplicate(edge2, edge1); } else { RecordLog.error(getClass(), "Equal geometry with different coordinates or Z-values", record1); } } else { RecordLog.error(getClass(), "Equal geometry with different attributes: ", record1); } } else { RecordLog.error(getClass(), "Equal geometry with different attributes: ", record1); } } else { removeDuplicate(edge2, edge1); } } private void processEqualEdges(final List<Edge<Record>> equalEdges) { final Iterator<Edge<Record>> edgeIter = equalEdges.iterator(); final Edge<Record> edge1 = edgeIter.next(); edge1.setProperty(EDGE_PROCESSED, Boolean.TRUE); while (edgeIter.hasNext()) { final Edge<Record> edge2 = edgeIter.next(); edge2.setProperty(EDGE_PROCESSED, Boolean.TRUE); processEqualEdge(edge1, edge2); if (edge1.isRemoved()) { return; } } } protected void removeDuplicate(final Edge<Record> removeEdge, final Edge<Record> keepEdge) { removeEdge.remove(); if (this.duplicateStatistics != null) { this.duplicateStatistics.addCount(removeEdge.getObject()); } } public void setEqualExcludeFieldNames(final Collection<String> equalExcludeFieldNames) { setEqualExcludeFieldNames(new HashSet<>(equalExcludeFieldNames)); } public void setEqualExcludeFieldNames(final Set<String> equalExcludeFieldNames) { this.equalExcludeFieldNames = new HashSet<>(equalExcludeFieldNames); this.equalExcludeFieldNames.add(Record.EXCLUDE_ID); this.equalExcludeFieldNames.add(Record.EXCLUDE_GEOMETRY); } }