package com.revolsys.elevation.tin; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Consumer; import java.util.function.Predicate; import com.revolsys.collection.list.Lists; import com.revolsys.collection.map.MapEx; import com.revolsys.elevation.tin.compactbinary.CompactBinaryTin; import com.revolsys.elevation.tin.tin.AsciiTin; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.GeometryFactoryProxy; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Triangle; import com.revolsys.geometry.model.impl.BoundingBoxDoubleXY; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.geometry.model.impl.PointDoubleXYZ; import com.revolsys.geometry.model.segment.LineSegment; import com.revolsys.geometry.model.segment.LineSegmentDoubleGF; import com.revolsys.io.IoFactory; import com.revolsys.io.IoFactoryRegistry; import com.revolsys.predicate.Predicates; import com.revolsys.spring.resource.Resource; public interface TriangulatedIrregularNetwork extends GeometryFactoryProxy { static final int[] OPPOSITE_INDEXES = { 2, 1, 0 }; static final String GEOMETRY_FACTORY = "geometryFactory"; static boolean forEachTriangle(final Object source, final Map<String, ? extends Object> properties, final TriangleConsumer action) { final TriangulatedIrregularNetworkReadFactory factory = IoFactory .factory(TriangulatedIrregularNetworkReadFactory.class, source); if (factory == null) { return false; } else { final Resource resource = factory.getZipResource(source); factory.forEachTriangle(resource, properties, action); return true; } } static boolean forEachTriangle(final Object source, final TriangleConsumer action) { final Map<String, Object> properties = Collections.emptyMap(); return forEachTriangle(source, properties, action); } /** * Get the index of the corner or a triangle opposite corners i1 -> i2. i1 and * i2 must have different values in the range 0..2. * * @param i1 * @param i2 * @return */ public static int getOtherIndex(final int i1, final int i2) { return OPPOSITE_INDEXES[i1 + i2 - 1]; } public static void ioFactoryInit() { IoFactoryRegistry.addFactory(new CompactBinaryTin()); IoFactoryRegistry.addFactory(new AsciiTin()); } static TriangulatedIrregularNetwork newTriangulatedIrregularNetwork(final Object source) { final Map<String, Object> properties = Collections.emptyMap(); return newTriangulatedIrregularNetwork(source, properties); } static TriangulatedIrregularNetwork newTriangulatedIrregularNetwork(final Object source, final Map<String, ? extends Object> properties) { final TriangulatedIrregularNetworkReadFactory factory = IoFactory .factory(TriangulatedIrregularNetworkReadFactory.class, source); if (factory == null) { return null; } else { final Resource resource = factory.getZipResource(source); final TriangulatedIrregularNetwork dem = factory.newTriangulatedIrregularNetwork(resource, properties); return dem; } } default void cancelChanges() { } void forEachTriangle(final BoundingBox boundingBox, final Consumer<? super Triangle> action); default void forEachTriangle(final BoundingBox boundingBox, final Predicate<? super Triangle> filter, final Consumer<? super Triangle> action) { final Consumer<? super Triangle> filteredAction = Predicates.newConsumer(filter, action); forEachTriangle(boundingBox, filteredAction); } void forEachTriangle(final Consumer<? super Triangle> action); default void forEachTriangle(final Predicate<? super Triangle> filter, final Consumer<? super Triangle> action) { final Consumer<? super Triangle> filteredAction = Predicates.newConsumer(filter, action); forEachTriangle(filteredAction); } default void forEachTriangle(final TriangleConsumer action) { forEachTriangle((triangle) -> { final double x1 = triangle.getX(0); final double y1 = triangle.getY(0); final double z1 = triangle.getZ(0); final double x2 = triangle.getX(1); final double y2 = triangle.getY(1); final double z2 = triangle.getZ(1); final double x3 = triangle.getX(2); final double y3 = triangle.getY(2); final double z3 = triangle.getZ(2); action.accept(x1, y1, z1, x2, y2, z2, x3, y3, z3); }); } void forEachVertex(Consumer<Point> action); BoundingBox getBoundingBox(); default double getElevation(final double x, final double y) { final List<Triangle> triangles = getTriangles(new PointDoubleXY(x, y)); for (final Triangle triangle : triangles) { return triangle.getElevation(x, y); } return Double.NaN; } default LineString getElevation(final LineString line) { final GeometryFactory geometryFactory = line.getGeometryFactory(); final int vertexCount = line.getVertexCount(); final int axisCount = line.getAxisCount(); final double[] newCoordinates = new double[vertexCount * axisCount]; boolean modified = false; int i = 0; for (int vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++) { for (int axisIndex = 0; axisIndex < axisCount; axisIndex++) { double value = line.getCoordinate(vertexIndex, axisIndex); if (axisIndex == 2) { final double newZ = getElevation(line.getPoint(vertexIndex)); if (!Double.isNaN(newZ)) { if (value != newZ) { value = newZ; modified = true; } } } newCoordinates[i] = value; i++; } } if (modified) { return geometryFactory.lineString(axisCount, newCoordinates); } else { return line; } } default double getElevation(Point point) { point = convertGeometry(point); final List<Triangle> triangles = getTriangles(point); for (final Triangle triangle : triangles) { final Point t0 = triangle.getP0(); if (t0.equals(point)) { return t0.getZ(); } final Point t1 = triangle.getP1(); if (t1.equals(point)) { return t1.getZ(); } final Point t2 = triangle.getP2(); if (t2.equals(point)) { return t2.getZ(); } Point closestCorner = t0; LineSegment oppositeEdge = new LineSegmentDoubleGF(t1, t2); double closestDistance = point.distancePoint(closestCorner); final double t1Distance = point.distancePoint(t1); if (closestDistance > t1Distance) { closestCorner = t1; oppositeEdge = new LineSegmentDoubleGF(t2, t0); closestDistance = t1Distance; } if (closestDistance > point.distancePoint(t2)) { closestCorner = t2; oppositeEdge = new LineSegmentDoubleGF(t0, t1); } LineSegment segment = new LineSegmentDoubleGF(closestCorner, point).extend(0, t0.distancePoint(t1) + t1.distancePoint(t2) + t0.distancePoint(t2)); final Geometry intersectCoordinates = oppositeEdge.getIntersection(segment); if (intersectCoordinates.getVertexCount() > 0) { final Point intersectPoint = intersectCoordinates.getVertex(0); final double z = oppositeEdge.getElevation(intersectPoint); if (!Double.isNaN(z)) { final double x = intersectPoint.getX(); final double y = intersectPoint.getY(); final Point end = new PointDoubleXYZ(x, y, z); segment = new LineSegmentDoubleGF(t0, end); return segment.getElevation(point); } } } return Double.NaN; } Resource getResource(); int getTriangleCount(); default List<Triangle> getTriangles() { Consumer<Consumer<Triangle>> action = this::forEachTriangle; return Lists.newArray(action); } default List<Triangle> getTriangles(BoundingBox boundingBox) { boundingBox = boundingBox.convert(getGeometryFactory()); final List<Triangle> triangles = new ArrayList<>(); forEachTriangle(boundingBox, triangles::add); return triangles; } default List<Triangle> getTriangles(final double x, final double y) { final List<Triangle> triangles = new ArrayList<>(); final Predicate<Triangle> filter = (triangle) -> { return triangle.containsPoint(x, y); }; final BoundingBox boundingBox = new BoundingBoxDoubleXY(x, y); forEachTriangle(boundingBox, filter, triangles::add); return triangles; } default List<Triangle> getTriangles(final LineSegment segment) { final BoundingBox boundingBox = segment.getBoundingBox(); final List<Triangle> triangles = new ArrayList<>(); forEachTriangle(boundingBox, triangles::add); return triangles; } default List<Triangle> getTriangles(final Point point) { final List<Triangle> triangles = new ArrayList<>(); final Predicate<Triangle> filter = (triangle) -> { return triangle.containsPoint(point); }; final BoundingBox boundingBox = point.getBoundingBox(); forEachTriangle(boundingBox, filter, triangles::add); return triangles; } int getVertexCount(); default List<Point> getVertices() { return Lists.newArray(this::forEachVertex); } default boolean writeTriangulatedIrregularNetwork() { return writeTriangulatedIrregularNetwork(MapEx.EMPTY); } default boolean writeTriangulatedIrregularNetwork( final Map<String, ? extends Object> properties) { final Resource resource = getResource(); if (resource == null) { return false; } else { writeTriangulatedIrregularNetwork(resource, properties); return true; } } default void writeTriangulatedIrregularNetwork(final Object target) { final Map<String, ? extends Object> properties = Collections.emptyMap(); writeTriangulatedIrregularNetwork(target, properties); } default void writeTriangulatedIrregularNetwork(final Object target, final Map<String, ? extends Object> properties) { try ( TriangulatedIrregularNetworkWriter writer = TriangulatedIrregularNetworkWriter .newTriangulatedIrregularNetworkWriter(target, properties)) { if (writer == null) { throw new IllegalArgumentException( "No triangulated irregular network writer exists for " + target); } writer.write(this); } } }