From 048672bdc2a575b6b59d55ff7903f68e44a3f3d0 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Tue, 6 Apr 2021 15:02:43 +0200 Subject: [PATCH 01/18] tinfoor update --- noisemodelling-pathfinder/pom.xml | 6 +- .../pathfinder/LayerTinfour.java | 447 ++++++++++++++++++ 2 files changed, 452 insertions(+), 1 deletion(-) create mode 100644 noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java diff --git a/noisemodelling-pathfinder/pom.xml b/noisemodelling-pathfinder/pom.xml index 538e5bce2..18e913a16 100644 --- a/noisemodelling-pathfinder/pom.xml +++ b/noisemodelling-pathfinder/pom.xml @@ -80,7 +80,11 @@ 1.6.4 test - + + org.tinfour + TinfourCore + 2.1.5 + diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java new file mode 100644 index 000000000..1f14e2a19 --- /dev/null +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -0,0 +1,447 @@ +package org.noise_planet.noisemodelling.pathfinder; + +import org.locationtech.jts.algorithm.Orientation; +import org.locationtech.jts.geom.*; +import org.locationtech.jts.index.strtree.STRtree; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.tinfour.common.PolygonConstraint; +import org.tinfour.common.Vertex; +import org.tinfour.standard.IncrementalTin; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +public class LayerTinfour implements LayerDelaunay { + // Precision + private MathContext mathContext = MathContext.DECIMAL64; + private static final Logger LOGGER = LoggerFactory.getLogger(LayerTinfour.class); + + private double r(double v) { + return new BigDecimal(v).round(mathContext).doubleValue(); + } + + private Map pts = new HashMap(); + private List segments = new ArrayList(); + private AtomicInteger pointsCount = new AtomicInteger(0); + private LayerTinfour.PointHandler pointHandler = new LayerTinfour.PointHandler(this, pts, pointsCount); + private LayerTinfour.LineStringHandler lineStringHandler = new LayerTinfour.LineStringHandler(this, pts, pointsCount, segments); + private List vertices = new ArrayList(); + private HashMap buildingWithID = new HashMap(); + + private boolean computeNeighbors = false; + private List triangles = new ArrayList(); + private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i + private HashMap> hashOfArrayIndex = new HashMap>(); + private double maxArea = 0; + + private GeometryFactory factory = new GeometryFactory(); + + private static Coordinate TPointToCoordinate(Vertex tPoint) { + return new Coordinate(tPoint.getX(), tPoint.getY(), tPoint.getZ()); + } + + private static Vertex CoordinateToTPoint(Coordinate coordinate) { + return new Vertex(coordinate.x, coordinate.y, coordinate.z, -1); + } + + private static Vertex CoordinateToTPoint(Coordinate coordinate, int attribute) { + return new Vertex(coordinate.x, coordinate.y, coordinate.z, attribute); + } + + private static final class BuildingWithID { + private Polygon building; + + + public BuildingWithID(Polygon building) { + this.building = building; + + } + + + public boolean isTriangleInBuilding(Vertex point) { + return this.building.intersects(new GeometryFactory().createPoint(TPointToCoordinate(point))); + } + + + } + + private int getOrAppendVertices(Coordinate newCoord, List vertices, HashMap> hashOfArrayIndex) { + // We can obtain the same hash with two different coordinate (4 Bytes or + // 8 Bytes against 12 or 24 Bytes) , then we use a list as the value of + // the hashmap + // First step - Search the vertice parameter within the hashMap + int newCoordIndex = -1; + Integer coordinateHash = newCoord.hashCode(); + LinkedList listOfIndex = hashOfArrayIndex.get(coordinateHash); + if (listOfIndex != null) // There are the same hash value + { + for (int vind : listOfIndex) // Loop inside the coordinate index + { + if (newCoord.equals3D(vertices.get(vind))) // the coordinate is + // equal to the + // existing + // coordinate + { + newCoordIndex = vind; + break; // Exit for loop + } + } + if (newCoordIndex == -1) { + // No vertices has been found, we push the new coordinate into + // the existing linked list + newCoordIndex = vertices.size(); + listOfIndex.add(newCoordIndex); + vertices.add(newCoord); + } + } else { + // Push a new hash element + listOfIndex = new LinkedList(); + newCoordIndex = vertices.size(); + listOfIndex.add(newCoordIndex); + vertices.add(newCoord); + hashOfArrayIndex.put(coordinateHash, listOfIndex); + } + return newCoordIndex; + } + + @Override + public void processDelaunay() throws LayerDelaunayError { + triangles.clear(); + neighbors.clear(); + vertices.clear(); + hashOfArrayIndex.clear(); + + // Create input data for Poly2Tri + int[] index = new int[segments.size()]; + for (int i = 0; i < index.length; i++) { + index[i] = segments.get(i); + } + // Construct final points array by reversing key,value of hash map + Vertex[] ptsArray = new Vertex[pointsCount.get()]; + for(Map.Entry entry : pts.entrySet()) { + ptsArray[entry.getValue()] = entry.getKey(); + } + + List meshPoints = new ArrayList<>(Arrays.asList(ptsArray)); + + STRtree buildingsRtree = null; + if(maxArea > 0) { + // STRtree minimal size is 2 + buildingsRtree = new STRtree(Math.max(10, buildingWithID.size())); + for (Map.Entry buildingWithIDEntry : buildingWithID.entrySet()) { + buildingsRtree.insert(buildingWithIDEntry.getValue().building.getEnvelopeInternal(), buildingWithIDEntry.getKey()); + } + } + + IncrementalTin tin = new IncrementalTin(); + + boolean refine; + do { + // Triangulate + Poly2Tri.triangulate(TriangulationAlgorithm.DTSweep, convertedInput); + refine = false; + + // Will triangulate multiple time if refinement is necessary + if(buildingsRtree != null && maxArea > 0) { + List trianglesDelaunay = convertedInput.getTriangles(); + for (DelaunayTriangle triangle : trianglesDelaunay) { + if(triangle.area() > maxArea) { + // Insert steiner point in centroid + TPoint centroid = triangle.centroid(); + // Do not add steiner points into buildings + Envelope searchEnvelope = new Envelope(TPointToCoordinate(centroid)); + searchEnvelope.expandBy(1.); + List polyInters = buildingsRtree.query(searchEnvelope); + boolean inBuilding = false; + for (Object id : polyInters) { + if (id instanceof Integer) { + LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); + if (inPoly.building.contains(factory.createPoint(TPointToCoordinate(centroid)))) { + inBuilding = true; + break; + } + } + } + if(!inBuilding) { + meshPoints.add(centroid); + refine = true; + } + } + } + if(refine) { + convertedInput = new ConstrainedPointSet(meshPoints, index); + } + } + } while (refine); + + + List trianglesDelaunay = convertedInput.getTriangles(); + List triangleAttribute = Arrays.asList(new Integer[trianglesDelaunay.size()]); + // Create an index of triangles instance for fast neighbors search + Map triangleSearch = new HashMap<>(trianglesDelaunay.size()); + int triangleIndex = 0; + if (computeNeighbors) { + for (DelaunayTriangle triangle : trianglesDelaunay) { + triangleSearch.put(triangle, triangleIndex); + triangleIndex++; + } + } + + //Build ArrayList for binary search + //test add height + int triangleId = 0; + for (DelaunayTriangle triangle : trianglesDelaunay) { + Coordinate[] ring = new Coordinate[]{TPointToCoordinate(triangle.points[0]), TPointToCoordinate(triangle.points[1]), TPointToCoordinate(triangle.points[2]), TPointToCoordinate(triangle.points[0])}; + //if one of three vertices have buildingID and buildingID>=1 + if (getPointAttribute(triangle.points[0]) >= 1 || getPointAttribute(triangle.points[1]) >= 1 || getPointAttribute(triangle.points[2]) >= 1) { + int propertyBulidingID = 0; + for (int i = 0; i <= 2; i++) { + int potentialBuildingID = getPointAttribute(triangle.points[i]); + if (potentialBuildingID >= 1) { + //get the Barycenter of the triangle so we can sure this point is in this triangle and we will check if the building contain this point + if (this.buildingWithID.get(potentialBuildingID).isTriangleInBuilding(triangle.centroid())) { + propertyBulidingID = potentialBuildingID; + break; + } + } + } + triangleAttribute.set(triangleId, propertyBulidingID); + } else { + //if there are less than 3 points have buildingID this triangle is out of building + triangleAttribute.set(triangleId, 0); + } + + if (!Orientation.isCCW(ring)) { + Coordinate tmp = new Coordinate(ring[0]); + ring[0] = ring[2]; + ring[2] = tmp; + } + + int a = getOrAppendVertices(ring[0], vertices, hashOfArrayIndex); + int b = getOrAppendVertices(ring[1], vertices, hashOfArrayIndex); + int c = getOrAppendVertices(ring[2], vertices, hashOfArrayIndex); + triangles.add(new Triangle(a, b, c, triangleAttribute.get(triangleId))); + if (computeNeighbors) { + // Compute neighbors index + neighbors.add(new Triangle(getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[0])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[1])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[2])))); + } + triangleId++; + } + } + + + public static final class SetZFilter implements CoordinateSequenceFilter { + private boolean done = false; + private boolean resetToZero = false; + + public SetZFilter() { + + } + + public SetZFilter(boolean resetToZero) { + this.resetToZero = resetToZero; + } + + @Override + public void filter(CoordinateSequence seq, int i) { + double x = seq.getX(i); + double y = seq.getY(i); + double z = seq.getOrdinate(i, 2); + seq.setOrdinate(i, 0, x); + seq.setOrdinate(i, 1, y); + if (Double.isNaN(z) || resetToZero) { + seq.setOrdinate(i, 2, 0); + } else { + seq.setOrdinate(i, 2, z); + } + if (i == seq.size()) { + done = true; + } + } + + @Override + public boolean isDone() { + return done; + } + + @Override + public boolean isGeometryChanged() { + return true; + } + } + + /** + * Add height of building + * + * @return + */ + @Override + public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayError { + + //// To avoid errors we set the Z coordinate to 0. + LayerTinfour.SetZFilter zFilter = new LayerTinfour.SetZFilter(); + newPoly.apply(zFilter); + GeometryFactory factory = new GeometryFactory(); + final Coordinate[] coordinates = newPoly.getExteriorRing().getCoordinates(); + if (coordinates.length > 1) { + LineString newLineString = factory.createLineString(coordinates); + this.addLineString(newLineString, buildingId); + this.buildingWithID.put(buildingId, new LayerTinfour.BuildingWithID(newPoly)); + } + // Append holes + final int holeCount = newPoly.getNumInteriorRing(); + for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) { + LineString holeLine = newPoly.getInteriorRingN(holeIndex); + // Convert hole into a polygon, then compute an interior point + Polygon polyBuffnew = factory.createPolygon(factory.createLinearRing(holeLine.getCoordinates()), null); + if (polyBuffnew.getArea() > 0.) { + Coordinate interiorPoint = polyBuffnew.getInteriorPoint().getCoordinate(); + if (!factory.createPoint(interiorPoint).intersects(holeLine)) { + this.addLineString(holeLine, buildingId); + } else { + LOGGER.info("Warning : hole rejected, can't find interior point."); + } + } else { + LOGGER.info("Warning : hole rejected, area=0"); + } + } + } + + @Override + public void setMinAngle(Double minAngle) { + // TODO Auto-generated method stub + + } + + @Override + public void hintInit(Envelope bBox, long polygonCount, long verticesCount) throws LayerDelaunayError { + } + + @Override + public List getVertices() throws LayerDelaunayError { + return this.vertices; + } + + @Override + public List getTriangles() throws LayerDelaunayError { + return this.triangles; + } + + @Override + public void addVertex(Coordinate vertexCoordinate) throws LayerDelaunayError { + pointHandler.addPt(vertexCoordinate, -1); + } + + @Override + public void setMaxArea(Double maxArea) throws LayerDelaunayError { + this.maxArea = Math.max(0, maxArea); + } + + //add buildingID to edge property and to points property + public void addLineString(LineString lineToProcess, int buildingID) throws LayerDelaunayError { + lineStringHandler.reset(); + lineStringHandler.setAttribute(buildingID); + lineToProcess.apply(lineStringHandler); + } + //add buildingID to edge property and to points property + + @Override + public void reset() { + // TODO Auto-generated method stub + + } + + @Override + public List getNeighbors() throws LayerDelaunayError { + if (computeNeighbors) { + return neighbors; + } else { + throw new LayerDelaunayError("You must call setRetrieveNeighbors(True) before process delaunay triangulation"); + } + } + + @Override + public void setRetrieveNeighbors(boolean retrieve) { + this.computeNeighbors = retrieve; + + } + + + private static class PointHandler implements CoordinateFilter { + private LayerTinfour delaunayData; + private Map pts; + private AtomicInteger maxIndex; + + public PointHandler(LayerTinfour delaunayData, Map pts, AtomicInteger maxIndex) { + this.delaunayData = delaunayData; + this.pts = pts; + this.maxIndex = maxIndex; + } + + public Coordinate[] getPoints() { + Coordinate[] ret = new Coordinate[pts.size()]; + int i = 0; + for (Vertex pt : pts.keySet()) { + ret[i] = TPointToCoordinate(pt); + i++; + } + return ret; + } + + protected int addPt(Coordinate coordinate, int attribute) { + Vertex pt = new Vertex(delaunayData.r(coordinate.x), delaunayData.r(coordinate.y), Double.isNaN(coordinate.z) ? 0 : delaunayData.r(coordinate.z), attribute); + Integer index = pts.get(pt); + if (index == null) { + index = maxIndex.getAndAdd(1); + pts.put(pt, index); + } + return index; + } + + @Override + public void filter(Coordinate pt) { + addPt(pt, -1); + } + } + + private static final class LineStringHandler extends LayerTinfour.PointHandler { + private List segments; + private int firstPtIndex = -1; + private int attribute = -1; + + public LineStringHandler(LayerTinfour delaunayData, Map pts, AtomicInteger maxIndex, List segments) { + super(delaunayData, pts, maxIndex); + this.segments = segments; + } + + /** + * New line string + */ + public void reset() { + firstPtIndex = -1; + attribute = -1; + } + + public void setAttribute(int attribute) { + this.attribute = attribute; + } + + @Override + public void filter(Coordinate pt) { + if (firstPtIndex == -1) { + firstPtIndex = addPt(pt, attribute); + } else { + int secondPt = addPt(pt, attribute); + if (secondPt != firstPtIndex) { + segments.add(firstPtIndex); + segments.add(secondPt); + firstPtIndex = secondPt; + } + } + } + } + +} From d3d612651ce34e39f72172260b9a125640a6e1fe Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Fri, 9 Apr 2021 16:47:53 +0200 Subject: [PATCH 02/18] retrieve vertex index and neighbors ok --- .../pathfinder/LayerTinfour.java | 136 +++++++++--------- .../pathfinder/LayerTinfourTest.java | 33 +++++ 2 files changed, 102 insertions(+), 67 deletions(-) create mode 100644 noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 1f14e2a19..79702ac04 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -6,13 +6,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tinfour.common.PolygonConstraint; +import org.tinfour.common.SimpleTriangle; import org.tinfour.common.Vertex; import org.tinfour.standard.IncrementalTin; +import org.tinfour.utils.TriangleCollector; import java.math.BigDecimal; import java.math.MathContext; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; public class LayerTinfour implements LayerDelaunay { // Precision @@ -34,7 +37,6 @@ private double r(double v) { private boolean computeNeighbors = false; private List triangles = new ArrayList(); private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i - private HashMap> hashOfArrayIndex = new HashMap>(); private double maxArea = 0; private GeometryFactory factory = new GeometryFactory(); @@ -107,12 +109,40 @@ private int getOrAppendVertices(Coordinate newCoord, List vertices, return newCoordIndex; } + private List computeTriangles(IncrementalTin incrementalTin) { + ArrayList triangles = new ArrayList<>(incrementalTin.countTriangles().getCount()); + TriangleBuilder triangleBuilder = new TriangleBuilder(triangles); + TriangleCollector.visitSimpleTriangles(incrementalTin, triangleBuilder); + return triangles; + } + + private static class TriangleBuilder implements Consumer { + ArrayList triangles; + + public TriangleBuilder(ArrayList triangles) { + this.triangles = triangles; + } + + @Override + public void accept(SimpleTriangle triangle) { + triangles.add(triangle); + } + } + + private static Coordinate getCentroid(SimpleTriangle triangle) { + Vertex va = triangle.getVertexA(); + Vertex vb = triangle.getVertexB(); + Vertex vc = triangle.getVertexC(); + double cx = ( va.getX() + vb.getX() + vc.getX() ) / 3d; + double cy = ( va.getY() + vb.getY() + vc.getY() ) / 3d; + double cz = ( va.getZ() + vb.getZ() + vc.getZ() ) / 3d; + return new Coordinate( cx, cy, cz); + } + @Override public void processDelaunay() throws LayerDelaunayError { triangles.clear(); - neighbors.clear(); vertices.clear(); - hashOfArrayIndex.clear(); // Create input data for Poly2Tri int[] index = new int[segments.size()]; @@ -136,99 +166,67 @@ public void processDelaunay() throws LayerDelaunayError { } } - IncrementalTin tin = new IncrementalTin(); + IncrementalTin tin; boolean refine; + List simpleTriangles = new ArrayList<>(); do { // Triangulate - Poly2Tri.triangulate(TriangulationAlgorithm.DTSweep, convertedInput); + tin = new IncrementalTin(); + tin.add(meshPoints, null); refine = false; + simpleTriangles = computeTriangles(tin); // Will triangulate multiple time if refinement is necessary if(buildingsRtree != null && maxArea > 0) { - List trianglesDelaunay = convertedInput.getTriangles(); - for (DelaunayTriangle triangle : trianglesDelaunay) { - if(triangle.area() > maxArea) { + for (SimpleTriangle triangle : simpleTriangles) { + if(triangle.getArea() > maxArea) { // Insert steiner point in centroid - TPoint centroid = triangle.centroid(); + Coordinate centroid = getCentroid(triangle); // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(TPointToCoordinate(centroid)); + Envelope searchEnvelope = new Envelope(centroid); searchEnvelope.expandBy(1.); List polyInters = buildingsRtree.query(searchEnvelope); boolean inBuilding = false; for (Object id : polyInters) { if (id instanceof Integer) { LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(TPointToCoordinate(centroid)))) { + if (inPoly.building.contains(factory.createPoint(centroid))) { inBuilding = true; break; } } } if(!inBuilding) { - meshPoints.add(centroid); + meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z)); refine = true; } } } - if(refine) { - convertedInput = new ConstrainedPointSet(meshPoints, index); - } } } while (refine); - - - List trianglesDelaunay = convertedInput.getTriangles(); - List triangleAttribute = Arrays.asList(new Integer[trianglesDelaunay.size()]); - // Create an index of triangles instance for fast neighbors search - Map triangleSearch = new HashMap<>(trianglesDelaunay.size()); - int triangleIndex = 0; - if (computeNeighbors) { - for (DelaunayTriangle triangle : trianglesDelaunay) { - triangleSearch.put(triangle, triangleIndex); - triangleIndex++; - } + List verts = tin.getVertices(); + vertices = new ArrayList<>(verts.size()); + for(Vertex v : verts) { + addVertex(toCoordinate(v)); } - - //Build ArrayList for binary search - //test add height - int triangleId = 0; - for (DelaunayTriangle triangle : trianglesDelaunay) { - Coordinate[] ring = new Coordinate[]{TPointToCoordinate(triangle.points[0]), TPointToCoordinate(triangle.points[1]), TPointToCoordinate(triangle.points[2]), TPointToCoordinate(triangle.points[0])}; - //if one of three vertices have buildingID and buildingID>=1 - if (getPointAttribute(triangle.points[0]) >= 1 || getPointAttribute(triangle.points[1]) >= 1 || getPointAttribute(triangle.points[2]) >= 1) { - int propertyBulidingID = 0; - for (int i = 0; i <= 2; i++) { - int potentialBuildingID = getPointAttribute(triangle.points[i]); - if (potentialBuildingID >= 1) { - //get the Barycenter of the triangle so we can sure this point is in this triangle and we will check if the building contain this point - if (this.buildingWithID.get(potentialBuildingID).isTriangleInBuilding(triangle.centroid())) { - propertyBulidingID = potentialBuildingID; - break; - } - } - } - triangleAttribute.set(triangleId, propertyBulidingID); - } else { - //if there are less than 3 points have buildingID this triangle is out of building - triangleAttribute.set(triangleId, 0); - } - - if (!Orientation.isCCW(ring)) { - Coordinate tmp = new Coordinate(ring[0]); - ring[0] = ring[2]; - ring[2] = tmp; - } - - int a = getOrAppendVertices(ring[0], vertices, hashOfArrayIndex); - int b = getOrAppendVertices(ring[1], vertices, hashOfArrayIndex); - int c = getOrAppendVertices(ring[2], vertices, hashOfArrayIndex); - triangles.add(new Triangle(a, b, c, triangleAttribute.get(triangleId))); - if (computeNeighbors) { - // Compute neighbors index - neighbors.add(new Triangle(getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[0])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[1])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[2])))); + Map edgeIndexToTriangleIndex = new HashMap<>(); + for(SimpleTriangle t : simpleTriangles) { + int triangleAttribute = -1; + triangles.add(new Triangle(pts.get(t.getVertexA()), pts.get(t.getVertexB()),pts.get(t.getVertexC()), triangleAttribute)); + edgeIndexToTriangleIndex.put(t.getEdgeA().getIndex(), triangles.size() - 1); + edgeIndexToTriangleIndex.put(t.getEdgeB().getIndex(), triangles.size() - 1); + edgeIndexToTriangleIndex.put(t.getEdgeC().getIndex(), triangles.size() - 1); + } + if(computeNeighbors) { + for(SimpleTriangle t : simpleTriangles) { + Integer neighA = edgeIndexToTriangleIndex.get(t.getEdgeA().getDual().getIndex()); + Integer neighB = edgeIndexToTriangleIndex.get(t.getEdgeB().getDual().getIndex()); + Integer neighC = edgeIndexToTriangleIndex.get(t.getEdgeC().getDual().getIndex()); + neighbors.add(new Triangle(neighA != null ? neighA : -1, + neighB != null ? neighB : -1, + neighC != null ? neighC : -1)); } - triangleId++; } } @@ -327,7 +325,11 @@ public List getVertices() throws LayerDelaunayError { @Override public List getTriangles() throws LayerDelaunayError { - return this.triangles; + return triangles; + } + + private static Coordinate toCoordinate(Vertex v) { + return new Coordinate(v.getX(), v.getY(), v.getZ()); } @Override diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java new file mode 100644 index 000000000..c43663c1f --- /dev/null +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -0,0 +1,33 @@ +package org.noise_planet.noisemodelling.pathfinder; + +import org.junit.Test; +import org.locationtech.jts.geom.Coordinate; + +import java.util.List; + +import static org.junit.Assert.*; + +public class LayerTinfourTest { + + @Test + public void testPointDelaunay1() throws LayerDelaunayError { + LayerTinfour layerTinfour = new LayerTinfour(); + layerTinfour.setRetrieveNeighbors(true); + + layerTinfour.addVertex(new Coordinate(0,0,0)); + layerTinfour.addVertex(new Coordinate(100,0,0)); + layerTinfour.addVertex(new Coordinate(100,100,0)); + layerTinfour.addVertex(new Coordinate(0,100,0)); + + layerTinfour.addVertex(new Coordinate(25,51,0)); + layerTinfour.addVertex(new Coordinate(10,5,0)); + layerTinfour.addVertex(new Coordinate(85,78,0)); + + layerTinfour.processDelaunay(); + + + List triangleList = layerTinfour.getTriangles(); + List neighbors = layerTinfour.getNeighbors(); + assertEquals(8, triangleList.size()); + } +} \ No newline at end of file From 0c4ab72e5a7a6dbc421428899925ad36fbe0d892 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Fri, 9 Apr 2021 17:37:36 +0200 Subject: [PATCH 03/18] add constraints --- .../pathfinder/LayerTinfour.java | 69 +++++-------------- .../pathfinder/LayerTinfourTest.java | 30 ++++++++ 2 files changed, 48 insertions(+), 51 deletions(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 79702ac04..25cff5169 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -5,9 +5,7 @@ import org.locationtech.jts.index.strtree.STRtree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.tinfour.common.PolygonConstraint; -import org.tinfour.common.SimpleTriangle; -import org.tinfour.common.Vertex; +import org.tinfour.common.*; import org.tinfour.standard.IncrementalTin; import org.tinfour.utils.TriangleCollector; @@ -56,57 +54,10 @@ private static Vertex CoordinateToTPoint(Coordinate coordinate, int attribute) { private static final class BuildingWithID { private Polygon building; - public BuildingWithID(Polygon building) { this.building = building; } - - - public boolean isTriangleInBuilding(Vertex point) { - return this.building.intersects(new GeometryFactory().createPoint(TPointToCoordinate(point))); - } - - - } - - private int getOrAppendVertices(Coordinate newCoord, List vertices, HashMap> hashOfArrayIndex) { - // We can obtain the same hash with two different coordinate (4 Bytes or - // 8 Bytes against 12 or 24 Bytes) , then we use a list as the value of - // the hashmap - // First step - Search the vertice parameter within the hashMap - int newCoordIndex = -1; - Integer coordinateHash = newCoord.hashCode(); - LinkedList listOfIndex = hashOfArrayIndex.get(coordinateHash); - if (listOfIndex != null) // There are the same hash value - { - for (int vind : listOfIndex) // Loop inside the coordinate index - { - if (newCoord.equals3D(vertices.get(vind))) // the coordinate is - // equal to the - // existing - // coordinate - { - newCoordIndex = vind; - break; // Exit for loop - } - } - if (newCoordIndex == -1) { - // No vertices has been found, we push the new coordinate into - // the existing linked list - newCoordIndex = vertices.size(); - listOfIndex.add(newCoordIndex); - vertices.add(newCoord); - } - } else { - // Push a new hash element - listOfIndex = new LinkedList(); - newCoordIndex = vertices.size(); - listOfIndex.add(newCoordIndex); - vertices.add(newCoord); - hashOfArrayIndex.put(coordinateHash, listOfIndex); - } - return newCoordIndex; } private List computeTriangles(IncrementalTin incrementalTin) { @@ -173,7 +124,14 @@ public void processDelaunay() throws LayerDelaunayError { do { // Triangulate tin = new IncrementalTin(); + // Add points tin.add(meshPoints, null); + // Add constraints + List constraints = new ArrayList<>(segments.size()); + for(int i=0; i < segments.size(); i+=2) { + constraints.add(new LinearConstraint(meshPoints.get(segments.get(i)), meshPoints.get(segments.get(i+1)))); + } + tin.addConstraints(constraints, maxArea > 0); refine = false; simpleTriangles = computeTriangles(tin); @@ -207,8 +165,9 @@ public void processDelaunay() throws LayerDelaunayError { } while (refine); List verts = tin.getVertices(); vertices = new ArrayList<>(verts.size()); + pts.clear(); for(Vertex v : verts) { - addVertex(toCoordinate(v)); + pointHandler.addVertex(v); } Map edgeIndexToTriangleIndex = new HashMap<>(); for(SimpleTriangle t : simpleTriangles) { @@ -393,6 +352,14 @@ public Coordinate[] getPoints() { return ret; } + protected int addVertex(Vertex pt) { + Integer index = pts.get(pt); + if (index == null) { + index = maxIndex.getAndAdd(1); + pts.put(pt, index); + } + return index; + } protected int addPt(Coordinate coordinate, int attribute) { Vertex pt = new Vertex(delaunayData.r(coordinate.x), delaunayData.r(coordinate.y), Double.isNaN(coordinate.z) ? 0 : delaunayData.r(coordinate.z), attribute); Integer index = pts.get(pt); diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index c43663c1f..57d3068d0 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -2,6 +2,7 @@ import org.junit.Test; import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; import java.util.List; @@ -30,4 +31,33 @@ public void testPointDelaunay1() throws LayerDelaunayError { List neighbors = layerTinfour.getNeighbors(); assertEquals(8, triangleList.size()); } + + @Test + public void testPolygonDelaunay1() throws LayerDelaunayError { + LayerTinfour layerTinfour = new LayerTinfour(); + layerTinfour.setRetrieveNeighbors(true); + + GeometryFactory geometryFactory = new GeometryFactory(); + + layerTinfour.addVertex(new Coordinate(0,0,0)); + layerTinfour.addVertex(new Coordinate(100,0,0)); + layerTinfour.addVertex(new Coordinate(100,100,0)); + layerTinfour.addVertex(new Coordinate(0,100,0)); + + Coordinate[] building1 = new Coordinate[] { + new Coordinate(40,20), + new Coordinate(55,20), + new Coordinate(55,45), + new Coordinate(40,45), + new Coordinate(40,20), + }; + + layerTinfour.addPolygon(geometryFactory.createPolygon(building1), 55); + + layerTinfour.processDelaunay(); + + List triangleList = layerTinfour.getTriangles(); + List neighbors = layerTinfour.getNeighbors(); + assertEquals(10, triangleList.size()); + } } \ No newline at end of file From 074673bebdda5dfc17fbd10a0ff759b6b511b484 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:09:37 +0200 Subject: [PATCH 04/18] fix processDelaunay --- .../pathfinder/LayerTinfour.java | 59 ++++++++++++------- .../pathfinder/MeshBuilder.java | 2 +- .../pathfinder/LayerTinfourTest.java | 10 ++++ 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 25cff5169..7ec6925e6 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -29,16 +29,18 @@ private double r(double v) { private AtomicInteger pointsCount = new AtomicInteger(0); private LayerTinfour.PointHandler pointHandler = new LayerTinfour.PointHandler(this, pts, pointsCount); private LayerTinfour.LineStringHandler lineStringHandler = new LayerTinfour.LineStringHandler(this, pts, pointsCount, segments); - private List vertices = new ArrayList(); private HashMap buildingWithID = new HashMap(); - private boolean computeNeighbors = false; - private List triangles = new ArrayList(); - private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i private double maxArea = 0; private GeometryFactory factory = new GeometryFactory(); + + // Output data + private List vertices = new ArrayList(); + private List triangles = new ArrayList(); + private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i + private static Coordinate TPointToCoordinate(Vertex tPoint) { return new Coordinate(tPoint.getX(), tPoint.getY(), tPoint.getZ()); } @@ -108,16 +110,11 @@ public void processDelaunay() throws LayerDelaunayError { List meshPoints = new ArrayList<>(Arrays.asList(ptsArray)); - STRtree buildingsRtree = null; - if(maxArea > 0) { - // STRtree minimal size is 2 - buildingsRtree = new STRtree(Math.max(10, buildingWithID.size())); - for (Map.Entry buildingWithIDEntry : buildingWithID.entrySet()) { - buildingsRtree.insert(buildingWithIDEntry.getValue().building.getEnvelopeInternal(), buildingWithIDEntry.getKey()); - } + STRtree buildingsRtree = new STRtree(Math.max(10, buildingWithID.size())); + for (Map.Entry buildingWithIDEntry : buildingWithID.entrySet()) { + buildingsRtree.insert(buildingWithIDEntry.getValue().building.getEnvelopeInternal(), buildingWithIDEntry.getKey()); } - IncrementalTin tin; boolean refine; List simpleTriangles = new ArrayList<>(); @@ -136,27 +133,29 @@ public void processDelaunay() throws LayerDelaunayError { simpleTriangles = computeTriangles(tin); // Will triangulate multiple time if refinement is necessary - if(buildingsRtree != null && maxArea > 0) { + if(maxArea > 0) { for (SimpleTriangle triangle : simpleTriangles) { if(triangle.getArea() > maxArea) { - // Insert steiner point in centroid - Coordinate centroid = getCentroid(triangle); + // Insert steiner point in circumcircle + Coordinate circumcentre = org.locationtech.jts.geom.Triangle.circumcentre(toCoordinate(triangle.getVertexA()), + toCoordinate(triangle.getVertexB()), + toCoordinate(triangle.getVertexC())); // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(centroid); + Envelope searchEnvelope = new Envelope(circumcentre); searchEnvelope.expandBy(1.); List polyInters = buildingsRtree.query(searchEnvelope); boolean inBuilding = false; for (Object id : polyInters) { if (id instanceof Integer) { LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(centroid))) { + if (inPoly.building.contains(factory.createPoint(circumcentre))) { inBuilding = true; break; } } } if(!inBuilding) { - meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z)); + meshPoints.add(new Vertex(circumcentre.x, circumcentre.y, circumcentre.z)); refine = true; } } @@ -165,14 +164,30 @@ public void processDelaunay() throws LayerDelaunayError { } while (refine); List verts = tin.getVertices(); vertices = new ArrayList<>(verts.size()); - pts.clear(); + Map vertIndex = new HashMap<>(); for(Vertex v : verts) { - pointHandler.addVertex(v); + vertIndex.put(v, vertices.size()); + vertices.add(toCoordinate(v)); } Map edgeIndexToTriangleIndex = new HashMap<>(); for(SimpleTriangle t : simpleTriangles) { - int triangleAttribute = -1; - triangles.add(new Triangle(pts.get(t.getVertexA()), pts.get(t.getVertexB()),pts.get(t.getVertexC()), triangleAttribute)); + int triangleAttribute = 0; + // Insert steiner point in centroid + Coordinate centroid = getCentroid(t); + // Do not add steiner points into buildings + Envelope searchEnvelope = new Envelope(centroid); + searchEnvelope.expandBy(1.); + List polyInters = buildingsRtree.query(searchEnvelope); + for (Object id : polyInters) { + if (id instanceof Integer) { + LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); + if (inPoly.building.contains(factory.createPoint(centroid))) { + triangleAttribute = (int) id; + break; + } + } + } + triangles.add(new Triangle(vertIndex.get(t.getVertexA()), vertIndex.get(t.getVertexB()),vertIndex.get(t.getVertexC()), triangleAttribute)); edgeIndexToTriangleIndex.put(t.getEdgeA().getIndex(), triangles.size() - 1); edgeIndexToTriangleIndex.put(t.getEdgeB().getIndex(), triangles.size() - 1); edgeIndexToTriangleIndex.put(t.getEdgeC().getIndex(), triangles.size() - 1); diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java index 228f8096e..5b6e4699f 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java @@ -435,7 +435,7 @@ public void finishPolygonFeeding(Geometry boundingBoxGeom) throws LayerDelaunayE this.geometriesBoundingBox = boundingBoxGeom.getEnvelopeInternal(); } - LayerDelaunay delaunayTool = new LayerPoly2Tri(); + LayerDelaunay delaunayTool = new LayerTinfour(); //merge buildings diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index 57d3068d0..73619abc7 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -1,5 +1,6 @@ package org.noise_planet.noisemodelling.pathfinder; +import org.h2gis.functions.io.osm.OSMDriverFunction; import org.junit.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; @@ -57,7 +58,16 @@ public void testPolygonDelaunay1() throws LayerDelaunayError { layerTinfour.processDelaunay(); List triangleList = layerTinfour.getTriangles(); + int numbertri55 = 0; + for(Triangle tri : triangleList) { + if(tri.getAttribute() == 55) { + numbertri55++; + } + } + // 2 triangle inside a rectangular building + assertEquals(2, numbertri55); List neighbors = layerTinfour.getNeighbors(); assertEquals(10, triangleList.size()); } + } \ No newline at end of file From bceb5c2bde95dceb414c77ba6143e7842f3a523c Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Mon, 12 Apr 2021 17:15:34 +0200 Subject: [PATCH 05/18] remove poly2tri --- noisemodelling-pathfinder/pom.xml | 5 - .../pathfinder/LayerPoly2Tri.java | 545 ------------------ .../pathfinder/MeshBuilder.java | 2 +- .../pathfinder/TestWallReflection.java | 2 +- 4 files changed, 2 insertions(+), 552 deletions(-) delete mode 100644 noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerPoly2Tri.java diff --git a/noisemodelling-pathfinder/pom.xml b/noisemodelling-pathfinder/pom.xml index 18e913a16..849b9688d 100644 --- a/noisemodelling-pathfinder/pom.xml +++ b/noisemodelling-pathfinder/pom.xml @@ -37,11 +37,6 @@ h2gis-utilities ${h2gis-version} - - org.orbisgis - poly2tri-core - 0.1.2 - com.fasterxml.jackson.core jackson-databind diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerPoly2Tri.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerPoly2Tri.java deleted file mode 100644 index 63926c0d4..000000000 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerPoly2Tri.java +++ /dev/null @@ -1,545 +0,0 @@ -/* - * NoiseMap is a scientific computation plugin for OrbisGIS developed in order to - * evaluate the noise impact in urban areas. This model is - * based on the French standard method NMPB2008. It includes traffic-to-noise - * sources evaluation and sound propagation processing. - * - * This plugin is currently developed by the Environmental Acoustics Laboratory (LAE) of Ifsttar - * (http://wwww.lae.ifsttar.fr/) in collaboration with the Lab-STICC CNRS laboratory. - * It was initially developed as part of the Eval-PDU project, funded by the - * French Agence Nationale de la Recherche (ANR) under contract ANR-08-VILL-0005-01. - * - * Noisemap is distributed under GPL 3 license. Its reference contact is Judicaƫl - * Picaut . It is maintained by Nicolas Fortin - * - * - * Copyright (C) 2011-2016 IFSTTAR-CNRS - * - * Noisemap is free software: you can redistribute it and/or modify it under the - * terms of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Noisemap is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * Noisemap. If not, see . - * - * For more information concerning NoiseM@p, please consult: - * - * For more information concerning OrbisGis, please consult: - * or contact directly: - * - * info_at_ orbisgis.org - */ - -package org.noise_planet.noisemodelling.pathfinder; - -import org.locationtech.jts.algorithm.Orientation; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.CoordinateFilter; -import org.locationtech.jts.geom.CoordinateSequence; -import org.locationtech.jts.geom.CoordinateSequenceFilter; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.index.strtree.*; -import org.poly2tri.Poly2Tri; -import org.poly2tri.geometry.polygon.PolygonPoint; -import org.poly2tri.triangulation.TriangulationAlgorithm; -import org.poly2tri.triangulation.TriangulationPoint; -import org.poly2tri.triangulation.delaunay.DelaunayTriangle; -import org.poly2tri.triangulation.point.TPoint; -import org.poly2tri.triangulation.sets.ConstrainedPointSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigDecimal; -import java.math.MathContext; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -public class LayerPoly2Tri implements LayerDelaunay { - // Precision - private MathContext mathContext = MathContext.DECIMAL64; - private static final Logger LOGGER = LoggerFactory.getLogger(LayerPoly2Tri.class); - - private double r(double v) { - return new BigDecimal(v).round(mathContext).doubleValue(); - } - - private Map pts = new HashMap(); - private List segments = new ArrayList(pts.size()); - private AtomicInteger pointsCount = new AtomicInteger(0); - private PointHandler pointHandler = new PointHandler(this, pts, pointsCount); - private LineStringHandler lineStringHandler = new LineStringHandler(this, pts, pointsCount, segments); - private List vertices = new ArrayList(); - private HashMap buildingWithID = new HashMap(); - - private boolean computeNeighbors = false; - private List triangles = new ArrayList(); - private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i - private HashMap> hashOfArrayIndex = new HashMap>(); - private double maxArea = 0; - - private GeometryFactory factory = new GeometryFactory(); - - private static Coordinate TPointToCoordinate(TriangulationPoint tPoint) { - return new Coordinate(tPoint.getX(), tPoint.getY(), tPoint.getZ()); - } - - private static TPoint CoordinateToTPoint(Coordinate coordinate) { - return new PointWithAttribute(coordinate.x, coordinate.y, coordinate.z, -1); - } - - private static TPoint CoordinateToTPoint(Coordinate coordinate, int attribute) { - return new PointWithAttribute(coordinate.x, coordinate.y, coordinate.z, attribute); - } - - private static final class BuildingWithID { - private Polygon building; - - - public BuildingWithID(Polygon building) { - this.building = building; - - } - - - public boolean isTriangleInBuilding(TPoint point) { - return this.building.intersects(new GeometryFactory().createPoint(TPointToCoordinate(point))); - } - - - } - - private int getOrAppendVertices(Coordinate newCoord, List vertices, HashMap> hashOfArrayIndex) { - // We can obtain the same hash with two different coordinate (4 Bytes or - // 8 Bytes against 12 or 24 Bytes) , then we use a list as the value of - // the hashmap - // First step - Search the vertice parameter within the hashMap - int newCoordIndex = -1; - Integer coordinateHash = newCoord.hashCode(); - LinkedList listOfIndex = hashOfArrayIndex.get(coordinateHash); - if (listOfIndex != null) // There are the same hash value - { - for (int vind : listOfIndex) // Loop inside the coordinate index - { - if (newCoord.equals3D(vertices.get(vind))) // the coordinate is - // equal to the - // existing - // coordinate - { - newCoordIndex = vind; - break; // Exit for loop - } - } - if (newCoordIndex == -1) { - // No vertices has been found, we push the new coordinate into - // the existing linked list - newCoordIndex = vertices.size(); - listOfIndex.add(newCoordIndex); - vertices.add(newCoord); - } - } else { - // Push a new hash element - listOfIndex = new LinkedList(); - newCoordIndex = vertices.size(); - listOfIndex.add(newCoordIndex); - vertices.add(newCoord); - hashOfArrayIndex.put(coordinateHash, listOfIndex); - } - return newCoordIndex; - } - - private static int getPointAttribute(TriangulationPoint p) { - if (p instanceof PointWithAttribute) { - return ((PointWithAttribute) p).getAttribute(); - } else { - return -1; - } - } - - private static int getTriangleIndex(Map index, DelaunayTriangle instance) { - if (instance == null) { - return -1; - } else { - Integer res = index.get(instance); - if (res == null) { - return -1; - } else { - return res; - } - } - } - - @Override - public void processDelaunay() throws LayerDelaunayError { - triangles.clear(); - neighbors.clear(); - vertices.clear(); - hashOfArrayIndex.clear(); - - // Create input data for Poly2Tri - int[] index = new int[segments.size()]; - for (int i = 0; i < index.length; i++) { - index[i] = segments.get(i); - } - // Construct final points array by reversing key,value of hash map - TriangulationPoint[] ptsArray = new TriangulationPoint[pointsCount.get()]; - for(Map.Entry entry : pts.entrySet()) { - ptsArray[entry.getValue()] = entry.getKey(); - } - - List meshPoints = new ArrayList<>(Arrays.asList(ptsArray)); - ConstrainedPointSet convertedInput = new ConstrainedPointSet(meshPoints, index); - - - STRtree buildingsRtree = null; - if(maxArea > 0) { - // STRtree minimal size is 2 - buildingsRtree = new STRtree(Math.max(10, buildingWithID.size())); - for (Map.Entry buildingWithIDEntry : buildingWithID.entrySet()) { - buildingsRtree.insert(buildingWithIDEntry.getValue().building.getEnvelopeInternal(), buildingWithIDEntry.getKey()); - } - } - - boolean refine; - do { - // Triangulate - Poly2Tri.triangulate(TriangulationAlgorithm.DTSweep, convertedInput); - refine = false; - - // Will triangulate multiple time if refinement is necessary - if(buildingsRtree != null && maxArea > 0) { - List trianglesDelaunay = convertedInput.getTriangles(); - for (DelaunayTriangle triangle : trianglesDelaunay) { - if(triangle.area() > maxArea) { - // Insert steiner point in centroid - TPoint centroid = triangle.centroid(); - // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(TPointToCoordinate(centroid)); - searchEnvelope.expandBy(1.); - List polyInters = buildingsRtree.query(searchEnvelope); - boolean inBuilding = false; - for (Object id : polyInters) { - if (id instanceof Integer) { - BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(TPointToCoordinate(centroid)))) { - inBuilding = true; - break; - } - } - } - if(!inBuilding) { - meshPoints.add(centroid); - refine = true; - } - } - } - if(refine) { - convertedInput = new ConstrainedPointSet(meshPoints, index); - } - } - } while (refine); - - - List trianglesDelaunay = convertedInput.getTriangles(); - List triangleAttribute = Arrays.asList(new Integer[trianglesDelaunay.size()]); - // Create an index of triangles instance for fast neighbors search - Map triangleSearch = new HashMap<>(trianglesDelaunay.size()); - int triangleIndex = 0; - if (computeNeighbors) { - for (DelaunayTriangle triangle : trianglesDelaunay) { - triangleSearch.put(triangle, triangleIndex); - triangleIndex++; - } - } - - //Build ArrayList for binary search - //test add height - int triangleId = 0; - for (DelaunayTriangle triangle : trianglesDelaunay) { - Coordinate[] ring = new Coordinate[]{TPointToCoordinate(triangle.points[0]), TPointToCoordinate(triangle.points[1]), TPointToCoordinate(triangle.points[2]), TPointToCoordinate(triangle.points[0])}; - //if one of three vertices have buildingID and buildingID>=1 - if (getPointAttribute(triangle.points[0]) >= 1 || getPointAttribute(triangle.points[1]) >= 1 || getPointAttribute(triangle.points[2]) >= 1) { - int propertyBulidingID = 0; - for (int i = 0; i <= 2; i++) { - int potentialBuildingID = getPointAttribute(triangle.points[i]); - if (potentialBuildingID >= 1) { - //get the Barycenter of the triangle so we can sure this point is in this triangle and we will check if the building contain this point - if (this.buildingWithID.get(potentialBuildingID).isTriangleInBuilding(triangle.centroid())) { - propertyBulidingID = potentialBuildingID; - break; - } - } - } - triangleAttribute.set(triangleId, propertyBulidingID); - } else { - //if there are less than 3 points have buildingID this triangle is out of building - triangleAttribute.set(triangleId, 0); - } - - if (!Orientation.isCCW(ring)) { - Coordinate tmp = new Coordinate(ring[0]); - ring[0] = ring[2]; - ring[2] = tmp; - } - - int a = getOrAppendVertices(ring[0], vertices, hashOfArrayIndex); - int b = getOrAppendVertices(ring[1], vertices, hashOfArrayIndex); - int c = getOrAppendVertices(ring[2], vertices, hashOfArrayIndex); - triangles.add(new Triangle(a, b, c, triangleAttribute.get(triangleId))); - if (computeNeighbors) { - // Compute neighbors index - neighbors.add(new Triangle(getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[0])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[1])), getTriangleIndex(triangleSearch, triangle.neighborAcross(triangle.points[2])))); - } - triangleId++; - } - } - - - public static final class SetZFilter implements CoordinateSequenceFilter { - private boolean done = false; - private boolean resetToZero = false; - - public SetZFilter() { - - } - - public SetZFilter(boolean resetToZero) { - this.resetToZero = resetToZero; - } - - @Override - public void filter(CoordinateSequence seq, int i) { - double x = seq.getX(i); - double y = seq.getY(i); - double z = seq.getOrdinate(i, 2); - seq.setOrdinate(i, 0, x); - seq.setOrdinate(i, 1, y); - if (Double.isNaN(z) || resetToZero) { - seq.setOrdinate(i, 2, 0); - } else { - seq.setOrdinate(i, 2, z); - } - if (i == seq.size()) { - done = true; - } - } - - @Override - public boolean isDone() { - return done; - } - - @Override - public boolean isGeometryChanged() { - return true; - } - } - - /** - * Add height of building - * - * @return - */ - @Override - public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayError { - - //// To avoid errors we set the Z coordinate to 0. - SetZFilter zFilter = new SetZFilter(); - newPoly.apply(zFilter); - GeometryFactory factory = new GeometryFactory(); - final Coordinate[] coordinates = newPoly.getExteriorRing().getCoordinates(); - if (coordinates.length > 1) { - LineString newLineString = factory.createLineString(coordinates); - this.addLineString(newLineString, buildingId); - this.buildingWithID.put(buildingId, new BuildingWithID(newPoly)); - } - // Append holes - final int holeCount = newPoly.getNumInteriorRing(); - for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) { - LineString holeLine = newPoly.getInteriorRingN(holeIndex); - // Convert hole into a polygon, then compute an interior point - Polygon polyBuffnew = factory.createPolygon(factory.createLinearRing(holeLine.getCoordinates()), null); - if (polyBuffnew.getArea() > 0.) { - Coordinate interiorPoint = polyBuffnew.getInteriorPoint().getCoordinate(); - if (!factory.createPoint(interiorPoint).intersects(holeLine)) { - this.addLineString(holeLine, buildingId); - } else { - LOGGER.info("Warning : hole rejected, can't find interior point."); - } - } else { - LOGGER.info("Warning : hole rejected, area=0"); - } - } - } - - @Override - public void setMinAngle(Double minAngle) { - // TODO Auto-generated method stub - - } - - @Override - public void hintInit(Envelope bBox, long polygonCount, long verticesCount) throws LayerDelaunayError { - } - - @Override - public List getVertices() throws LayerDelaunayError { - return this.vertices; - } - - @Override - public List getTriangles() throws LayerDelaunayError { - return this.triangles; - } - - @Override - public void addVertex(Coordinate vertexCoordinate) throws LayerDelaunayError { - pointHandler.addPt(vertexCoordinate, -1); - } - - @Override - public void setMaxArea(Double maxArea) throws LayerDelaunayError { - this.maxArea = Math.max(0, maxArea); - } - - //add buildingID to edge property and to points property - public void addLineString(LineString lineToProcess, int buildingID) throws LayerDelaunayError { - lineStringHandler.reset(); - lineStringHandler.setAttribute(buildingID); - lineToProcess.apply(lineStringHandler); - } - //add buildingID to edge property and to points property - - @Override - public void reset() { - // TODO Auto-generated method stub - - } - - @Override - public List getNeighbors() throws LayerDelaunayError { - if (computeNeighbors) { - return neighbors; - } else { - throw new LayerDelaunayError("You must call setRetrieveNeighbors(True) before process delaunay triangulation"); - } - } - - @Override - public void setRetrieveNeighbors(boolean retrieve) { - this.computeNeighbors = retrieve; - - } - - - private static class PointHandler implements CoordinateFilter { - private LayerPoly2Tri delaunayData; - private Map pts; - private AtomicInteger maxIndex; - - public PointHandler(LayerPoly2Tri delaunayData, Map pts, AtomicInteger maxIndex) { - this.delaunayData = delaunayData; - this.pts = pts; - this.maxIndex = maxIndex; - } - - public Coordinate[] getPoints() { - Coordinate[] ret = new Coordinate[pts.size()]; - int i = 0; - for (TriangulationPoint pt : pts.keySet()) { - ret[i] = TPointToCoordinate(pt); - i++; - } - return ret; - } - - protected int addPt(Coordinate coordinate, int attribute) { - TPoint pt = new PointWithAttribute(delaunayData.r(coordinate.x), delaunayData.r(coordinate.y), Double.isNaN(coordinate.z) ? 0 : delaunayData.r(coordinate.z), attribute); - Integer index = pts.get(pt); - if (index == null) { - index = maxIndex.getAndAdd(1); - pts.put(pt, index); - } - return index; - } - - @Override - public void filter(Coordinate pt) { - addPt(pt, -1); - } - } - - private static final class LineStringHandler extends PointHandler { - private List segments; - private int firstPtIndex = -1; - private int attribute = -1; - - public LineStringHandler(LayerPoly2Tri delaunayData, Map pts, AtomicInteger maxIndex, List segments) { - super(delaunayData, pts, maxIndex); - this.segments = segments; - } - - /** - * New line string - */ - public void reset() { - firstPtIndex = -1; - attribute = -1; - } - - public void setAttribute(int attribute) { - this.attribute = attribute; - } - - @Override - public void filter(Coordinate pt) { - if (firstPtIndex == -1) { - firstPtIndex = addPt(pt, attribute); - } else { - int secondPt = addPt(pt, attribute); - if (secondPt != firstPtIndex) { - segments.add(firstPtIndex); - segments.add(secondPt); - firstPtIndex = secondPt; - } - } - } - } - - /** - * Points instance are kept by poly2tri, so define our own point instance in order to retrieve attributes - */ - private static final class PointWithAttribute extends PolygonPoint { - int attribute; - - public PointWithAttribute(double x, double y, int attribute) { - super(x, y); - this.attribute = attribute; - } - - public PointWithAttribute(double x, double y, double z, int attribute) { - super(x, y, z); - this.attribute = attribute; - } - - public int getAttribute() { - return attribute; - } - - public void setAttribute(int attribute) { - this.attribute = attribute; - } - } -} diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java index 5b6e4699f..dbf43edbe 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java @@ -467,7 +467,7 @@ public void finishPolygonFeeding(Geometry boundingBoxGeom) throws LayerDelaunayE CoordinateSequenceFilter absoluteCoordinateSequenceFilter; if(resetBuildingsZGround) { // set buildings Z coordinates to 0 m - absoluteCoordinateSequenceFilter = new LayerPoly2Tri.SetZFilter(true); + absoluteCoordinateSequenceFilter = new LayerTinfour.SetZFilter(true); } else { absoluteCoordinateSequenceFilter = new ComputeRays.AbsoluteCoordinateSequenceFilter(fastObstructionTest, resetBuildingsZGround); } diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestWallReflection.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestWallReflection.java index 678db75d0..2290c37ed 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestWallReflection.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestWallReflection.java @@ -338,7 +338,7 @@ public void testPath() throws ParseException, LayerDelaunayError { Coordinate source = new Coordinate(316900.8845049501,6703903.754851485, 0.05); List walls = data.freeFieldFinder.getLimitsInRange( data.maxRefDist, source, false); - assertEquals(12, walls.size()); + assertEquals(13, walls.size()); List paths; paths = computeRays.computeReflexion(receiver, source, false, walls); assertEquals(1, paths.size()); From 2dae3f1ffc4b67b39432c6be9282ae49451594e4 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Tue, 13 Apr 2021 09:29:16 +0200 Subject: [PATCH 06/18] Fix maven repo and lib version --- h2gis-extension/pom.xml | 2 +- noisemodelling-emission/pom.xml | 2 +- noisemodelling-jdbc/pom.xml | 2 +- noisemodelling-pathfinder/pom.xml | 2 +- noisemodelling-propagation/pom.xml | 2 +- noisemodelling-tutorial-01/pom.xml | 2 +- pom.xml | 2 +- wps_scripts/build.gradle | 12 ++++++------ 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/h2gis-extension/pom.xml b/h2gis-extension/pom.xml index 907fbd8b4..e58f23688 100644 --- a/h2gis-extension/pom.xml +++ b/h2gis-extension/pom.xml @@ -10,7 +10,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Additional drivers and functions for H2GIS database diff --git a/noisemodelling-emission/pom.xml b/noisemodelling-emission/pom.xml index 6f3ffe9bf..e49e9dbfa 100644 --- a/noisemodelling-emission/pom.xml +++ b/noisemodelling-emission/pom.xml @@ -10,7 +10,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Translate light vehicle, heavy vehicle, public bus, tramway traffic into linear noise source. Then compute sound propagation and iso surface. diff --git a/noisemodelling-jdbc/pom.xml b/noisemodelling-jdbc/pom.xml index ff3baef71..812a2971d 100644 --- a/noisemodelling-jdbc/pom.xml +++ b/noisemodelling-jdbc/pom.xml @@ -10,7 +10,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Compute sound propagation rays. diff --git a/noisemodelling-pathfinder/pom.xml b/noisemodelling-pathfinder/pom.xml index 849b9688d..d3b9acc56 100644 --- a/noisemodelling-pathfinder/pom.xml +++ b/noisemodelling-pathfinder/pom.xml @@ -10,7 +10,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Compute sound propagation rays. diff --git a/noisemodelling-propagation/pom.xml b/noisemodelling-propagation/pom.xml index 63227101f..3c5e71d51 100644 --- a/noisemodelling-propagation/pom.xml +++ b/noisemodelling-propagation/pom.xml @@ -10,7 +10,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Compute sound propagation rays. diff --git a/noisemodelling-tutorial-01/pom.xml b/noisemodelling-tutorial-01/pom.xml index cc450fd32..edb482e5a 100644 --- a/noisemodelling-tutorial-01/pom.xml +++ b/noisemodelling-tutorial-01/pom.xml @@ -11,7 +11,7 @@ org.orbisgis noisemodelling-parent - 3.3.1 + 3.3.2-SNAPSHOT ../pom.xml Test case with OpenStreetMap buildings and roads diff --git a/pom.xml b/pom.xml index ce5e59eb6..c00ab90a4 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ noisemodelling-parent noisemodelling-parent org.orbisgis - 3.3.1 + 3.3.2-SNAPSHOT NoiseModelling is an extension of H2GIS Lab-STICC - UMR CNRS 6285 diff --git a/wps_scripts/build.gradle b/wps_scripts/build.gradle index 16a878f8c..36a783f48 100644 --- a/wps_scripts/build.gradle +++ b/wps_scripts/build.gradle @@ -19,7 +19,7 @@ repositories { url "http://nexus.orbisgis.org/repository/orbisgis-snapshot/" } maven { - url "https://dl.bintray.com/matsim/matsim" + url "https://repo.matsim.org/repository/matsim/" } mavenCentral() } @@ -49,11 +49,11 @@ dependencies { compile('org.locationtech.jts:jts-io:1.15.1') { force=true } - compile group: 'org.orbisgis', name: 'noisemodelling-emission', version: '3.3.1' - compile group: 'org.orbisgis', name: 'noisemodelling-propagation', version: '3.3.1' - compile group: 'org.orbisgis', name: 'noisemodelling-pathfinder', version: '3.3.1' - compile group: 'org.orbisgis', name: 'noisemodelling-jdbc', version: '3.3.1' - compile group: 'org.orbisgis', name: 'h2gis-extension', version: '3.3.1' + compile group: 'org.orbisgis', name: 'noisemodelling-emission', version: '3.3.2-SNAPSHOT' + compile group: 'org.orbisgis', name: 'noisemodelling-propagation', version: '3.3.2-SNAPSHOT' + compile group: 'org.orbisgis', name: 'noisemodelling-pathfinder', version: '3.3.2-SNAPSHOT' + compile group: 'org.orbisgis', name: 'noisemodelling-jdbc', version: '3.3.2-SNAPSHOT' + compile group: 'org.orbisgis', name: 'h2gis-extension', version: '3.3.2-SNAPSHOT' compile group: 'org.openstreetmap.osmosis', name: 'osmosis-core', version: '0.48.3' compile group: 'org.openstreetmap.osmosis', name: 'osmosis-pbf', version: '0.46' compile group: 'org.matsim', name: 'matsim', version: '11.0' From 0768233ccd1f9b5ee4403f48ad8243e85119d81d Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Tue, 13 Apr 2021 10:57:56 +0200 Subject: [PATCH 07/18] use centroid instead of circumcentre --- .../noisemodelling/pathfinder/LayerTinfour.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 7ec6925e6..467cc886f 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -137,25 +137,23 @@ public void processDelaunay() throws LayerDelaunayError { for (SimpleTriangle triangle : simpleTriangles) { if(triangle.getArea() > maxArea) { // Insert steiner point in circumcircle - Coordinate circumcentre = org.locationtech.jts.geom.Triangle.circumcentre(toCoordinate(triangle.getVertexA()), - toCoordinate(triangle.getVertexB()), - toCoordinate(triangle.getVertexC())); + Coordinate centroid = getCentroid(triangle); // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(circumcentre); + Envelope searchEnvelope = new Envelope(centroid); searchEnvelope.expandBy(1.); List polyInters = buildingsRtree.query(searchEnvelope); boolean inBuilding = false; for (Object id : polyInters) { if (id instanceof Integer) { LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(circumcentre))) { + if (inPoly.building.contains(factory.createPoint(centroid))) { inBuilding = true; break; } } } if(!inBuilding) { - meshPoints.add(new Vertex(circumcentre.x, circumcentre.y, circumcentre.z)); + meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z)); refine = true; } } From 523303dbfbe0ddcc122abeb7357bd1b594d2f6c5 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Tue, 13 Apr 2021 16:28:39 +0200 Subject: [PATCH 08/18] Remove usage of meshbuilder in trianglenoisemap --- .../noisemodelling/jdbc/JdbcNoiseMap.java | 16 +++- .../noisemodelling/jdbc/TriangleNoiseMap.java | 96 +++++++------------ .../jdbc/PointNoiseMapTest.java | 26 +++++ .../pathfinder/LayerTinfour.java | 28 +++--- .../pathfinder/MeshBuilder.java | 2 +- 5 files changed, 89 insertions(+), 79 deletions(-) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java index 04871c3f5..e643f867e 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java @@ -197,7 +197,16 @@ protected void fetchCellSoilAreas(Connection connection, Envelope fetchEnvelope, } } + void fetchCellBuildings(Connection connection, Envelope fetchEnvelope, MeshBuilder mesh) throws SQLException { + ArrayList buildings = new ArrayList<>(); + fetchCellBuildings(connection, fetchEnvelope, buildings); + for(MeshBuilder.PolygonWithHeight building : buildings) { + mesh.addGeometry(building); + } + } + + void fetchCellBuildings(Connection connection, Envelope fetchEnvelope, List buildings) throws SQLException { Geometry envGeo = geometryFactory.toGeometry(fetchEnvelope); boolean fetchAlpha = JDBCUtilities.hasField(connection, buildingsTableName, alphaFieldName); String additionalQuery = ""; @@ -250,11 +259,14 @@ void fetchCellBuildings(Connection connection, Envelope fetchEnvelope, MeshBuild alphaList.add(MeshBuilder.getWallAlpha(oldAlpha, freq)); } } - MeshBuilder.PolygonWithHeight poly = mesh.addGeometry(intersectedGeometry, - heightField.isEmpty() ? Double.MAX_VALUE : rs.getDouble(heightField), alphaList); + + MeshBuilder.PolygonWithHeight poly = new MeshBuilder.PolygonWithHeight(intersectedGeometry, + heightField.isEmpty() ? Double.MAX_VALUE : rs.getDouble(heightField), + alphaList); if(columnIndex != 0) { poly.setPrimaryKey(rs.getInt(columnIndex)); } + buildings.add(poly); } } } diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java index 69810c564..d546a652f 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java @@ -3,8 +3,16 @@ import org.h2gis.utilities.JDBCUtilities; import org.h2gis.utilities.SFSUtilities; import org.h2gis.utilities.TableLocation; -import org.locationtech.jts.densify.Densifier; -import org.locationtech.jts.geom.*; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Envelope; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryCollection; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiLineString; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.TopologyException; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.operation.buffer.BufferOp; import org.locationtech.jts.operation.buffer.BufferParameters; @@ -33,10 +41,8 @@ */ public class TriangleNoiseMap extends JdbcNoiseMap { private static final int BATCH_MAX_SIZE = 100; - private final static double BUILDING_BUFFER = 0.5; private Logger logger = LoggerFactory.getLogger(TriangleNoiseMap.class); private double roadWidth = 2; - private double sourceDensification = 4; private double maximumArea = 75; private long nbreceivers = 0; private double receiverHeight = 1.6; @@ -67,15 +73,15 @@ public void setBuildingBuffer(double buildingBuffer) { } private void explodeAndAddPolygon(Geometry intersectedGeometry, - MeshBuilder delaunayTool) + LayerDelaunay delaunayTool) throws LayerDelaunayError { if (intersectedGeometry instanceof GeometryCollection) { for (int j = 0; j < intersectedGeometry.getNumGeometries(); j++) { Geometry subGeom = intersectedGeometry.getGeometryN(j); explodeAndAddPolygon(subGeom, delaunayTool); } - } else { - delaunayTool.addGeometry(intersectedGeometry); + } else if(intersectedGeometry instanceof Polygon && !intersectedGeometry.isEmpty()){ + delaunayTool.addPolygon((Polygon)intersectedGeometry, 1); } } @@ -90,9 +96,9 @@ private Geometry merge(LinkedList toUnite, double bufferSize) { return bufferOp.getResultGeometry(bufferSize); } - private void feedDelaunay(Collection buildings, MeshBuilder delaunayTool, Envelope boundingBoxFilter, + private void feedDelaunay(List buildings, LayerDelaunay delaunayTool, Envelope boundingBoxFilter, double srcDistance, LinkedList delaunaySegments, double minRecDist, - double srcPtDist, double triangleSide, double buildingBuffer) throws LayerDelaunayError { + double buildingBuffer) throws LayerDelaunayError { Envelope extendedEnvelope = new Envelope(boundingBoxFilter); extendedEnvelope.expandBy(srcDistance * 2.); Geometry linearRing = geometryFactory.toGeometry(boundingBoxFilter); @@ -104,9 +110,9 @@ private void feedDelaunay(Collection buildings, MeshBuilder delaunayTo Envelope fetchBox = new Envelope(boundingBoxFilter); fetchBox.expandBy(buildingBuffer); Geometry fetchGeometry = geometryFactory.toGeometry(fetchBox); - for(Geometry building : buildings) { - if(building.intersects(fetchGeometry)) { - toUnite.add(building); + for(MeshBuilder.PolygonWithHeight building : buildings) { + if(building.getGeometry().intersects(fetchGeometry)) { + toUnite.add(building.getGeometry()); } } // Reduce small artifacts to avoid, shortest geometry to be @@ -116,10 +122,6 @@ private void feedDelaunay(Collection buildings, MeshBuilder delaunayTo Geometry bufferBuildings = merge(toUnite, buildingBuffer); bufferBuildings = TopologyPreservingSimplifier.simplify(bufferBuildings, minRecDist / 2); - // Remove small artifacts due to buildingsTableName buffer - if(triangleSide > 0) { - bufferBuildings = Densifier.densify(bufferBuildings, triangleSide); - } toUniteFinal.add(bufferBuildings); // Add buildingsTableName to triangulation } Geometry geom1 = geometryFactory.createPolygon(); @@ -134,17 +136,7 @@ private void feedDelaunay(Collection buildings, MeshBuilder delaunayTo // Remove small artifacts due to multiple buffer crosses bufferRoads = TopologyPreservingSimplifier.simplify(bufferRoads, minRecDist / 2); - // Densify roads to set more receiver near roads. - if(srcPtDist > 0){ - bufferRoads = Densifier.densify(bufferRoads, srcPtDist); - } else if (triangleSide > 0) { - bufferRoads = Densifier.densify(bufferRoads, triangleSide); - } - //Add points buffer to the final triangulation, this will densify sound level extraction near - //toUniteFinal.add(makeBufferSegmentsNearRoads(toUniteRoads,srcPtDist)); - //roads, and helps to reduce over estimation due to inappropriate interpolation. toUniteFinal.add(bufferRoads); // Merge roads with minRecDist m - // buffer } } Geometry union = merge(toUniteFinal, 0.); // Merge roads and buildingsTableName @@ -171,13 +163,12 @@ private void feedDelaunay(Collection buildings, MeshBuilder delaunayTo * @param cellJ J cell index * @param maxSrcDist Maximum propagation distance * @param minRecDist Minimal distance receiver-source - * @param srcPtDist Densification distance of sources pts * @param maximumArea Maximum area of triangles * @throws LayerDelaunayError */ - public void computeDelaunay(MeshBuilder cellMesh, + public void computeDelaunay(LayerDelaunay cellMesh, Envelope mainEnvelope, int cellI, int cellJ, double maxSrcDist, Collection sources, - double minRecDist, double srcPtDist, double maximumArea, double buildingBuffer) + double minRecDist, double maximumArea, double buildingBuffer, List buildings) throws LayerDelaunayError { Envelope cellEnvelope = getCellEnv(mainEnvelope, cellI, cellJ, @@ -199,7 +190,7 @@ public void computeDelaunay(MeshBuilder cellMesh, if (ptEnv.intersects(expandedCellEnvelop)) { if (pt instanceof Point) { // Add square in rendering - cellMesh.addGeometry(cellEnvelopeGeometry.intersection(pt.buffer(minRecDist, BufferParameters.CAP_SQUARE))); + cellMesh.addPolygon((Polygon)cellEnvelopeGeometry.intersection(pt.buffer(minRecDist, BufferParameters.CAP_SQUARE)), 1); } else { if (pt instanceof LineString) { delaunaySegments.add((LineString) (pt)); @@ -215,25 +206,19 @@ public void computeDelaunay(MeshBuilder cellMesh, } } - // Compute equilateral triangle side from Area - double triangleSide = (2*Math.pow(maximumArea, 0.5)) / Math.pow(3, 0.25); - List buildings = new ArrayList<>(cellMesh.getPolygonWithHeight().size()); - for(MeshBuilder.PolygonWithHeight poly : cellMesh.getPolygonWithHeight()) { - buildings.add(poly.getGeometry()); - } - cellMesh.clearBuildings(); feedDelaunay(buildings, cellMesh, cellEnvelope, maxSrcDist, delaunaySegments, - minRecDist, srcPtDist, triangleSide, buildingBuffer); + minRecDist, buildingBuffer); // Process delaunay logger.info("Begin delaunay"); - cellMesh.setComputeNeighbors(false); + cellMesh.setRetrieveNeighbors(false); + // Add cell envelope + cellMesh.addPolygon((Polygon)(new GeometryFactory().toGeometry(cellEnvelope)), 0); if (maximumArea > 1) { - cellMesh.setMaximumArea(maximumArea); - Geometry densifiedEnvelope = Densifier.densify(new GeometryFactory().toGeometry(cellEnvelope), triangleSide); - cellMesh.finishPolygonFeeding(densifiedEnvelope); + cellMesh.setMaxArea(maximumArea); + cellMesh.processDelaunay(); } else { - cellMesh.finishPolygonFeeding(cellEnvelope); + cellMesh.processDelaunay(); } logger.info("End delaunay"); } @@ -249,7 +234,6 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin // vertices of neighbor cells at the borders // then, there are discontinuities in iso surfaces at each // border of cell - MeshBuilder cellMesh = new MeshBuilder(); Envelope cellEnvelope = getCellEnv(mainEnvelope, cellI, cellJ, getCellWidth(), getCellHeight()); // Fetch all source located in expandedCellEnvelop @@ -257,21 +241,17 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin fetchCellSource(connection, cellEnvelope, data); List sourceDelaunayGeometries = data.sourceGeometries; - fetchCellBuildings(connection, cellEnvelope, cellMesh); - MeshBuilder demMesh = new MeshBuilder(); + ArrayList buildings = new ArrayList<>(); + fetchCellBuildings(connection, cellEnvelope, buildings); + FastObstructionTest freeFieldFinder = null; - if(!demTable.isEmpty()) { - fetchCellDem(connection, cellEnvelope, demMesh); - demMesh.finishPolygonFeeding(cellEnvelope); - freeFieldFinder = new FastObstructionTest(demMesh.getPolygonWithHeight(), - demMesh.getTriangles(), demMesh.getTriNeighbors(), demMesh.getVertices()); - } + + LayerDelaunay cellMesh = new LayerTinfour(); try { computeDelaunay(cellMesh, mainEnvelope, cellI, cellJ, - maximumPropagationDistance, sourceDelaunayGeometries, roadWidth, - sourceDensification, maximumArea, buildingBuffer); + maximumPropagationDistance, sourceDelaunayGeometries, roadWidth, maximumArea, buildingBuffer, buildings); } catch (LayerDelaunayError err) { throw new SQLException(err.getLocalizedMessage(), err); } @@ -361,14 +341,6 @@ public void setRoadWidth(double roadWidth) { this.roadWidth = roadWidth; } - public double getSourceDensification() { - return sourceDensification; - } - - public void setSourceDensification(double sourceDensification) { - this.sourceDensification = sourceDensification; - } - public double getMaximumArea() { return maximumArea; } diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java index 92f160892..52f8261db 100644 --- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java +++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java @@ -2,6 +2,9 @@ import org.h2gis.api.EmptyProgressVisitor; import org.h2gis.functions.factory.H2GISDBFactory; +import org.h2gis.functions.io.shp.SHPRead; +import org.h2gis.functions.io.shp.SHPWrite; +import org.h2gis.functions.io.shp.internal.SHPDriver; import org.h2gis.utilities.SFSUtilities; import org.junit.After; import org.junit.Before; @@ -163,5 +166,28 @@ public void testNoiseMapBuilding() throws Exception { } + @Test + public void testNoiseMapBuilding2() throws Exception { + try(Statement st = connection.createStatement()) { + SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("roads_traff.shp").getFile(), "ROADS_GEOM"); + SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("buildings.shp").getFile(), " BUILDINGS"); + TriangleNoiseMap noisemap = new TriangleNoiseMap("BUILDINGS", "ROADS_GEOM"); + noisemap.setReceiverHasAbsoluteZCoordinates(false); + noisemap.setSourceHasAbsoluteZCoordinates(false); + noisemap.setHeightField("HEIGHT"); + noisemap.initialize(connection, new EmptyProgressVisitor()); + noisemap.setMaximumArea(300); + + AtomicInteger pk = new AtomicInteger(0); + for(int i=0; i < noisemap.getGridDim(); i++) { + for(int j=0; j < noisemap.getGridDim(); j++) { + noisemap.generateReceivers(connection, i, j, "NM_RECEIVERS", "TRIANGLES", pk); + } + } + assertNotSame(0, pk.get()); + SHPWrite.exportTable(connection, "target/triangle.shp", "TRIANGLES"); + } + } + } \ No newline at end of file diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 467cc886f..c936af670 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -171,20 +171,20 @@ public void processDelaunay() throws LayerDelaunayError { for(SimpleTriangle t : simpleTriangles) { int triangleAttribute = 0; // Insert steiner point in centroid - Coordinate centroid = getCentroid(t); - // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(centroid); - searchEnvelope.expandBy(1.); - List polyInters = buildingsRtree.query(searchEnvelope); - for (Object id : polyInters) { - if (id instanceof Integer) { - LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(centroid))) { - triangleAttribute = (int) id; - break; - } - } - } +// Coordinate centroid = getCentroid(t); +// // Do not add steiner points into buildings +// Envelope searchEnvelope = new Envelope(centroid); +// searchEnvelope.expandBy(1.); +// List polyInters = buildingsRtree.query(searchEnvelope); +// for (Object id : polyInters) { +// if (id instanceof Integer) { +// LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); +// if (inPoly.building.contains(factory.createPoint(centroid))) { +// triangleAttribute = (int) id; +// break; +// } +// } +// } triangles.add(new Triangle(vertIndex.get(t.getVertexA()), vertIndex.get(t.getVertexB()),vertIndex.get(t.getVertexC()), triangleAttribute)); edgeIndexToTriangleIndex.put(t.getEdgeA().getIndex(), triangles.size() - 1); edgeIndexToTriangleIndex.put(t.getEdgeB().getIndex(), triangles.size() - 1); diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java index dbf43edbe..ffedf057f 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java @@ -275,7 +275,7 @@ public PolygonWithHeight addGeometry(Geometry obstructionPoly) { return addGeometry(new PolygonWithHeight(obstructionPoly)); } - private PolygonWithHeight addGeometry(PolygonWithHeight poly) { + public PolygonWithHeight addGeometry(PolygonWithHeight poly) { this.geometriesBoundingBox.expandToInclude(poly.getGeometry().getEnvelopeInternal()); polygonWithHeight.add(poly); return poly; From 75b0c2f7b0cba861f3d092c73a76f93c8943a187 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 11:13:26 +0200 Subject: [PATCH 09/18] test polygon holes --- .../noisemodelling/jdbc/TriangleNoiseMap.java | 18 +++++++------- .../jdbc/PointNoiseMapTest.java | 8 +++++-- .../pathfinder/LayerTinfourTest.java | 24 +++++++++++++++++++ 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java index d546a652f..a03a449cf 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java @@ -120,8 +120,8 @@ private void feedDelaunay(List buildings, LayerDe LinkedList toUniteFinal = new LinkedList<>(); if (!toUnite.isEmpty()) { Geometry bufferBuildings = merge(toUnite, buildingBuffer); - bufferBuildings = TopologyPreservingSimplifier.simplify(bufferBuildings, - minRecDist / 2); + //bufferBuildings = TopologyPreservingSimplifier.simplify(bufferBuildings, + // minRecDist / 2); toUniteFinal.add(bufferBuildings); // Add buildingsTableName to triangulation } Geometry geom1 = geometryFactory.createPolygon(); @@ -216,10 +216,8 @@ public void computeDelaunay(LayerDelaunay cellMesh, cellMesh.addPolygon((Polygon)(new GeometryFactory().toGeometry(cellEnvelope)), 0); if (maximumArea > 1) { cellMesh.setMaxArea(maximumArea); - cellMesh.processDelaunay(); - } else { - cellMesh.processDelaunay(); } + cellMesh.processDelaunay(); logger.info("End delaunay"); } @@ -229,6 +227,11 @@ protected Envelope getComputationEnvelope(Connection connection) throws SQLExcep } public void generateReceivers(Connection connection, int cellI, int cellJ, String receiverTableName, String trianglesTableName, AtomicInteger receiverPK) throws SQLException, LayerDelaunayError, IOException { + + int ij = cellI * gridDim + cellJ + 1; + if(verbose) { + logger.info("Begin processing of cell " + ij + " / " + gridDim * gridDim); + } // Compute the first pass delaunay mesh // The first pass doesn't take account of additional // vertices of neighbor cells at the borders @@ -245,8 +248,6 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin ArrayList buildings = new ArrayList<>(); fetchCellBuildings(connection, cellEnvelope, buildings); - FastObstructionTest freeFieldFinder = null; - LayerDelaunay cellMesh = new LayerTinfour(); try { computeDelaunay(cellMesh, mainEnvelope, cellI, @@ -267,9 +268,6 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin for(Coordinate vertex : cellMesh.getVertices()) { Coordinate translatedVertex = new Coordinate(vertex); double z = receiverHeight; - if(freeFieldFinder != null) { - z = freeFieldFinder.getHeightAtPosition(translatedVertex) + receiverHeight; - } translatedVertex.setOrdinate(2, z); vertices.add(translatedVertex); } diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java index 52f8261db..9b7d07ae4 100644 --- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java +++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java @@ -175,9 +175,13 @@ public void testNoiseMapBuilding2() throws Exception { noisemap.setReceiverHasAbsoluteZCoordinates(false); noisemap.setSourceHasAbsoluteZCoordinates(false); noisemap.setHeightField("HEIGHT"); - noisemap.initialize(connection, new EmptyProgressVisitor()); - noisemap.setMaximumArea(300); + noisemap.setMaximumArea(0); + noisemap.setBuildingBuffer(0); + noisemap.setMaximumPropagationDistance(800); + + + noisemap.initialize(connection, new EmptyProgressVisitor()); AtomicInteger pk = new AtomicInteger(0); for(int i=0; i < noisemap.getGridDim(); i++) { for(int j=0; j < noisemap.getGridDim(); j++) { diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index 73619abc7..633ec5a71 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -4,6 +4,10 @@ import org.junit.Test; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.io.ParseException; +import org.locationtech.jts.io.WKTReader; import java.util.List; @@ -70,4 +74,24 @@ public void testPolygonDelaunay1() throws LayerDelaunayError { assertEquals(10, triangleList.size()); } + @Test + public void testPolygonHole() throws ParseException, LayerDelaunayError { + WKTReader wktReader = new WKTReader(); + GeometryFactory factory = new GeometryFactory(); + Polygon[] polygons = { + (Polygon) wktReader.read("POLYGON((222677.439091769 6758514.42927814 0,222671.451152259 6758525.56045846 0,222666.53640143 6758534.70500279 0,222660.568467509 6758545.82623747 0,222680.275526724 6758556.23812336 0,222682.923201221 6758551.44635515 0,222687.825144212 6758553.96433641 0,222688.6946897 6758553.91607613 0,222689.594539629 6758553.69797565 0,222708.564257407 6758552.76566163 0,222718.050422367 6758536.28473477 0,222718.049359588 6758536.28416282 0,222715.538763539 6758534.93305632 0,222715.534664596 6758534.94067328 0,222713.49856221 6758538.72427529 0,222712.138657153 6758539.12136151 0,222705.459934079 6758535.53060206 0,222705.062847945 6758534.17068046 0,222707.088948575 6758530.38705905 0,222707.089519954 6758530.38599728 0,222696.179880457 6758524.51484693 0,222696.177391059 6758524.51947287 0,222694.151290324 6758528.30309409 0,222692.791365757 6758528.71018202 0,222686.102658034 6758525.11938618 0,222685.695570154 6758523.75946162 0,222687.731672806 6758519.97586009 0,222687.734820884 6758519.97003873 0,222677.439091769 6758514.42927814 0)))"), + (Polygon) wktReader.read("POLYGON((222718.050422367 6758536.28473477 0,222726.944985841 6758520.82200926 0,222711.291580801 6758512.32681897 0,222709.875972122 6758510.54519262 0,222707.961134851 6758508.53272968 0,222706.494573643 6758507.25068349 0,222704.937863906 6758506.07838239 0,222704.57854509 6758505.84781922 0,222704.889271526 6758505.37873581 0,222705.348389209 6758505.67944089 0,222706.541930339 6758503.47318075 0,222706.002942329 6758503.13234521 0,222704.775286101 6758502.34044378 0,222703.717046283 6758501.77873438 0,222702.848494026 6758501.3273335 0,222702.379276882 6758501.07656837 0,222701.091151516 6758500.54439877 0,222699.957711378 6758502.67082755 0,222700.416829049 6758502.97153264 0,222700.215942338 6758503.49080695 0,222698.837944834 6758502.91847076 0,222696.450902373 6758502.18426504 0,222693.773683978 6758501.63937878 0,222691.535579581 6758501.3951585 0,222690.536302091 6758501.35323118 0,222685.884034415 6758498.91570387 0,222684.890461927 6758501.07237841 0,222679.744696108 6758505.69935698 0,222678.532224271 6758512.47266004 0,222678.080905977 6758513.2912627 0,222677.439091769 6758514.42927814 0,222687.734820884 6758519.97003873 0,222689.77776067 6758516.19227811 0,222691.117714616 6758515.78515118 0,222697.816387942 6758519.38595153 0,222698.213493564 6758520.73587122 0,222696.179880457 6758524.51484693 0,222707.089519954 6758530.38599728 0,222709.125050975 6758526.60345721 0,222710.474970686 6758526.20635152 0,222717.163678848 6758529.79714675 0,222717.570750355 6758531.15707127 0,222715.538763539 6758534.93305632 0,222718.049359588 6758536.28416282 0,222718.050422367 6758536.28473477 0)))"), + (Polygon) wktReader.read("POLYGON((222705.696340536 6758557.74687277 0,222724.075919557 6758557.07325136 0,222740.531595117 6758528.34377304 0,222741.230128632 6758528.86480505 0,222743.686623899 6758524.74224218 0,222742.888076702 6758524.26098912 0,222744.171861162 6758521.90499592 0,222732.249628577 6758511.59826499 0,222726.944985841 6758520.82200926 0,222718.050422367 6758536.28473477 0,222708.564257407 6758552.76566163 0,222705.696340536 6758557.74687277 0))") + }; + Polygon merged = (Polygon)factory.createMultiPolygon(polygons).buffer(0); + System.out.println(merged.toString()); + LayerTinfour layerTinfour = new LayerTinfour(); + layerTinfour.setRetrieveNeighbors(true); + + layerTinfour.addPolygon(merged, 1); + + layerTinfour.processDelaunay(); + + List triangleList = layerTinfour.getTriangles(); + } } \ No newline at end of file From 9851f04b14e54fa1fbd6f99729ff350c645d81f5 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 16:42:24 +0200 Subject: [PATCH 10/18] Reword layertinfour in order to use PolygonContraint --- .../pathfinder/LayerTinfour.java | 164 ++++++++---------- .../pathfinder/LayerTinfourTest.java | 2 +- 2 files changed, 74 insertions(+), 92 deletions(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index c936af670..ba4f22a35 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -1,8 +1,7 @@ package org.noise_planet.noisemodelling.pathfinder; -import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.*; -import org.locationtech.jts.index.strtree.STRtree; +import org.locationtech.jts.index.quadtree.Quadtree; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tinfour.common.*; @@ -18,23 +17,24 @@ public class LayerTinfour implements LayerDelaunay { // Precision private MathContext mathContext = MathContext.DECIMAL64; + private double epsilon = 0.001; // merge of Vertex instances below this distance private static final Logger LOGGER = LoggerFactory.getLogger(LayerTinfour.class); private double r(double v) { return new BigDecimal(v).round(mathContext).doubleValue(); } - private Map pts = new HashMap(); - private List segments = new ArrayList(); - private AtomicInteger pointsCount = new AtomicInteger(0); - private LayerTinfour.PointHandler pointHandler = new LayerTinfour.PointHandler(this, pts, pointsCount); - private LayerTinfour.LineStringHandler lineStringHandler = new LayerTinfour.LineStringHandler(this, pts, pointsCount, segments); - private HashMap buildingWithID = new HashMap(); - private boolean computeNeighbors = false; - private double maxArea = 0; + //private Map pts = new HashMap(); + //private List segments = new ArrayList(); + List constraints = new ArrayList<>(); + List constraintIndex = new ArrayList<>(); - private GeometryFactory factory = new GeometryFactory(); + Quadtree ptsIndex = new Quadtree(); + //private LayerTinfour.PointHandler pointHandler = new LayerTinfour.PointHandler(this, pts, pointsCount); + //private LayerTinfour.LineStringHandler lineStringHandler = new LayerTinfour.LineStringHandler(this, pts, pointsCount, segments); + private boolean computeNeighbors = false; + private double maxArea = 0; // Output data private List vertices = new ArrayList(); @@ -45,12 +45,22 @@ private static Coordinate TPointToCoordinate(Vertex tPoint) { return new Coordinate(tPoint.getX(), tPoint.getY(), tPoint.getZ()); } - private static Vertex CoordinateToTPoint(Coordinate coordinate) { - return new Vertex(coordinate.x, coordinate.y, coordinate.z, -1); - } - - private static Vertex CoordinateToTPoint(Coordinate coordinate, int attribute) { - return new Vertex(coordinate.x, coordinate.y, coordinate.z, attribute); + private Vertex addCoordinate(Coordinate coordinate, int index) { + List result = ptsIndex.query(new Envelope(coordinate)); + Vertex found = null; + for(Object vertex : result) { + if(vertex instanceof Vertex) { + if(((Vertex) vertex).getDistance(coordinate.x, coordinate.y) < epsilon) { + found = (Vertex) vertex; + break; + } + } + } + if(found == null) { + found = new Vertex(coordinate.x, coordinate.y, coordinate.z, index); + ptsIndex.insert(new Envelope(coordinate), found); + } + return found; } private static final class BuildingWithID { @@ -97,23 +107,7 @@ public void processDelaunay() throws LayerDelaunayError { triangles.clear(); vertices.clear(); - // Create input data for Poly2Tri - int[] index = new int[segments.size()]; - for (int i = 0; i < index.length; i++) { - index[i] = segments.get(i); - } - // Construct final points array by reversing key,value of hash map - Vertex[] ptsArray = new Vertex[pointsCount.get()]; - for(Map.Entry entry : pts.entrySet()) { - ptsArray[entry.getValue()] = entry.getKey(); - } - - List meshPoints = new ArrayList<>(Arrays.asList(ptsArray)); - - STRtree buildingsRtree = new STRtree(Math.max(10, buildingWithID.size())); - for (Map.Entry buildingWithIDEntry : buildingWithID.entrySet()) { - buildingsRtree.insert(buildingWithIDEntry.getValue().building.getEnvelopeInternal(), buildingWithIDEntry.getKey()); - } + List meshPoints = ptsIndex.queryAll(); IncrementalTin tin; boolean refine; @@ -124,10 +118,6 @@ public void processDelaunay() throws LayerDelaunayError { // Add points tin.add(meshPoints, null); // Add constraints - List constraints = new ArrayList<>(segments.size()); - for(int i=0; i < segments.size(); i+=2) { - constraints.add(new LinearConstraint(meshPoints.get(segments.get(i)), meshPoints.get(segments.get(i+1)))); - } tin.addConstraints(constraints, maxArea > 0); refine = false; @@ -138,24 +128,8 @@ public void processDelaunay() throws LayerDelaunayError { if(triangle.getArea() > maxArea) { // Insert steiner point in circumcircle Coordinate centroid = getCentroid(triangle); - // Do not add steiner points into buildings - Envelope searchEnvelope = new Envelope(centroid); - searchEnvelope.expandBy(1.); - List polyInters = buildingsRtree.query(searchEnvelope); - boolean inBuilding = false; - for (Object id : polyInters) { - if (id instanceof Integer) { - LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); - if (inPoly.building.contains(factory.createPoint(centroid))) { - inBuilding = true; - break; - } - } - } - if(!inBuilding) { - meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z)); - refine = true; - } + meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z)); + refine = true; } } } @@ -170,21 +144,11 @@ public void processDelaunay() throws LayerDelaunayError { Map edgeIndexToTriangleIndex = new HashMap<>(); for(SimpleTriangle t : simpleTriangles) { int triangleAttribute = 0; - // Insert steiner point in centroid -// Coordinate centroid = getCentroid(t); -// // Do not add steiner points into buildings -// Envelope searchEnvelope = new Envelope(centroid); -// searchEnvelope.expandBy(1.); -// List polyInters = buildingsRtree.query(searchEnvelope); -// for (Object id : polyInters) { -// if (id instanceof Integer) { -// LayerTinfour.BuildingWithID inPoly = buildingWithID.get(id); -// if (inPoly.building.contains(factory.createPoint(centroid))) { -// triangleAttribute = (int) id; -// break; -// } -// } -// } + if(t.getContainingRegion() != null) { + if(t.getContainingRegion().getConstraintIndex() < constraintIndex.size()) { + triangleAttribute = constraintIndex.get(t.getContainingRegion().getConstraintIndex()); + } + } triangles.add(new Triangle(vertIndex.get(t.getVertexA()), vertIndex.get(t.getVertexB()),vertIndex.get(t.getVertexC()), triangleAttribute)); edgeIndexToTriangleIndex.put(t.getEdgeA().getIndex(), triangles.size() - 1); edgeIndexToTriangleIndex.put(t.getEdgeB().getIndex(), triangles.size() - 1); @@ -250,33 +214,33 @@ public boolean isGeometryChanged() { */ @Override public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayError { - - //// To avoid errors we set the Z coordinate to 0. + // To avoid errors we set NaN Z coordinates to 0. LayerTinfour.SetZFilter zFilter = new LayerTinfour.SetZFilter(); newPoly.apply(zFilter); GeometryFactory factory = new GeometryFactory(); final Coordinate[] coordinates = newPoly.getExteriorRing().getCoordinates(); if (coordinates.length > 1) { - LineString newLineString = factory.createLineString(coordinates); - this.addLineString(newLineString, buildingId); - this.buildingWithID.put(buildingId, new LayerTinfour.BuildingWithID(newPoly)); + List vertexList = new ArrayList<>(); + for(Coordinate coordinate : coordinates) { + vertexList.add(addCoordinate(coordinate, buildingId)); + } + PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); + constraints.add(polygonConstraint); + constraintIndex.add(buildingId); } // Append holes final int holeCount = newPoly.getNumInteriorRing(); for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) { LineString holeLine = newPoly.getInteriorRingN(holeIndex); - // Convert hole into a polygon, then compute an interior point - Polygon polyBuffnew = factory.createPolygon(factory.createLinearRing(holeLine.getCoordinates()), null); - if (polyBuffnew.getArea() > 0.) { - Coordinate interiorPoint = polyBuffnew.getInteriorPoint().getCoordinate(); - if (!factory.createPoint(interiorPoint).intersects(holeLine)) { - this.addLineString(holeLine, buildingId); - } else { - LOGGER.info("Warning : hole rejected, can't find interior point."); - } - } else { - LOGGER.info("Warning : hole rejected, area=0"); + final Coordinate[] hCoordinates = holeLine.getCoordinates(); + // Should be counter clock wise + List vertexList = new ArrayList<>(); + for(Coordinate coordinate : hCoordinates) { + vertexList.add(addCoordinate(coordinate, buildingId)); } + PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); + constraints.add(polygonConstraint); + constraintIndex.add(buildingId); } } @@ -306,7 +270,7 @@ private static Coordinate toCoordinate(Vertex v) { @Override public void addVertex(Coordinate vertexCoordinate) throws LayerDelaunayError { - pointHandler.addPt(vertexCoordinate, -1); + addCoordinate(vertexCoordinate, 0); } @Override @@ -316,9 +280,14 @@ public void setMaxArea(Double maxArea) throws LayerDelaunayError { //add buildingID to edge property and to points property public void addLineString(LineString lineToProcess, int buildingID) throws LayerDelaunayError { - lineStringHandler.reset(); - lineStringHandler.setAttribute(buildingID); - lineToProcess.apply(lineStringHandler); + Coordinate[] coordinates = lineToProcess.getCoordinates(); + List vertexList = new ArrayList<>(); + for(Coordinate coordinate : coordinates) { + vertexList.add(addCoordinate(coordinate, buildingID)); + } + LinearConstraint linearConstraint = new LinearConstraint(vertexList); + constraints.add(linearConstraint); + constraintIndex.add(buildingID); } //add buildingID to edge property and to points property @@ -389,6 +358,19 @@ public void filter(Coordinate pt) { } } + /** + * When defining a polygon with identifier, + * the zone marker is a coordinate in this polygon with an associated ID + */ + private static final class ZoneMarker { + public final Coordinate coordinate; + public final Integer index; + + public ZoneMarker(Coordinate coordinate, Integer index) { + this.coordinate = coordinate; + this.index = index; + } + } private static final class LineStringHandler extends LayerTinfour.PointHandler { private List segments; private int firstPtIndex = -1; diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index 633ec5a71..c3734c09a 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -88,7 +88,7 @@ public void testPolygonHole() throws ParseException, LayerDelaunayError { LayerTinfour layerTinfour = new LayerTinfour(); layerTinfour.setRetrieveNeighbors(true); - layerTinfour.addPolygon(merged, 1); + layerTinfour.addPolygon(merged, 55); layerTinfour.processDelaunay(); From 327fbe819fd26add90f99cffa913e433231c1ddd Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 16:54:50 +0200 Subject: [PATCH 11/18] fix vertices, cleaning --- .../pathfinder/LayerTinfour.java | 155 ++++-------------- 1 file changed, 28 insertions(+), 127 deletions(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index ba4f22a35..a7de67b85 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -1,5 +1,6 @@ package org.noise_planet.noisemodelling.pathfinder; +import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.*; import org.locationtech.jts.index.quadtree.Quadtree; import org.slf4j.Logger; @@ -8,31 +9,19 @@ import org.tinfour.standard.IncrementalTin; import org.tinfour.utils.TriangleCollector; -import java.math.BigDecimal; -import java.math.MathContext; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; public class LayerTinfour implements LayerDelaunay { - // Precision - private MathContext mathContext = MathContext.DECIMAL64; private double epsilon = 0.001; // merge of Vertex instances below this distance private static final Logger LOGGER = LoggerFactory.getLogger(LayerTinfour.class); - private double r(double v) { - return new BigDecimal(v).round(mathContext).doubleValue(); - } - //private Map pts = new HashMap(); //private List segments = new ArrayList(); List constraints = new ArrayList<>(); List constraintIndex = new ArrayList<>(); Quadtree ptsIndex = new Quadtree(); - //private LayerTinfour.PointHandler pointHandler = new LayerTinfour.PointHandler(this, pts, pointsCount); - //private LayerTinfour.LineStringHandler lineStringHandler = new LayerTinfour.LineStringHandler(this, pts, pointsCount, segments); - private boolean computeNeighbors = false; private double maxArea = 0; @@ -41,10 +30,6 @@ private double r(double v) { private List triangles = new ArrayList(); private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i - private static Coordinate TPointToCoordinate(Vertex tPoint) { - return new Coordinate(tPoint.getX(), tPoint.getY(), tPoint.getZ()); - } - private Vertex addCoordinate(Coordinate coordinate, int index) { List result = ptsIndex.query(new Envelope(coordinate)); Vertex found = null; @@ -63,15 +48,6 @@ private Vertex addCoordinate(Coordinate coordinate, int index) { return found; } - private static final class BuildingWithID { - private Polygon building; - - public BuildingWithID(Polygon building) { - this.building = building; - - } - } - private List computeTriangles(IncrementalTin incrementalTin) { ArrayList triangles = new ArrayList<>(incrementalTin.countTriangles().getCount()); TriangleBuilder triangleBuilder = new TriangleBuilder(triangles); @@ -79,6 +55,20 @@ private List computeTriangles(IncrementalTin incrementalTin) { return triangles; } + /** + * @return Merge vertices closer than specified epsilon + */ + public double getEpsilon() { + return epsilon; + } + + /** + * @param epsilon Merge vertices closer than specified epsilon + */ + public void setEpsilon(double epsilon) { + this.epsilon = epsilon; + } + private static class TriangleBuilder implements Consumer { ArrayList triangles; @@ -219,10 +209,14 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro newPoly.apply(zFilter); GeometryFactory factory = new GeometryFactory(); final Coordinate[] coordinates = newPoly.getExteriorRing().getCoordinates(); - if (coordinates.length > 1) { + // Exterior ring must be CCW + if(!Orientation.isCCW(coordinates)) { + CoordinateArrays.reverse(coordinates); + } + if (coordinates.length >= 4) { List vertexList = new ArrayList<>(); - for(Coordinate coordinate : coordinates) { - vertexList.add(addCoordinate(coordinate, buildingId)); + for(int vId = 0; vId < coordinates.length - 1 ; vId++) { + vertexList.add(addCoordinate(coordinates[vId], buildingId)); } PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); constraints.add(polygonConstraint); @@ -233,10 +227,14 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) { LineString holeLine = newPoly.getInteriorRingN(holeIndex); final Coordinate[] hCoordinates = holeLine.getCoordinates(); + // Exterior ring must be CW + if(Orientation.isCCW(hCoordinates)) { + CoordinateArrays.reverse(hCoordinates); + } // Should be counter clock wise List vertexList = new ArrayList<>(); - for(Coordinate coordinate : hCoordinates) { - vertexList.add(addCoordinate(coordinate, buildingId)); + for(int vId = 0; vId < hCoordinates.length - 1 ; vId++) { + vertexList.add(addCoordinate(hCoordinates[vId], buildingId)); } PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); constraints.add(polygonConstraint); @@ -311,101 +309,4 @@ public void setRetrieveNeighbors(boolean retrieve) { this.computeNeighbors = retrieve; } - - - private static class PointHandler implements CoordinateFilter { - private LayerTinfour delaunayData; - private Map pts; - private AtomicInteger maxIndex; - - public PointHandler(LayerTinfour delaunayData, Map pts, AtomicInteger maxIndex) { - this.delaunayData = delaunayData; - this.pts = pts; - this.maxIndex = maxIndex; - } - - public Coordinate[] getPoints() { - Coordinate[] ret = new Coordinate[pts.size()]; - int i = 0; - for (Vertex pt : pts.keySet()) { - ret[i] = TPointToCoordinate(pt); - i++; - } - return ret; - } - - protected int addVertex(Vertex pt) { - Integer index = pts.get(pt); - if (index == null) { - index = maxIndex.getAndAdd(1); - pts.put(pt, index); - } - return index; - } - protected int addPt(Coordinate coordinate, int attribute) { - Vertex pt = new Vertex(delaunayData.r(coordinate.x), delaunayData.r(coordinate.y), Double.isNaN(coordinate.z) ? 0 : delaunayData.r(coordinate.z), attribute); - Integer index = pts.get(pt); - if (index == null) { - index = maxIndex.getAndAdd(1); - pts.put(pt, index); - } - return index; - } - - @Override - public void filter(Coordinate pt) { - addPt(pt, -1); - } - } - - /** - * When defining a polygon with identifier, - * the zone marker is a coordinate in this polygon with an associated ID - */ - private static final class ZoneMarker { - public final Coordinate coordinate; - public final Integer index; - - public ZoneMarker(Coordinate coordinate, Integer index) { - this.coordinate = coordinate; - this.index = index; - } - } - private static final class LineStringHandler extends LayerTinfour.PointHandler { - private List segments; - private int firstPtIndex = -1; - private int attribute = -1; - - public LineStringHandler(LayerTinfour delaunayData, Map pts, AtomicInteger maxIndex, List segments) { - super(delaunayData, pts, maxIndex); - this.segments = segments; - } - - /** - * New line string - */ - public void reset() { - firstPtIndex = -1; - attribute = -1; - } - - public void setAttribute(int attribute) { - this.attribute = attribute; - } - - @Override - public void filter(Coordinate pt) { - if (firstPtIndex == -1) { - firstPtIndex = addPt(pt, attribute); - } else { - int secondPt = addPt(pt, attribute); - if (secondPt != firstPtIndex) { - segments.add(firstPtIndex); - segments.add(secondPt); - firstPtIndex = secondPt; - } - } - } - } - } From d9153b0b68dfe6357cbce19bfbbd555cc6385002 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 17:15:58 +0200 Subject: [PATCH 12/18] densify envelope cell --- .../noisemodelling/jdbc/TriangleNoiseMap.java | 9 ++++++++- .../noisemodelling/jdbc/PointNoiseMapTest.java | 2 +- .../noisemodelling/pathfinder/LayerTinfour.java | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java index a03a449cf..52c65cc20 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java @@ -3,6 +3,7 @@ import org.h2gis.utilities.JDBCUtilities; import org.h2gis.utilities.SFSUtilities; import org.h2gis.utilities.TableLocation; +import org.locationtech.jts.densify.Densifier; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; @@ -19,6 +20,7 @@ import org.locationtech.jts.simplify.TopologyPreservingSimplifier; import org.noise_planet.noisemodelling.pathfinder.*; import org.noise_planet.noisemodelling.pathfinder.Triangle; +import org.noise_planet.noisemodelling.pathfinder.utils.Densifier3D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -213,9 +215,14 @@ public void computeDelaunay(LayerDelaunay cellMesh, logger.info("Begin delaunay"); cellMesh.setRetrieveNeighbors(false); // Add cell envelope - cellMesh.addPolygon((Polygon)(new GeometryFactory().toGeometry(cellEnvelope)), 0); if (maximumArea > 1) { cellMesh.setMaxArea(maximumArea); + double triangleSide = (2*Math.pow(maximumArea, 0.5)) / Math.pow(3, 0.25); + Polygon polygon = (Polygon)Densifier.densify(new GeometryFactory().toGeometry(cellEnvelope), triangleSide); + cellMesh.addLineString(polygon.getExteriorRing(), 0); + } else { + Polygon polygon = (Polygon) new GeometryFactory().toGeometry(cellEnvelope); + cellMesh.addLineString(polygon.getExteriorRing(), 0); } cellMesh.processDelaunay(); logger.info("End delaunay"); diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java index 9b7d07ae4..d823d3afe 100644 --- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java +++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java @@ -175,7 +175,7 @@ public void testNoiseMapBuilding2() throws Exception { noisemap.setReceiverHasAbsoluteZCoordinates(false); noisemap.setSourceHasAbsoluteZCoordinates(false); noisemap.setHeightField("HEIGHT"); - noisemap.setMaximumArea(0); + noisemap.setMaximumArea(300); noisemap.setBuildingBuffer(0); noisemap.setMaximumPropagationDistance(800); diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index a7de67b85..e04fd8d91 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -227,11 +227,11 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) { LineString holeLine = newPoly.getInteriorRingN(holeIndex); final Coordinate[] hCoordinates = holeLine.getCoordinates(); - // Exterior ring must be CW + // Holes must be CW if(Orientation.isCCW(hCoordinates)) { CoordinateArrays.reverse(hCoordinates); } - // Should be counter clock wise + // Should be clock wise List vertexList = new ArrayList<>(); for(int vId = 0; vId < hCoordinates.length - 1 ; vId++) { vertexList.add(addCoordinate(hCoordinates[vId], buildingId)); From fa11a1a808ffab6953e0f4a1941826bcb0cc0754 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 17:43:14 +0200 Subject: [PATCH 13/18] Avoid NaN Z in triangulation --- .../jdbc/PointNoiseMapTest.java | 53 +++++++++---------- .../pathfinder/LayerTinfour.java | 47 +--------------- .../pathfinder/MeshBuilder.java | 41 +++++++++++++- 3 files changed, 67 insertions(+), 74 deletions(-) diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java index d823d3afe..97222a68b 100644 --- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java +++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/PointNoiseMapTest.java @@ -165,33 +165,32 @@ public void testNoiseMapBuilding() throws Exception { } } - - @Test - public void testNoiseMapBuilding2() throws Exception { - try(Statement st = connection.createStatement()) { - SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("roads_traff.shp").getFile(), "ROADS_GEOM"); - SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("buildings.shp").getFile(), " BUILDINGS"); - TriangleNoiseMap noisemap = new TriangleNoiseMap("BUILDINGS", "ROADS_GEOM"); - noisemap.setReceiverHasAbsoluteZCoordinates(false); - noisemap.setSourceHasAbsoluteZCoordinates(false); - noisemap.setHeightField("HEIGHT"); - noisemap.setMaximumArea(300); - noisemap.setBuildingBuffer(0); - noisemap.setMaximumPropagationDistance(800); - - - - noisemap.initialize(connection, new EmptyProgressVisitor()); - AtomicInteger pk = new AtomicInteger(0); - for(int i=0; i < noisemap.getGridDim(); i++) { - for(int j=0; j < noisemap.getGridDim(); j++) { - noisemap.generateReceivers(connection, i, j, "NM_RECEIVERS", "TRIANGLES", pk); - } - } - assertNotSame(0, pk.get()); - SHPWrite.exportTable(connection, "target/triangle.shp", "TRIANGLES"); - } - } + // @Test + // public void testNoiseMapBuilding2() throws Exception { + // try(Statement st = connection.createStatement()) { + // SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("roads_traff.shp").getFile(), "ROADS_GEOM"); + // SHPRead.readShape(connection, LDENPointNoiseMapFactoryTest.class.getResource("buildings.shp").getFile(), " BUILDINGS"); + // TriangleNoiseMap noisemap = new TriangleNoiseMap("BUILDINGS", "ROADS_GEOM"); + // noisemap.setReceiverHasAbsoluteZCoordinates(false); + // noisemap.setSourceHasAbsoluteZCoordinates(false); + // noisemap.setHeightField("HEIGHT"); + // noisemap.setMaximumArea(300); + // noisemap.setBuildingBuffer(0); + // noisemap.setMaximumPropagationDistance(800); + // + // + // + // noisemap.initialize(connection, new EmptyProgressVisitor()); + // AtomicInteger pk = new AtomicInteger(0); + // for(int i=0; i < noisemap.getGridDim(); i++) { + // for(int j=0; j < noisemap.getGridDim(); j++) { + // noisemap.generateReceivers(connection, i, j, "NM_RECEIVERS", "TRIANGLES", pk); + // } + // } + // assertNotSame(0, pk.get()); + // SHPWrite.exportTable(connection, "target/triangle.shp", "TRIANGLES"); + // } + // } } \ No newline at end of file diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index e04fd8d91..d48eafb8b 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -42,7 +42,7 @@ private Vertex addCoordinate(Coordinate coordinate, int index) { } } if(found == null) { - found = new Vertex(coordinate.x, coordinate.y, coordinate.z, index); + found = new Vertex(coordinate.x, coordinate.y, Double.isNaN(coordinate.z) ? 0 : coordinate.z, index); ptsIndex.insert(new Envelope(coordinate), found); } return found; @@ -156,47 +156,6 @@ public void processDelaunay() throws LayerDelaunayError { } } - - public static final class SetZFilter implements CoordinateSequenceFilter { - private boolean done = false; - private boolean resetToZero = false; - - public SetZFilter() { - - } - - public SetZFilter(boolean resetToZero) { - this.resetToZero = resetToZero; - } - - @Override - public void filter(CoordinateSequence seq, int i) { - double x = seq.getX(i); - double y = seq.getY(i); - double z = seq.getOrdinate(i, 2); - seq.setOrdinate(i, 0, x); - seq.setOrdinate(i, 1, y); - if (Double.isNaN(z) || resetToZero) { - seq.setOrdinate(i, 2, 0); - } else { - seq.setOrdinate(i, 2, z); - } - if (i == seq.size()) { - done = true; - } - } - - @Override - public boolean isDone() { - return done; - } - - @Override - public boolean isGeometryChanged() { - return true; - } - } - /** * Add height of building * @@ -204,10 +163,6 @@ public boolean isGeometryChanged() { */ @Override public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayError { - // To avoid errors we set NaN Z coordinates to 0. - LayerTinfour.SetZFilter zFilter = new LayerTinfour.SetZFilter(); - newPoly.apply(zFilter); - GeometryFactory factory = new GeometryFactory(); final Coordinate[] coordinates = newPoly.getExteriorRing().getCoordinates(); // Exterior ring must be CCW if(!Orientation.isCCW(coordinates)) { diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java index ffedf057f..e330a1d6c 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/MeshBuilder.java @@ -467,7 +467,7 @@ public void finishPolygonFeeding(Geometry boundingBoxGeom) throws LayerDelaunayE CoordinateSequenceFilter absoluteCoordinateSequenceFilter; if(resetBuildingsZGround) { // set buildings Z coordinates to 0 m - absoluteCoordinateSequenceFilter = new LayerTinfour.SetZFilter(true); + absoluteCoordinateSequenceFilter = new SetZFilter(true); } else { absoluteCoordinateSequenceFilter = new ComputeRays.AbsoluteCoordinateSequenceFilter(fastObstructionTest, resetBuildingsZGround); } @@ -526,4 +526,43 @@ public void testMergeGetPolygonWithHeight() { } } + public static final class SetZFilter implements CoordinateSequenceFilter { + private boolean done = false; + private boolean resetToZero = false; + + public SetZFilter() { + + } + + public SetZFilter(boolean resetToZero) { + this.resetToZero = resetToZero; + } + + @Override + public void filter(CoordinateSequence seq, int i) { + double x = seq.getX(i); + double y = seq.getY(i); + double z = seq.getOrdinate(i, 2); + seq.setOrdinate(i, 0, x); + seq.setOrdinate(i, 1, y); + if (Double.isNaN(z) || resetToZero) { + seq.setOrdinate(i, 2, 0); + } else { + seq.setOrdinate(i, 2, z); + } + if (i == seq.size()) { + done = true; + } + } + + @Override + public boolean isDone() { + return done; + } + + @Override + public boolean isGeometryChanged() { + return true; + } + } } \ No newline at end of file From 623bc5c96944ab5e34b58cfe6438f9c5ce89ba1d Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 17:51:33 +0200 Subject: [PATCH 14/18] forget expand search extent --- .../noise_planet/noisemodelling/pathfinder/LayerTinfour.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index d48eafb8b..04816a494 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -31,7 +31,9 @@ public class LayerTinfour implements LayerDelaunay { private List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i private Vertex addCoordinate(Coordinate coordinate, int index) { - List result = ptsIndex.query(new Envelope(coordinate)); + final Envelope env = new Envelope(coordinate); + env.expandBy(epsilon); + List result = ptsIndex.query(env); Vertex found = null; for(Object vertex : result) { if(vertex instanceof Vertex) { From d76a73ae276ce5cfb3540757b05fe5ac9b2fa23d Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Wed, 14 Apr 2021 17:57:41 +0200 Subject: [PATCH 15/18] Remove unused parameter --- .../wps/Receivers/Delaunay_Grid.groovy | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy index 3deadc97f..f75b5fcaf 100644 --- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy +++ b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy @@ -91,14 +91,6 @@ inputs = [ min : 0, max: 1, type : Double.class ], - sourceDensification: [ - name : 'Source densification', - title : 'Source densification', - description: 'Set additional receivers near sound sources (roads). This is the maximum distance between the points that compose the polygon near the source in meter. (FLOAT)' + - '

Default value : 8 ', - min : 0, max: 1, - type : Double.class - ], height : [ name : 'Height', title : 'Height', @@ -206,12 +198,6 @@ def exec(Connection connection, input) { maxArea = input['maxArea'] as Double } - Double sourceDensification = 8.0 - if (input.containsKey('sourceDensification')) { - sourceDensification = input['sourceDensification'] as Double - } - - int srid = SFSUtilities.getSRID(connection, TableLocation.parse(building_table_name)) Geometry fence = null @@ -256,8 +242,6 @@ def exec(Connection connection, input) { noiseMap.setRoadWidth(roadWidth) // No triangles larger than provided area noiseMap.setMaximumArea(maxArea) - // Densification of receivers near sound sources - noiseMap.setSourceDensification(sourceDensification) logger.info("Delaunay initialize") noiseMap.initialize(connection, new EmptyProgressVisitor()) From 3c7207b28d7c32fa8525ade1f2be7f5e81219673 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Thu, 15 Apr 2021 17:16:56 +0200 Subject: [PATCH 16/18] Check constraint validity before inserting --- .../noisemodelling/jdbc/TriangleNoiseMap.java | 18 +++- .../pathfinder/LayerTinfour.java | 100 ++++++++++++++++-- .../pathfinder/LayerTinfourTest.java | 42 ++++++-- .../wps/Receivers/Delaunay_Grid.groovy | 1 + 4 files changed, 145 insertions(+), 16 deletions(-) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java index 52c65cc20..7834bffd6 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java @@ -49,6 +49,7 @@ public class TriangleNoiseMap extends JdbcNoiseMap { private long nbreceivers = 0; private double receiverHeight = 1.6; private double buildingBuffer = 2; + private String exceptionDumpFolder = ""; /** @@ -59,6 +60,20 @@ public TriangleNoiseMap(String buildingsTableName, String sourcesTableName) { super(buildingsTableName, sourcesTableName); } + /** + * @return When an exception occur, this folder with receiver the input data + */ + public String getExceptionDumpFolder() { + return exceptionDumpFolder; + } + + /** + * @param exceptionDumpFolder When an exception occur, this folder with receiver the input data + */ + public void setExceptionDumpFolder(String exceptionDumpFolder) { + this.exceptionDumpFolder = exceptionDumpFolder; + } + /** * @return Do not add receivers closer to specified distance */ @@ -255,7 +270,8 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin ArrayList buildings = new ArrayList<>(); fetchCellBuildings(connection, cellEnvelope, buildings); - LayerDelaunay cellMesh = new LayerTinfour(); + LayerTinfour cellMesh = new LayerTinfour(); + cellMesh.setDumpFolder(exceptionDumpFolder); try { computeDelaunay(cellMesh, mainEnvelope, cellI, cellJ, diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java index 04816a494..c2273f226 100644 --- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java +++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfour.java @@ -3,18 +3,25 @@ import org.locationtech.jts.algorithm.Orientation; import org.locationtech.jts.geom.*; import org.locationtech.jts.index.quadtree.Quadtree; +import org.locationtech.jts.io.WKBWriter; +import org.locationtech.jts.io.WKTWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.tinfour.common.*; import org.tinfour.standard.IncrementalTin; import org.tinfour.utils.TriangleCollector; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.*; import java.util.function.Consumer; public class LayerTinfour implements LayerDelaunay { private double epsilon = 0.001; // merge of Vertex instances below this distance private static final Logger LOGGER = LoggerFactory.getLogger(LayerTinfour.class); + public String dumpFolder = ""; //private Map pts = new HashMap(); //private List segments = new ArrayList(); @@ -57,6 +64,20 @@ private List computeTriangles(IncrementalTin incrementalTin) { return triangles; } + /** + * @return When an exception occur, this folder with receiver the input data + */ + public String getDumpFolder() { + return dumpFolder; + } + + /** + * @param dumpFolder When an exception occur, this folder with receiver the input data + */ + public void setDumpFolder(String dumpFolder) { + this.dumpFolder = dumpFolder; + } + /** * @return Merge vertices closer than specified epsilon */ @@ -94,6 +115,53 @@ private static Coordinate getCentroid(SimpleTriangle triangle) { return new Coordinate( cx, cy, cz); } + public void dumpData() { + GeometryFactory factory = new GeometryFactory(); + WKTWriter wktWriter = new WKTWriter(3); + try { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(dumpFolder, "tinfour_dump.csv")))) { + for(Object vObj : ptsIndex.queryAll()) { + if(vObj instanceof Vertex) { + final Vertex v = (Vertex)vObj; + Point p = factory.createPoint(toCoordinate(v)); + writer.write(wktWriter.write(p)); + writer.write("\n"); + } + } + for (IConstraint constraint : constraints) { + if (constraint instanceof LinearConstraint) { + List vertices = constraint.getVertices(); + Coordinate[] coordinates = new Coordinate[vertices.size()]; + for (int i = 0; i < vertices.size(); i++) { + final Vertex v = vertices.get(i); + coordinates[i] = new Coordinate(v.getX(), v.getY(), v.getZ()); + } + LineString l = factory.createLineString(coordinates); + writer.write(wktWriter.write(l)); + writer.write("\n"); + } else if (constraint instanceof PolygonConstraint) { + List vertices = constraint.getVertices(); + if(vertices != null && vertices.size() >= 3) { + Coordinate[] coordinates = new Coordinate[vertices.size() + 1]; + for (int i = 0; i < vertices.size() ; i++) { + final Vertex v = vertices.get(i); + coordinates[i] = new Coordinate(v.getX(), v.getY(), v.getZ()); + } + coordinates[coordinates.length - 1] = coordinates[0]; + Polygon l = factory.createPolygon(coordinates); + writer.write(wktWriter.write(l)); + writer.write("\n"); + } else { + LOGGER.info("Weird null polygon " + constraint); + } + } + } + } + } catch (IOException ioEx) { + // ignore + } + } + @Override public void processDelaunay() throws LayerDelaunayError { triangles.clear(); @@ -110,7 +178,16 @@ public void processDelaunay() throws LayerDelaunayError { // Add points tin.add(meshPoints, null); // Add constraints - tin.addConstraints(constraints, maxArea > 0); + try { + tin.addConstraints(constraints, maxArea > 0); + }catch (IllegalStateException ex) { + // Got error + // Dump input data + if(!dumpFolder.isEmpty()) { + dumpData(); + } + throw new LayerDelaunayError(ex); + } refine = false; simpleTriangles = computeTriangles(tin); @@ -176,8 +253,11 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro vertexList.add(addCoordinate(coordinates[vId], buildingId)); } PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); - constraints.add(polygonConstraint); - constraintIndex.add(buildingId); + polygonConstraint.complete(); + if(polygonConstraint.isValid()) { + constraints.add(polygonConstraint); + constraintIndex.add(buildingId); + } } // Append holes final int holeCount = newPoly.getNumInteriorRing(); @@ -194,8 +274,11 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro vertexList.add(addCoordinate(hCoordinates[vId], buildingId)); } PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList); - constraints.add(polygonConstraint); - constraintIndex.add(buildingId); + polygonConstraint.complete(); + if(polygonConstraint.isValid()) { + constraints.add(polygonConstraint); + constraintIndex.add(buildingId); + } } } @@ -241,8 +324,11 @@ public void addLineString(LineString lineToProcess, int buildingID) throws Layer vertexList.add(addCoordinate(coordinate, buildingID)); } LinearConstraint linearConstraint = new LinearConstraint(vertexList); - constraints.add(linearConstraint); - constraintIndex.add(buildingID); + linearConstraint.complete(); + if(linearConstraint.isValid()) { + constraints.add(linearConstraint); + constraintIndex.add(buildingID); + } } //add buildingID to edge property and to points property diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index c3734c09a..feece8263 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -2,13 +2,14 @@ import org.h2gis.functions.io.osm.OSMDriverFunction; import org.junit.Test; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.PrecisionModel; +import org.locationtech.jts.geom.*; import org.locationtech.jts.io.ParseException; import org.locationtech.jts.io.WKTReader; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; import java.util.List; import static org.junit.Assert.*; @@ -84,14 +85,39 @@ public void testPolygonHole() throws ParseException, LayerDelaunayError { (Polygon) wktReader.read("POLYGON((222705.696340536 6758557.74687277 0,222724.075919557 6758557.07325136 0,222740.531595117 6758528.34377304 0,222741.230128632 6758528.86480505 0,222743.686623899 6758524.74224218 0,222742.888076702 6758524.26098912 0,222744.171861162 6758521.90499592 0,222732.249628577 6758511.59826499 0,222726.944985841 6758520.82200926 0,222718.050422367 6758536.28473477 0,222708.564257407 6758552.76566163 0,222705.696340536 6758557.74687277 0))") }; Polygon merged = (Polygon)factory.createMultiPolygon(polygons).buffer(0); - System.out.println(merged.toString()); LayerTinfour layerTinfour = new LayerTinfour(); + layerTinfour.setDumpFolder("target"); layerTinfour.setRetrieveNeighbors(true); - layerTinfour.addPolygon(merged, 55); - layerTinfour.processDelaunay(); - List triangleList = layerTinfour.getTriangles(); + layerTinfour.dumpData(); } + +// @Test +// public void debugDump() throws ParseException, LayerDelaunayError, IOException { +// File dumpPath = new File("C:\\Users\\kento\\softs\\NoiseModelling_3.3.2\\data_dir\\tinfour_dump.csv"); +// WKTReader wktReader = new WKTReader(); +// LayerTinfour layerTinfour = new LayerTinfour(); +// try(BufferedReader reader = new BufferedReader(new FileReader(dumpPath))) { +// String line; +// int index = 0; +// while ((line = reader.readLine()) != null) { +// Geometry obj = wktReader.read(line); +// if(obj instanceof Point) { +// layerTinfour.addVertex(obj.getCoordinate()); +// } else if(obj instanceof Polygon) { +// layerTinfour.addPolygon((Polygon)obj, index++); +// } else if (obj instanceof LineString) { +// layerTinfour.addLineString((LineString)obj, index++); +// } +// } +// } +// layerTinfour.processDelaunay(); +// +// List triangles = layerTinfour.getTriangles(); +// +// System.out.println(triangles.size()); +// } + } \ No newline at end of file diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy index f75b5fcaf..242266460 100644 --- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy +++ b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy @@ -245,6 +245,7 @@ def exec(Connection connection, input) { logger.info("Delaunay initialize") noiseMap.initialize(connection, new EmptyProgressVisitor()) + noiseMap.setExceptionDumpFolder("data_dir/") AtomicInteger pk = new AtomicInteger(0) ProgressVisitor progressVisitorNM = progressLogger.subProcess(noiseMap.getGridDim() * noiseMap.getGridDim()) From dd4963ff2e1d855e64dc8196f2c1b5e3ef4f0654 Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Thu, 15 Apr 2021 17:49:50 +0200 Subject: [PATCH 17/18] Use good SRID on RECEIVERS points --- .../noisemodelling/jdbc/JdbcNoiseMap.java | 8 ++++++-- .../noisemodelling/jdbc/TriangleNoiseMap.java | 18 +++--------------- .../wps/Receivers/Delaunay_Grid.groovy | 6 ++---- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java index e643f867e..db501010c 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/JdbcNoiseMap.java @@ -349,8 +349,12 @@ public void initialize(Connection connection, ProgressVisitor progression) throw if(sourcesTableName.isEmpty()) { throw new SQLException("A sound source table must be provided"); } - geometryFactory = new GeometryFactory(new PrecisionModel(), - SFSUtilities.getSRID(connection, TableLocation.parse(sourcesTableName))); + int srid = 0; + srid = SFSUtilities.getSRID(connection, TableLocation.parse(sourcesTableName)); + if(srid == 0) { + srid = SFSUtilities.getSRID(connection, TableLocation.parse(buildingsTableName)); + } + geometryFactory = new GeometryFactory(new PrecisionModel(), srid); // Steps of execution // Evaluation of the main bounding box (sourcesTableName+buildingsTableName) diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java index 7834bffd6..5f9dc5df1 100644 --- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java +++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/TriangleNoiseMap.java @@ -4,16 +4,7 @@ import org.h2gis.utilities.SFSUtilities; import org.h2gis.utilities.TableLocation; import org.locationtech.jts.densify.Densifier; -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Envelope; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; -import org.locationtech.jts.geom.GeometryFactory; -import org.locationtech.jts.geom.LineString; -import org.locationtech.jts.geom.MultiLineString; -import org.locationtech.jts.geom.Point; -import org.locationtech.jts.geom.Polygon; -import org.locationtech.jts.geom.TopologyException; +import org.locationtech.jts.geom.*; import org.locationtech.jts.io.WKTWriter; import org.locationtech.jts.operation.buffer.BufferOp; import org.locationtech.jts.operation.buffer.BufferParameters; @@ -51,7 +42,6 @@ public class TriangleNoiseMap extends JdbcNoiseMap { private double buildingBuffer = 2; private String exceptionDumpFolder = ""; - /** * @param buildingsTableName Buildings table * @param sourcesTableName Source table name @@ -313,13 +303,11 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin } int receiverPkOffset = receiverPK.get(); // Add vertices to receivers - PreparedStatement ps = connection.prepareStatement("INSERT INTO "+TableLocation.parse(receiverTableName)+" VALUES (?, ST_MAKEPOINT(?,?,?));"); + PreparedStatement ps = connection.prepareStatement("INSERT INTO "+TableLocation.parse(receiverTableName)+" VALUES (?, ?);"); int batchSize = 0; for(Coordinate v : vertices) { ps.setInt(1, receiverPK.getAndAdd(1)); - ps.setDouble(2, v.x); - ps.setDouble(3, v.y); - ps.setDouble(4, v.z); + ps.setObject(2, geometryFactory.createPoint(v)); ps.addBatch(); batchSize++; if (batchSize >= BATCH_MAX_SIZE) { diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy index 242266460..a7de71008 100644 --- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy +++ b/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Receivers/Delaunay_Grid.groovy @@ -209,7 +209,7 @@ def exec(Connection connection, input) { //Statement sql = connection.createStatement() Sql sql = new Sql(connection) connection = new ConnectionWrapper(connection) - RootProgressVisitor progressLogger = new RootProgressVisitor(2, true, 1) + RootProgressVisitor progressLogger = new RootProgressVisitor(1, true, 1) // Delete previous receivers grid sql.execute(String.format("DROP TABLE IF EXISTS %s", receivers_table_name)) @@ -256,9 +256,7 @@ def exec(Connection connection, input) { progressVisitorNM.endStep() } } - - sql.execute("UPDATE " + receivers_table_name + " SET THE_GEOM = ST_SETSRID(THE_GEOM, " + srid + ")") - + logger.info("Create spatial index on "+receivers_table_name+" table") sql.execute("Create spatial index on " + receivers_table_name + "(the_geom);") int nbReceivers = sql.firstRow("SELECT COUNT(*) FROM " + receivers_table_name)[0] as Integer From d896d5e7db7656b53f763ca6e9b93f60d2a4a79a Mon Sep 17 00:00:00 2001 From: nicolas-f <1382241+nicolas-f@users.noreply.github.com> Date: Mon, 19 Apr 2021 10:19:25 +0200 Subject: [PATCH 18/18] complete unit test --- .../pathfinder/LayerTinfourTest.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java index feece8263..c9c0f1773 100644 --- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java +++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/LayerTinfourTest.java @@ -91,12 +91,38 @@ public void testPolygonHole() throws ParseException, LayerDelaunayError { layerTinfour.addPolygon(merged, 55); layerTinfour.processDelaunay(); List triangleList = layerTinfour.getTriangles(); + List vertices = layerTinfour.getVertices(); + // Test dump layerTinfour.dumpData(); + Point hole1 = factory.createPoint(new Coordinate(222690.860,6758520.184)); + Point hole2 = factory.createPoint(new Coordinate(222711.177,6758532.233)); + Point inGeom = merged.getInteriorPoint(); + boolean foundHole1 = false; + boolean foundHole2 = false; + boolean foundInGeom = false; + for(Triangle triangle : triangleList) { + Coordinate[] tri = new Coordinate[] {vertices.get(triangle.getA()), vertices.get(triangle.getB()), + vertices.get(triangle.getC()), vertices.get(triangle.getA())}; + Polygon triGeom = factory.createPolygon(tri); + if(triGeom.contains(hole1)) { + assertEquals(0, triangle.getAttribute()); + foundHole1 = true; + } else if(triGeom.contains(hole2)) { + assertEquals(0, triangle.getAttribute()); + foundHole2 = true; + } else if(triGeom.contains(inGeom)) { + assertEquals(55, triangle.getAttribute()); + foundInGeom = true; + } + } + assertTrue(foundHole1); + assertTrue(foundHole2); + assertTrue(foundInGeom); } // @Test // public void debugDump() throws ParseException, LayerDelaunayError, IOException { -// File dumpPath = new File("C:\\Users\\kento\\softs\\NoiseModelling_3.3.2\\data_dir\\tinfour_dump.csv"); +// File dumpPath = new File("target/tinfour_dump.csv"); // WKTReader wktReader = new WKTReader(); // LayerTinfour layerTinfour = new LayerTinfour(); // try(BufferedReader reader = new BufferedReader(new FileReader(dumpPath))) {