Computes sound emission levels from sound source characteristics (vehicle flow, vehicle type, sound
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailWayParametersvar.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailWayParametersvar.java
new file mode 100644
index 000000000..ab2961374
--- /dev/null
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailWayParametersvar.java
@@ -0,0 +1,76 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+
+package org.noise_planet.noisemodelling.emission.railway;
+/**
+ * Railway noise evaluation from Cnossos reference : COMMISSION DIRECTIVE (EU) 2015/996
+ * of 19 May 2015 establishing common noise assessment methods according to Directive 2002/49/EC
+ * of the European Parliament and of the Council
+ *
+ * amending, for the purposes of adapting to scientific and technical progress, Annex II to
+ * Directive 2002/49/EC of the European Parliament and of the Council as regards
+ * common noise assessment methods
+ *
+ * part 2.3. Railway noise
+ *
+ * Return the dB value corresponding to the parameters
+ * @author Adrien Le Bellec, Université Gustave Eiffel
+ * @author Olivier Chiello, Université Gustave Eiffel
+ */
+
+import org.noise_planet.noisemodelling.emission.LineSource;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.noise_planet.noisemodelling.emission.utils.Utils.sumDbArray;
+
+/**
+ * Data result stockage
+ */
+public class RailWayParametersvar {
+ public Map railwaySourceList = new HashMap<>();
+
+
+ public Map getRailwaySourceList() {
+ return railwaySourceList;
+ }
+
+ public void setRailwaySourceList(Map railwaySourceList) {
+ this.railwaySourceList = railwaySourceList;
+ }
+
+ /**
+ * method adds a railway source to the list of railway sources, associating it with a specified ID.
+ * @param ID
+ * @param lineSource
+ */
+ public void addRailwaySource(String ID, LineSource lineSource) {
+ this.railwaySourceList.put(ID, lineSource);
+ }
+
+ /**
+ *
+ * @param lineSource1
+ * @param lineSource2
+ * @return an instance of RailWayParameters
+ */
+ public RailWayParametersvar sumRailwaySource(RailWayParametersvar lineSource1, RailWayParametersvar lineSource2) {
+ if (!lineSource2.getRailwaySourceList().isEmpty()){
+ for (Map.Entry railwaySourceEntry : lineSource1.getRailwaySourceList().entrySet()) {
+ double[] lW1 = railwaySourceEntry.getValue().getlW();
+ double[] lW2 = lineSource2.getRailwaySourceList().get(railwaySourceEntry.getKey()).getlW();
+ lineSource1.getRailwaySourceList().get(railwaySourceEntry.getKey()).setlW(sumDbArray(lW1, lW2));
+ }
+ }
+ return lineSource1;
+ }
+
+}
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/Railway.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/Railway.java
index 692eb403f..9c7bd1d6a 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/Railway.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/Railway.java
@@ -13,9 +13,13 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;
+import org.noise_planet.noisemodelling.emission.utils.UriUtils;
import java.io.IOException;
import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
import java.util.Iterator;
import java.util.Map;
@@ -50,14 +54,42 @@ public static Iterable iteratorToIterable(Iterator iterator) {
return () -> iterator;
}
- public void setVehicleDataFile(String VehicleData) {
- this.vehicleData = parse(Railway.class.getResourceAsStream(VehicleData));
+ /**
+ * Retrieves an InputStream from a resource string, which can either be a URI or a classpath resource.
+ * If the resource string represents a valid URI, the input stream is opened from the URI.
+ * Otherwise, the input stream is retrieved from the classpath using the resource string.
+ *
+ * @param resource the resource string, which can be a URI or a classpath resource
+ * @return the InputStream corresponding to the resource
+ * @throws IOException if the resource cannot be found or accessed
+ */
+ private static InputStream getStreamFromResourceString(String resource) throws IOException {
+ if (UriUtils.isValidUri(resource)) {
+ return UriUtils.openSafeStream(resource);
+ } else {
+ InputStream stream = Railway.class.getResourceAsStream(resource);
+ if (stream == null) {
+ throw new IOException("Resource not found: " + resource);
+ }
+ return stream;
+ }
}
- public void setTrainSetDataFile(String TrainsetData) {
- this.trainsetData = parse(Railway.class.getResourceAsStream(TrainsetData));
+
+ public void setVehicleDataFile(String VehicleData) throws IOException {
+ try (InputStream stream = getStreamFromResourceString(VehicleData)) {
+ this.vehicleData = parse(stream);
+ }
+ }
+
+ public void setTrainSetDataFile(String trainsetResource) throws IOException {
+ try (InputStream stream = getStreamFromResourceString(trainsetResource)) {
+ this.trainsetData = parse(stream);
+ }
}
- public void setRailwayDataFile(String RailWayData) {
- this.railWayData = parse(Railway.class.getResourceAsStream(RailWayData));
+ public void setRailwayDataFile(String railwayResource) throws IOException {
+ try (InputStream stream = getStreamFromResourceString(railwayResource)) {
+ this.railWayData = parse(stream);
+ }
}
public JsonNode getVehicleNode(String typeVehicle) {
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailwayVehicleParametersvar.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailwayVehicleParametersvar.java
new file mode 100644
index 000000000..9dc241885
--- /dev/null
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/RailwayVehicleParametersvar.java
@@ -0,0 +1,60 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+
+package org.noise_planet.noisemodelling.emission.railway;
+
+/**
+ * Railway noise evaluation from Cnossos reference : COMMISSION DIRECTIVE (EU) 2015/996
+ * of 19 May 2015 establishing common noise assessment methods according to Directive 2002/49/EC
+ * of the European Parliament and of the Council
+ *
+ * amending, for the purposes of adapting to scientific and technical progress, Annex II to
+ * Directive 2002/49/EC of the European Parliament and of the Council as regards
+ * common noise assessment methods
+ *
+ * part 2.3. Railway noise
+ *
+ * @author Adrien Le Bellec, Université Gustave Eiffel
+ * @author Olivier Chiello, Université Gustave Eiffel
+ */
+
+/**
+ * Parameters Vehicule
+ */
+
+public class RailwayVehicleParametersvar {
+
+ // set default value
+ private String typeVehicle = ""; // name of the vehicles
+ private double speedVehicle; // speed of the vehicles (km/h)
+
+
+ public RailwayVehicleParametersvar() {
+
+ setTypeVehicle(typeVehicle);
+ setSpeedVehicle(speedVehicle);
+ }
+
+ public String getTypeVehicle() {
+ return typeVehicle;
+ }
+
+ public void setTypeVehicle(String typeVehicle) {
+ this.typeVehicle = typeVehicle;
+ }
+
+ public double getSpeedVehicle() {
+ return speedVehicle;
+ }
+
+ public void setSpeedVehicle(double speedVehicle) {
+ this.speedVehicle = speedVehicle;
+ }
+
+}
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayCnossos.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayCnossos.java
index 941313f4b..abb021906 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayCnossos.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayCnossos.java
@@ -13,9 +13,11 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;
import org.noise_planet.noisemodelling.emission.LineSource;
+import org.noise_planet.noisemodelling.emission.railway.Railway;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Arrays;
import java.util.Iterator;
import java.util.Locale;
@@ -38,34 +40,24 @@
* @author Olivier Chiello, Université Gustave Eiffel
*/
-public class RailwayCnossos extends org.noise_planet.noisemodelling.emission.railway.Railway {
+public class RailwayCnossos extends Railway {
public RailwayCnossos() {
}
-
/**
- *
- * @param inputStream
- * @return
+ * Helper to read a Ref field from a vehicle JSON node.
+ * Supports both string (new merged file) and integer (legacy files) JSON values.
+ * @param vehicleNode the JSON node of the vehicle
+ * @param fieldName the Ref field name (e.g. "RefRoughness")
+ * @return the string value of the reference
*/
- private static JsonNode parse(InputStream inputStream) {
- try {
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readTree(inputStream);
- } catch (IOException ex) {
- return NullNode.getInstance();
+ private static String getRefValue(JsonNode vehicleNode, String fieldName) {
+ JsonNode value = vehicleNode.get(fieldName);
+ if (value.isTextual()) {
+ return value.textValue();
}
- }
-
- /**
- *
- * @param iterator
- * @return an iterator
- * @param
- */
- public static Iterable iteratorToIterable(Iterator iterator) {
- return () -> iterator;
+ return String.valueOf(value.intValue());
}
/**
@@ -76,8 +68,8 @@ public static Iterable iteratorToIterable(Iterator iterator) {
* @return
*/
public Double getWheelRoughness(String typeVehicle, String fileVersion, int lambdaId) { //
- int refId = getVehicleNode(typeVehicle).get("RefRoughness").intValue();
- return getRailWayData().get("Vehicle").get("WheelRoughness").get(String.valueOf(refId)).get("Values").get(lambdaId).doubleValue();
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefRoughness");
+ return getRailWayData().get("Vehicle").get("WheelRoughness").get(refId).get("Values").get(lambdaId).doubleValue();
}
/**
@@ -87,8 +79,8 @@ public Double getWheelRoughness(String typeVehicle, String fileVersion, int lamb
* @return contact filter
*/
public Double getContactFilter(String typeVehicle, int lambdaId) { //
- int refId = getVehicleNode(typeVehicle).get("RefContact").intValue();
- return getRailWayData().get("Vehicle").get("ContactFilter").get(String.valueOf(refId)).get("Values").get(lambdaId).doubleValue();
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefContact");
+ return getRailWayData().get("Vehicle").get("ContactFilter").get(refId).get("Values").get(lambdaId).doubleValue();
}
/**
@@ -97,8 +89,8 @@ public Double getContactFilter(String typeVehicle, int lambdaId) { //
* @param lambdaId
* @return
*/
- public Double getTrackRoughness(int trackRoughnessId, int lambdaId) { //
- return getRailWayData().get("Track").get("RailRoughness").get(String.valueOf(trackRoughnessId)).get("Values").get(lambdaId).doubleValue();
+ public Double getTrackRoughness(String trackRoughnessId, int lambdaId) { //
+ return getRailWayData().get("Track").get("RailRoughness").get(trackRoughnessId).get("Values").get(lambdaId).doubleValue();
}
/**
@@ -136,11 +128,11 @@ public int getNbCoach(String typeVehicle) { //
* @return
*/
public double getTractionNoise(String typeVehicle, int runningCondition, String sourceHeightId, String fileVersion, int freqId) { //
- int refId = getVehicleNode(typeVehicle).get("RefTraction").intValue();
- double tractionSpectre =0;
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefTraction");
+ double tractionSpectrum = 0;
- String condition = "ConstantSpeed";
- if (refId != 0) {
+ if (refId != null && !refId.isEmpty()) {
+ String condition = "ConstantSpeed";
switch (runningCondition) {
case 0:
condition = "ConstantSpeed";
@@ -155,15 +147,19 @@ public double getTractionNoise(String typeVehicle, int runningCondition, String
condition = "IdlingSpeed";
break;
}
- try {
- tractionSpectre = getRailWayData().get("Vehicle").get(condition).get(String.valueOf(refId)).get("Values").get(sourceHeightId).get(freqId).doubleValue();
- } catch (NullPointerException ex) {
- throw new IllegalArgumentException(String.format(Locale.ROOT, "Could not find traction spectrum for the following parameters " +
- "getRailWayData(%d).get(\"Vehicle\").get(%s).get(String.valueOf" +
- "(%d)).get(\"Values\").get(%s).get(%d)", fileVersion, condition, refId, sourceHeightId, freqId));
+ // Resolve traction node dynamically from JSON data
+ JsonNode tractionNode = getRailWayData().get("Vehicle").get(condition).get(refId);
+ if (tractionNode != null) {
+ try {
+ tractionSpectrum = tractionNode.get("Values").get(sourceHeightId).get(freqId).doubleValue();
+ } catch (NullPointerException ex) {
+ throw new IllegalArgumentException(String.format(Locale.ROOT, "Could not find traction spectrum for the following parameters " +
+ "getRailWayData(%s).get(\"Vehicle\").get(%s).get" +
+ "(%s).get(\"Values\").get(%s).get(%d)", fileVersion, condition, refId, sourceHeightId, freqId));
+ }
}
}
- return tractionSpectre;
+ return tractionSpectrum;
}
/**
@@ -176,8 +172,8 @@ public double getTractionNoise(String typeVehicle, int runningCondition, String
* @return
*/
public double getAerodynamicNoise(String typeVehicle, String sourceHeightId, String fileVersion, int freqId) { //
- int refId = getVehicleNode(typeVehicle).get("RefAerodynamic").intValue();
- return getRailWayData().get("Vehicle").get("AerodynamicNoise").get(String.valueOf(refId)).get("Values").get(sourceHeightId).get(freqId).doubleValue();
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefAerodynamic");
+ return getRailWayData().get("Vehicle").get("AerodynamicNoise").get(refId).get("Values").get(sourceHeightId).get(freqId).doubleValue();
}
/**
@@ -187,8 +183,8 @@ public double getAerodynamicNoise(String typeVehicle, String sourceHeightId, Str
* @param freqId
* @return
*/
- public Double getBridgeStructural(int bridgeId, int freqId) {
- return getRailWayData().get("Track").get("BridgeConstant").get(String.valueOf(bridgeId)).get("Values").get(freqId).doubleValue();
+ public Double getBridgeStructural(String bridgeId, int freqId) {
+ return getRailWayData().get("Track").get("BridgeConstant").get(bridgeId).get("Values").get(freqId).doubleValue();
}
@@ -198,8 +194,8 @@ public Double getBridgeStructural(int bridgeId, int freqId) {
* @param freqId
* @return
*/
- public Double getTrackTransfer(int trackTransferId, int freqId) { //
- return getRailWayData().get("Track").get("TrackTransfer").get(String.valueOf(trackTransferId)).get("Spectre").get(freqId).doubleValue();
+ public Double getTrackTransfer(String trackTransferId, int freqId) { //
+ return getRailWayData().get("Track").get("TrackTransfer").get(trackTransferId).get("Spectre").get(freqId).doubleValue();
}
/**
@@ -208,8 +204,8 @@ public Double getTrackTransfer(int trackTransferId, int freqId) { //
* @param freqId
* @return
*/
- public Double getImpactNoise(int impactNoiseId, int freqId) { //
- return getRailWayData().get("Track").get("ImpactNoise").get(String.valueOf(impactNoiseId)).get("Values").get(freqId).doubleValue();
+ public Double getImpactNoise(String impactNoiseId, int freqId) { //
+ return getRailWayData().get("Track").get("ImpactNoise").get(impactNoiseId).get("Values").get(freqId).doubleValue();
}
/**
@@ -219,8 +215,8 @@ public Double getImpactNoise(int impactNoiseId, int freqId) { //
* @return
*/
public Double getVehTransfer(String typeVehicle, int freqId) {
- int RefTransfer = getVehicleNode(typeVehicle).get("RefTransfer").intValue();
- return getRailWayData().get("Vehicle").get("Transfer").get(String.valueOf(RefTransfer)).get("Spectre").get(freqId).doubleValue();
+ String refTransfer = getRefValue(getVehicleNode(typeVehicle), "RefTransfer");
+ return getRailWayData().get("Vehicle").get("Transfer").get(refTransfer).get("Spectre").get(freqId).doubleValue();
}
@@ -234,7 +230,7 @@ public Double getVehTransfer(String typeVehicle, int freqId) {
* @param idLambda
* @return
*/
- public Double getLRoughness(String typeVehicle, int trackRoughnessId, String vehicleFileVersion, int idLambda) { //
+ public Double getLRoughness(String typeVehicle, String trackRoughnessId, String vehicleFileVersion, int idLambda) { //
double wheelRoughness = getWheelRoughness(typeVehicle, vehicleFileVersion, idLambda);
double trackRoughness = getTrackRoughness(trackRoughnessId, idLambda);
return 10 * Math.log10(Math.pow(10, wheelRoughness / 10) + Math.pow(10, trackRoughness / 10));
@@ -280,10 +276,10 @@ public RailWayCnossosParameters evaluate(RailwayVehicleCnossosParameters vehicle
double speedTrack = trackParameters.getSpeedTrack();
double speedCommercial = trackParameters.getSpeedCommercial();
- int trackRoughnessId = trackParameters.getRailRoughness();
- int trackTransferId = trackParameters.getTrackTransfer();
- int impactId = trackParameters.getImpactNoise();
- int bridgeId = trackParameters.getBridgeTransfert();
+ String trackRoughnessId = trackParameters.getRailRoughness();
+ String trackTransferId = trackParameters.getTrackTransfer();
+ String impactId = trackParameters.getImpactNoise();
+ String bridgeId = trackParameters.getBridgeTransfert();
int curvature = trackParameters.getCurvature();
// get speed of the vehicle
@@ -341,16 +337,26 @@ private double[] getLWTraction(String typeVehicle, int runningCondition, String
private double[] getLWAero(String typeVehicle, double speed, String height, String fileVersion) {
double[] lWSpectre = new double[24];
- for (int idFreq = 0; idFreq < 24; idFreq++) {
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefAerodynamic");
- int refId = getVehicleNode(typeVehicle).get("RefAerodynamic").intValue();
- if (speed < 200 || refId == 0) {
- lWSpectre[idFreq] = -99;
- } else {
- lWSpectre[idFreq] = getAerodynamicNoise(typeVehicle, height, fileVersion, idFreq);
- double v0Aero = Double.parseDouble(getRailWayData().get("Vehicle").get("AerodynamicNoise").get(String.valueOf(refId)).get("V0").asText());
- double alphaAero = Double.parseDouble(getRailWayData().get("Vehicle").get("AerodynamicNoise").get(String.valueOf(refId)).get("Alpha").asText());
- lWSpectre[idFreq] = lWSpectre[idFreq] + alphaAero * Math.log10(speed / v0Aero);
+ // Resolve the aerodynamic noise node from JSON data
+ JsonNode aeroNode = (refId != null && !refId.isEmpty()) ?
+ getRailWayData().get("Vehicle").get("AerodynamicNoise").get(refId) : null;
+
+ // Check V0 value from JSON - if 0 or node not found, no aerodynamic noise
+ double v0Aero = 0;
+ double alphaAero = 0;
+ if (aeroNode != null) {
+ v0Aero = Double.parseDouble(aeroNode.get("V0").asText());
+ alphaAero = Double.parseDouble(aeroNode.get("Alpha").asText());
+ }
+
+ if (speed < 200 || aeroNode == null || v0Aero == 0) {
+ Arrays.fill(lWSpectre, -99);
+ } else {
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lWSpectre[idFreq] = getAerodynamicNoise(typeVehicle, height, fileVersion, idFreq)
+ + alphaAero * Math.log10(speed / v0Aero);
}
}
@@ -376,7 +382,7 @@ private double[] getLWAero(String typeVehicle, double speed, String height, Stri
**/
- private double[] getLWRolling(String typeVehicle, int trackRoughnessId, int impactId, int curvature, double speed, int trackTransferId, String trackFileVersion, double axlesPerVeh) {
+ private double[] getLWRolling(String typeVehicle, String trackRoughnessId, String impactId, int curvature, double speed, String trackTransferId, String trackFileVersion, double axlesPerVeh) {
double[] trackTransfer = new double[24];
double[] lWTr = new double[24];
@@ -422,22 +428,39 @@ private double[] getLWRolling(String typeVehicle, int trackRoughnessId, int impa
* @param axlesPerVeh
* @return
*/
- private double[] getLWBridge(String typeVehicle, int trackRoughnessId, int impactId, int bridgeId, double speed, String trackFileVersion, double axlesPerVeh) {
+ private double[] getLWBridge(String typeVehicle, String trackRoughnessId, String impactId, String bridgeId, double speed, String trackFileVersion, double axlesPerVeh) {
double[] lW = new double[24];
// roughnessLtot = CNOSSOS p.19 (2.3.7)
double[] roughnessLtot = checkNanValue(getLWRoughness(typeVehicle, trackRoughnessId, impactId, speed, trackFileVersion));
- double[] lWBridge = new double[24];
for (int idFreq = 0; idFreq < 24; idFreq++) {
lW[idFreq] = -99;
}
- if (trackFileVersion == "EU") {
- if (bridgeId == 3 || bridgeId == 4) {
- for (int idFreq = 0; idFreq < 24; idFreq++) {
- lWBridge[idFreq] = getBridgeStructural(bridgeId, idFreq);
- lW[idFreq] = roughnessLtot[idFreq] + lWBridge[idFreq] + 10 * Math.log10(axlesPerVeh);
+
+ // Resolve bridge node dynamically from JSON data
+ JsonNode bridgeNode = (bridgeId != null && !bridgeId.isEmpty()) ?
+ getRailWayData().get("Track").get("BridgeConstant").get(bridgeId) : null;
+
+ if (bridgeNode != null) {
+ // Check "Values" first, then "Value" (singular)
+ JsonNode valuesNode = bridgeNode.get("Values");
+ if (valuesNode == null) {
+ valuesNode = bridgeNode.get("Value");
+ }
+ if (valuesNode != null) {
+ if (valuesNode.isArray() && valuesNode.size() >= 24) {
+ // Frequency-dependent bridge structural spectrum (e.g. EU3, EU4)
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lW[idFreq] = roughnessLtot[idFreq] + valuesNode.get(idFreq).doubleValue() + 10 * Math.log10(axlesPerVeh);
+ }
+ } else if (valuesNode.isNumber() && valuesNode.doubleValue() != 0) {
+ // Uniform bridge constant (e.g. EU2, SNCF2)
+ double bridgeConstant = valuesNode.doubleValue();
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lW[idFreq] = roughnessLtot[idFreq] + bridgeConstant + 10 * Math.log10(axlesPerVeh);
+ }
}
}
}
@@ -456,7 +479,7 @@ private double[] getLWBridge(String typeVehicle, int trackRoughnessId, int impac
* @param speed impact reference
* @return Lroughness(freq)
**/
- private double[] getLWRoughness(String typeVehicle, int trackRoughnessId, int impactId, double speed, String trackFileVersion) {
+ private double[] getLWRoughness(String typeVehicle, String trackRoughnessId, String impactId, double speed, String trackFileVersion) {
double[] roughnessTotLambda = new double[35];
double[] roughnessLtot = new double[35];
@@ -465,6 +488,17 @@ private double[] getLWRoughness(String typeVehicle, int trackRoughnessId, int im
double[] freqMedLog = new double[24];
double[] Lambda = new double[35];
+ // Resolve impact noise node once before the loop
+ boolean hasImpactNoise = false;
+ if (impactId != null && !impactId.isEmpty()) {
+ JsonNode impactNode = getRailWayData().get("Track").get("ImpactNoise").get(impactId);
+ if (impactNode != null) {
+ // Check JoinDensity: if present and null, this is an empty sentinel entry
+ JsonNode joinDensity = impactNode.get("JoinDensity");
+ hasImpactNoise = joinDensity == null || !joinDensity.isNull();
+ }
+ }
+
double m = 33;
for (int idLambda = 0; idLambda < 35; idLambda++) {
Lambda[idLambda] = Math.pow(10, m / 10);
@@ -473,9 +507,10 @@ private double[] getLWRoughness(String typeVehicle, int trackRoughnessId, int im
roughnessTotLambda[idLambda] = Math.pow(10, getLRoughness(typeVehicle, trackRoughnessId, trackFileVersion, idLambda) / 10);
contactFilter[idLambda] = getContactFilter(typeVehicle, idLambda);
- roughnessLtot[idLambda] = 10 * Math.log10(roughnessTotLambda[idLambda]) + contactFilter[idLambda];
- if (impactId != 0) {
- roughnessLtot[idLambda] = 10 * Math.log10(Math.pow(10,roughnessLtot[idLambda]/ 10) + Math.pow(10, getImpactNoise(impactId, idLambda) / 10));
+ if (hasImpactNoise) {
+ roughnessLtot[idLambda] = 10 * Math.log10(roughnessTotLambda[idLambda]+ Math.pow(10, getImpactNoise(impactId, idLambda) / 10))+ contactFilter[idLambda];
+ } else {
+ roughnessLtot[idLambda] = 10 * Math.log10(roughnessTotLambda[idLambda]) + contactFilter[idLambda];
}
roughnessLtot[idLambda] = Math.pow(10, roughnessLtot[idLambda] / 10);
m--;
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayTrackCnossosParameters.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayTrackCnossosParameters.java
index 647da6e4f..b4b50a468 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayTrackCnossosParameters.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossos/RailwayTrackCnossosParameters.java
@@ -32,15 +32,15 @@
public class RailwayTrackCnossosParameters extends RailwayTrackParameters {
//set default value
- private int trackTransfer;
- private int railRoughness;
+ private String trackTransfer;
+ private String railRoughness;
private int curvature;
- private int impactNoise;
- private int bridgeTransfert;
+ private String impactNoise;
+ private String bridgeTransfert;
private String fileVersion = "FR"; // version of cnossos coefficient, if 2 == amendments 2019
- public RailwayTrackCnossosParameters(double speedTrack, int trackTransfer, int railRoughness, int impactNoise,
- int bridgeTransfert, int curvature, double speedCommercial, boolean isTunnel, int nTrack) {
+ public RailwayTrackCnossosParameters(double speedTrack, String trackTransfer, String railRoughness, String impactNoise,
+ String bridgeTransfert, int curvature, double speedCommercial, boolean isTunnel, int nTrack) {
setSpeedTrack(speedTrack);
setSpeedCommercial(speedCommercial);
@@ -64,35 +64,35 @@ public void setFileVersion(String fileVersion) {
}
- public int getTrackTransfer() {
+ public String getTrackTransfer() {
return trackTransfer;
}
- public void setTrackTransfer(int trackTransfer) {
+ public void setTrackTransfer(String trackTransfer) {
this.trackTransfer = trackTransfer;
}
- public int getRailRoughness() {
+ public String getRailRoughness() {
return railRoughness;
}
- public void setRailRoughness(int railRoughness) {
+ public void setRailRoughness(String railRoughness) {
this.railRoughness = railRoughness;
}
- public int getImpactNoise() {
+ public String getImpactNoise() {
return impactNoise;
}
- public void setImpactNoise(int impactNoise) {
+ public void setImpactNoise(String impactNoise) {
this.impactNoise = impactNoise;
}
- public int getBridgeTransfert() {
+ public String getBridgeTransfert() {
return bridgeTransfert;
}
- public void setBridgeTransfert(int bridgeTransfert) {
+ public void setBridgeTransfert(String bridgeTransfert) {
this.bridgeTransfert = bridgeTransfert;
}
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayCnossosvar.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayCnossosvar.java
new file mode 100644
index 000000000..4b610aa59
--- /dev/null
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayCnossosvar.java
@@ -0,0 +1,555 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+
+package org.noise_planet.noisemodelling.emission.railway.cnossosvar;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.NullNode;
+import org.noise_planet.noisemodelling.emission.LineSource;
+import org.noise_planet.noisemodelling.emission.railway.cnossos.RailWayCnossosParameters;
+import org.noise_planet.noisemodelling.emission.railway.cnossos.RailwayTrackCnossosParameters;
+import org.noise_planet.noisemodelling.emission.railway.cnossosvar.RailwayVehicleCnossosParametersvar;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Locale;
+
+import static java.lang.Math.min;
+import static org.noise_planet.noisemodelling.emission.utils.interpLinear.interpLinear;
+
+/**
+ * Railway noise evaluation from Cnossos reference : COMMISSION DIRECTIVE (EU) 2015/996
+ * of 19 May 2015 establishing common noise assessment methods according to Directive 2002/49/EC
+ * of the European Parliament and of the Council
+ *
+ * amending, for the purposes of adapting to scientific and technical progress, Annex II to
+ * Directive 2002/49/EC of the European Parliament and of the Council as regards
+ * common noise assessment methods
+ *
+ * part 2.3. Railway noise
+ *
+ * Return the dB value corresponding to the parameters
+ * @author Adrien Le Bellec, Université Gustave Eiffel
+ * @author Olivier Chiello, Université Gustave Eiffel
+ */
+
+public class RailwayCnossosvar extends org.noise_planet.noisemodelling.emission.railway.Railway {
+
+ public RailwayCnossosvar() {
+ }
+
+
+ /**
+ *
+ * @param inputStream
+ * @return
+ */
+ private static JsonNode parse(InputStream inputStream) {
+ try {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readTree(inputStream);
+ } catch (IOException ex) {
+ return NullNode.getInstance();
+ }
+ }
+
+ /**
+ *
+ * @param iterator
+ * @return an iterator
+ * @param
+ */
+ public static Iterable iteratorToIterable(Iterator iterator) {
+ return () -> iterator;
+ }
+
+ /**
+ * Helper to read a Ref field from a vehicle JSON node.
+ * Supports both string (new merged file) and integer (legacy files) JSON values.
+ * @param vehicleNode the JSON node of the vehicle
+ * @param fieldName the Ref field name (e.g. "RefRoughness")
+ * @return the string value of the reference
+ */
+ private static String getRefValue(JsonNode vehicleNode, String fieldName) {
+ JsonNode value = vehicleNode.get(fieldName);
+ if (value.isTextual()) {
+ return value.textValue();
+ }
+ return String.valueOf(value.intValue());
+ }
+
+ /**
+ * get Wheel Roughness by wavenumber - Only CNOSSOS
+ * @param typeVehicle
+ * @param fileVersion
+ * @param lambdaId
+ * @return
+ */
+ public Double getWheelRoughness(String typeVehicle, String fileVersion, int lambdaId) { //
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefRoughness");
+ return getRailWayData().get("Vehicle").get("WheelRoughness").get(refId).get("Values").get(lambdaId).doubleValue();
+ }
+
+ /**
+ * get Contact Filter by wavenumber - Only CNOSSOS
+ * @param typeVehicle type of a vehicle, for example : SNCF2
+ * @param lambdaId wavenumber (between 1 and 34 corresponding the normalised third octave bands from 2000mm to 0.8mm).
+ * @return contact filter
+ */
+ public Double getContactFilter(String typeVehicle, int lambdaId) { //
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefContact");
+ return getRailWayData().get("Vehicle").get("ContactFilter").get(refId).get("Values").get(lambdaId).doubleValue();
+ }
+
+ /**
+ * get Track Roughness Filter by wavenumber - Only CNOSSOS
+ * @param trackRoughnessId
+ * @param lambdaId
+ * @return
+ */
+ public Double getTrackRoughness(String trackRoughnessId, int lambdaId) { //
+ return getRailWayData().get("Track").get("RailRoughness").get(trackRoughnessId).get("Values").get(lambdaId).doubleValue();
+ }
+
+ /**
+ * Axles number by vehicles
+ * @param typeVehicle
+ * @return
+ */
+ public double getAxlesPerVeh(String typeVehicle) { //
+ return getVehicleNode(typeVehicle).get("NbAxlePerVeh").doubleValue();
+ }
+
+ /**
+ * get Nb of coach by vehicle
+ * @param typeVehicle
+ * @return
+ */
+ public int getNbCoach(String typeVehicle) { //
+ int nbCoach;
+ try {
+ nbCoach = getVehicleData().get(typeVehicle).get("NbCoach").intValue();
+ } catch (Exception e) {
+ nbCoach = 1;
+ }
+
+ return nbCoach;
+ }
+
+ /**
+ *
+ * @param typeVehicle
+ * @param runningCondition
+ * @param sourceHeightId
+ * @param fileVersion
+ * @param freqId
+ * @return
+ */
+ public double getTractionNoise(String typeVehicle, int runningCondition, String sourceHeightId, String fileVersion, int freqId) { //
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefTraction");
+ double tractionSpectrum = 0;
+
+ if (refId != null && !refId.isEmpty()) {
+ String condition = "ConstantSpeed";
+ switch (runningCondition) {
+ case 0:
+ condition = "ConstantSpeed";
+ break;
+ case 1:
+ condition = "AccelerationSpeed";
+ break;
+ case 3:
+ condition = "DecelerationSpeed";
+ break;
+ case 4:
+ condition = "IdlingSpeed";
+ break;
+ }
+ // Resolve traction node dynamically from JSON data
+ JsonNode tractionNode = getRailWayData().get("Vehicle").get(condition).get(refId);
+ if (tractionNode != null) {
+ try {
+ tractionSpectrum = tractionNode.get("Values").get(sourceHeightId).get(freqId).doubleValue();
+ } catch (NullPointerException ex) {
+ throw new IllegalArgumentException(String.format(Locale.ROOT, "Could not find traction spectrum for the following parameters " +
+ "getRailWayData(%s).get(\"Vehicle\").get(%s).get" +
+ "(%s).get(\"Values\").get(%s).get(%d)", fileVersion, condition, refId, sourceHeightId, freqId));
+ }
+ }
+ }
+ return tractionSpectrum;
+ }
+
+ /**
+ * retrieve the aerodynamic noise value for a specific type of vehicle, source height, file version, and frequency ID
+ * by accessing the corresponding data from the vehicle node, railway data, and noise values.
+ * @param typeVehicle
+ * @param sourceHeightId
+ * @param fileVersion
+ * @param freqId
+ * @return
+ */
+ public double getAerodynamicNoise(String typeVehicle, String sourceHeightId, String fileVersion, int freqId) { //
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefAerodynamic");
+ return getRailWayData().get("Vehicle").get("AerodynamicNoise").get(refId).get("Values").get(sourceHeightId).get(freqId).doubleValue();
+ }
+
+ /**
+ * retrieves the structural constant for a specific bridge and frequency ID
+ * by accessing the corresponding data from the railway track's bridge constants.
+ * @param bridgeId
+ * @param freqId
+ * @return
+ */
+ public Double getBridgeStructural(String bridgeId, int freqId) {
+ return getRailWayData().get("Track").get("BridgeConstant").get(bridgeId).get("Values").get(freqId).doubleValue();
+ }
+
+
+ /**
+ * fetches and returns the transfer value from the railway data for a specific track transfer ID and frequency ID.
+ * @param trackTransferId
+ * @param freqId
+ * @return
+ */
+ public Double getTrackTransfer(String trackTransferId, int freqId) { //
+ return getRailWayData().get("Track").get("TrackTransfer").get(trackTransferId).get("Spectre").get(freqId).doubleValue();
+ }
+
+ /**
+ * fetches and returns the impact noise value from the railway data for a specific impact noise ID and frequency ID.
+ * @param impactNoiseId
+ * @param freqId
+ * @return
+ */
+ public Double getImpactNoise(String impactNoiseId, int freqId) { //
+ return getRailWayData().get("Track").get("ImpactNoise").get(impactNoiseId).get("Values").get(freqId).doubleValue();
+ }
+
+ /**
+ * retrieve and return the transfer value associated with a given vehicle type and a specific frequency ID.
+ * @param typeVehicle
+ * @param freqId
+ * @return
+ */
+ public Double getVehTransfer(String typeVehicle, int freqId) {
+ String refTransfer = getRefValue(getVehicleNode(typeVehicle), "RefTransfer");
+ return getRailWayData().get("Vehicle").get("Transfer").get(refTransfer).get("Spectre").get(freqId).doubleValue();
+
+ }
+
+
+ /**
+ * calculates the total roughness level for a specific type of vehicle,
+ * track roughness, vehicle file version, and lambda ID by retrieving the wheel roughness and track roughness
+ * @param typeVehicle
+ * @param trackRoughnessId
+ * @param vehicleFileVersion
+ * @param idLambda
+ * @return
+ */
+ public Double getLRoughness(String typeVehicle, String trackRoughnessId, String vehicleFileVersion, int idLambda) { //
+ double wheelRoughness = getWheelRoughness(typeVehicle, vehicleFileVersion, idLambda);
+ double trackRoughness = getTrackRoughness(trackRoughnessId, idLambda);
+ return 10 * Math.log10(Math.pow(10, wheelRoughness / 10) + Math.pow(10, trackRoughness / 10));
+ }
+
+ /**
+ * Check if it exists some NaN values in the Roughness sound level.
+ * They appear when calculating the roughness in the frequency domain by interpolating the roughness in the wavelength domain
+ * @param roughnessLtot
+ * @return
+ */
+ private double[] checkNanValue(double[] roughnessLtot) {
+ int indice_NaN = 0;
+ for (int i = 0; i < roughnessLtot.length-2; i++) {
+ if (Double.isNaN(roughnessLtot[i])) {
+ indice_NaN++;
+ }
+ }
+ for (int i = 0; i < indice_NaN; i++) {
+ roughnessLtot[i] = roughnessLtot[indice_NaN + 1];
+ }
+ return roughnessLtot;
+ }
+
+ /**
+ * Evaluate the sound level for one Vehicle
+ * @param vehicleParameters Vehicle Noise emission parameters
+ * @param trackParameters Track Noise emission parameters
+ * constant speed
+ *
+ * @return LWRoll / LWTraction A and B / LWAerodynamic A and B / LWBridge level in dB
+ **/
+ public RailWayCnossosParameters evaluate(RailwayVehicleCnossosParametersvar vehicleParameters, RailwayTrackCnossosParameters trackParameters) throws IOException {
+
+ String vehicleFileVersion = vehicleParameters.getFileVersion();
+ String trackFileVersion = trackParameters.getFileVersion();
+ String typeVehicle = vehicleParameters.getTypeVehicle();
+
+ double speedVehicle = vehicleParameters.getSpeedVehicle();
+ double axlesPerVeh = getAxlesPerVeh(typeVehicle);
+ int runningCondition = vehicleParameters.getRunningCondition();
+
+ double speedTrack = trackParameters.getSpeedTrack();
+ double speedCommercial = trackParameters.getSpeedCommercial();
+ String trackRoughnessId = trackParameters.getRailRoughness();
+ String trackTransferId = trackParameters.getTrackTransfer();
+ String impactId = trackParameters.getImpactNoise();
+ String bridgeId = trackParameters.getBridgeTransfert();
+ int curvature = trackParameters.getCurvature();
+
+ // get speed of the vehicle
+ double speed = min(speedVehicle, min(speedTrack, speedCommercial));
+
+ boolean isTunnel = trackParameters.getIsTunnel();
+ // %% Take into account the number of coach and the number of units
+ // 10*log10(NbUnit*NbCoach);
+
+ RailWayCnossosParameters railWayParameters = new RailWayCnossosParameters();
+
+ if (isTunnel) {
+ return railWayParameters;
+ } else {
+ // Rolling noise calcul
+ double[] lW = getLWRolling(typeVehicle, trackRoughnessId, impactId, curvature, speed, trackTransferId, trackFileVersion, axlesPerVeh);
+ railWayParameters.addRailwaySource("ROLLING", new LineSource(lW,0.5, "ROLLING"));
+ lW = getLWTraction(typeVehicle, runningCondition, "A", vehicleFileVersion);
+ railWayParameters.addRailwaySource("TRACTIONA", new LineSource(lW,0.05, "TRACTIONA"));
+ lW = getLWTraction(typeVehicle, runningCondition, "B", vehicleFileVersion);
+ railWayParameters.addRailwaySource("TRACTIONB", new LineSource(lW,4, "TRACTIONB"));
+ lW = getLWAero(typeVehicle, speed, "A", vehicleFileVersion);
+ railWayParameters.addRailwaySource("AERODYNAMICA", new LineSource(lW,0.05, "AERODYNAMICA"));
+ lW = getLWAero(typeVehicle, speed, "B", vehicleFileVersion);
+ railWayParameters.addRailwaySource("AERODYNAMICB", new LineSource(lW,4, "AERODYNAMICB"));
+ lW = getLWBridge(typeVehicle, trackRoughnessId, impactId, bridgeId, speed, trackFileVersion,axlesPerVeh);
+ railWayParameters.addRailwaySource("BRIDGE", new LineSource(lW,0.5, "BRIDGE"));
+
+ return railWayParameters;
+ }
+ }
+
+ /**
+ * traction or Aerodynamic Level.
+ * @param typeVehicle vehicle data base
+ * @param height height source
+ * @return lWSpectre(freq) (Traction or Aerodynamic)
+ **/
+ private double[] getLWTraction(String typeVehicle, int runningCondition, String height, String fileVersion) {
+ double[] lWSpectre = new double[24];
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lWSpectre[idFreq] = getTractionNoise(typeVehicle, runningCondition, height, fileVersion, idFreq);
+ }
+ return lWSpectre;
+ }
+
+ /**
+ * traction or Aerodynamic Level.
+ * @param typeVehicle vehicle data base
+ * @param speed min speed between vehicle and track
+ * @param height height source
+ * @return lWSpectre(freq) (Traction or Aerodynamic)
+ **/
+ private double[] getLWAero(String typeVehicle, double speed, String height, String fileVersion) {
+ double[] lWSpectre = new double[24];
+
+ String refId = getRefValue(getVehicleNode(typeVehicle), "RefAerodynamic");
+
+ // Resolve the aerodynamic noise node from JSON data
+ JsonNode aeroNode = (refId != null && !refId.isEmpty()) ?
+ getRailWayData().get("Vehicle").get("AerodynamicNoise").get(refId) : null;
+
+ // Check V0 value from JSON - if 0 or node not found, no aerodynamic noise
+ double v0Aero = 0;
+ double alphaAero = 0;
+ if (aeroNode != null) {
+ v0Aero = Double.parseDouble(aeroNode.get("V0").asText());
+ alphaAero = Double.parseDouble(aeroNode.get("Alpha").asText());
+ }
+
+ if (speed < 200 || aeroNode == null || v0Aero == 0) {
+ Arrays.fill(lWSpectre, -99);
+ } else {
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lWSpectre[idFreq] = getAerodynamicNoise(typeVehicle, height, fileVersion, idFreq)
+ + alphaAero * Math.log10(speed / v0Aero);
+ }
+ }
+
+ return lWSpectre;
+ }
+
+
+ /**
+ * Rolling Level.
+ * @param typeVehicle vehicle data base
+ * @param trackRoughnessId track Roughness reference
+ * @param impactId impact reference
+ * @param speed min speed between vehicle and track
+ *
+ * Step 1
+ * wavelength to frequecy (evaluateRoughnessLtotFreq)
+ * Step 2
+ * calcul sound power of wheel and bogie emission
+ * calcul sound power of rail sleeper and ballast/slab emission
+ * todo add sound power of superstructure emission ?
+ *
+ * @return lWRoll(freq)
+ **/
+
+
+ private double[] getLWRolling(String typeVehicle, String trackRoughnessId, String impactId, int curvature, double speed, String trackTransferId, String trackFileVersion, double axlesPerVeh) {
+
+ double[] trackTransfer = new double[24];
+ double[] lWTr = new double[24];
+ double[] vehTransfer = new double[24];
+ double[] lWVeh = new double[24];
+ double[] lW = new double[24];
+
+ // roughnessLtot = CNOSSOS p.19 (2.3.7)
+ double[] roughnessLtot = checkNanValue(getLWRoughness(typeVehicle, trackRoughnessId, impactId, speed, trackFileVersion));
+
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ // lWTr = CNOSSOS p.20 (2.3.8)
+ trackTransfer[idFreq] = getTrackTransfer(trackTransferId, idFreq);
+ lWTr[idFreq] = roughnessLtot[idFreq] + trackTransfer[idFreq] + 10 * Math.log10(axlesPerVeh);
+
+ // lWVeh = CNOSSOS p.20 (2.3.9)
+ vehTransfer[idFreq] = getVehTransfer(typeVehicle, idFreq);
+ lWVeh[idFreq] = roughnessLtot[idFreq] + vehTransfer[idFreq] + 10 * Math.log10(axlesPerVeh);
+ // lWRoll = CNOSSOS p.19 (2.3.7)
+ lW[idFreq] = 10 * Math.log10(Math.pow(10, lWTr[idFreq] / 10) + Math.pow(10, lWVeh[idFreq] / 10));
+ if (curvature == 1) {
+ lW[idFreq] = lW[idFreq] + 5;
+ } else if (curvature == 2) {
+ lW[idFreq] = lW[idFreq] + 8;
+ } else if (curvature == 3) {
+ lW[idFreq] = lW[idFreq] + 8;
+ }
+ }
+
+ return lW;
+ }
+
+
+ /**
+ * method calculates the overall sound power level for a specific type of vehicle, track roughness,
+ * impact ID, bridge ID, speed, track file version, and number of axles per vehicle
+ * @param typeVehicle
+ * @param trackRoughnessId
+ * @param impactId
+ * @param bridgeId
+ * @param speed
+ * @param trackFileVersion
+ * @param axlesPerVeh
+ * @return
+ */
+ private double[] getLWBridge(String typeVehicle, String trackRoughnessId, String impactId, String bridgeId, double speed, String trackFileVersion, double axlesPerVeh) {
+
+ double[] lW = new double[24];
+
+ // roughnessLtot = CNOSSOS p.19 (2.3.7)
+ double[] roughnessLtot = checkNanValue(getLWRoughness(typeVehicle, trackRoughnessId, impactId, speed, trackFileVersion));
+
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lW[idFreq] = -99;
+ }
+
+ // Resolve bridge node dynamically from JSON data
+ JsonNode bridgeNode = (bridgeId != null && !bridgeId.isEmpty()) ?
+ getRailWayData().get("Track").get("BridgeConstant").get(bridgeId) : null;
+
+ if (bridgeNode != null) {
+ // Check "Values" first, then "Value" (singular)
+ JsonNode valuesNode = bridgeNode.get("Values");
+ if (valuesNode == null) {
+ valuesNode = bridgeNode.get("Value");
+ }
+ if (valuesNode != null) {
+ if (valuesNode.isArray() && valuesNode.size() >= 24) {
+ // Frequency-dependent bridge structural spectrum (e.g. EU3, EU4)
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lW[idFreq] = roughnessLtot[idFreq] + valuesNode.get(idFreq).doubleValue() + 10 * Math.log10(axlesPerVeh);
+ }
+ } else if (valuesNode.isNumber() && valuesNode.doubleValue() != 0) {
+ // Uniform bridge constant (e.g. EU2, SNCF2)
+ double bridgeConstant = valuesNode.doubleValue();
+ for (int idFreq = 0; idFreq < 24; idFreq++) {
+ lW[idFreq] = roughnessLtot[idFreq] + bridgeConstant + 10 * Math.log10(axlesPerVeh);
+ }
+ }
+ }
+ }
+
+ return lW;
+ }
+
+
+
+ /**
+ * Roughness Level.
+ * linear interpolation wavelength to frequency
+ * @param typeVehicle vehicle data base
+ * @param trackRoughnessId track Roughness reference
+ * @param impactId impact reference
+ * @param speed impact reference
+ * @return Lroughness(freq)
+ **/
+ private double[] getLWRoughness(String typeVehicle, String trackRoughnessId, String impactId, double speed, String trackFileVersion) {
+
+ double[] roughnessTotLambda = new double[35];
+ double[] roughnessLtot = new double[35];
+ double[] contactFilter = new double[35];
+ double[] lambdaToFreqLog = new double[35];
+ double[] freqMedLog = new double[24];
+ double[] Lambda = new double[35];
+
+ // Resolve impact noise node once before the loop
+ boolean hasImpactNoise = false;
+ if (impactId != null && !impactId.isEmpty()) {
+ JsonNode impactNode = getRailWayData().get("Track").get("ImpactNoise").get(impactId);
+ if (impactNode != null) {
+ // Check JoinDensity: if present and null, this is an empty sentinel entry
+ JsonNode joinDensity = impactNode.get("JoinDensity");
+ hasImpactNoise = joinDensity == null || !joinDensity.isNull();
+ }
+ }
+
+ double m = 33;
+ for (int idLambda = 0; idLambda < 35; idLambda++) {
+ Lambda[idLambda] = Math.pow(10, m / 10);
+ lambdaToFreqLog[idLambda] = Math.log10(speed / Lambda[idLambda] * 1000 / 3.6);
+
+ roughnessTotLambda[idLambda] = Math.pow(10, getLRoughness(typeVehicle, trackRoughnessId, trackFileVersion, idLambda) / 10);
+
+ contactFilter[idLambda] = getContactFilter(typeVehicle, idLambda);
+ roughnessLtot[idLambda] = 10 * Math.log10(roughnessTotLambda[idLambda]) + contactFilter[idLambda];
+ if (hasImpactNoise) {
+ roughnessLtot[idLambda] = 10 * Math.log10(Math.pow(10,roughnessLtot[idLambda]/ 10) + Math.pow(10, getImpactNoise(impactId, idLambda) / 10));
+ }
+ roughnessLtot[idLambda] = Math.pow(10, roughnessLtot[idLambda] / 10);
+ m--;
+ }
+ for (int idFreqMed = 0; idFreqMed < 24; idFreqMed++) {
+ freqMedLog[idFreqMed] = Math.log10(Math.pow(10, (17 + Double.valueOf(idFreqMed)) / 10));
+ }
+
+ double[] roughnessLtotFreq = interpLinear(lambdaToFreqLog, roughnessLtot, freqMedLog);
+
+ for (int idRoughnessLtotFreq = 0; idRoughnessLtotFreq < 24; idRoughnessLtotFreq++) {
+ roughnessLtotFreq[idRoughnessLtotFreq] = 10 * Math.log10(roughnessLtotFreq[idRoughnessLtotFreq]);
+ }
+ return roughnessLtotFreq;
+ }
+
+}
+
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayVehicleCnossosParametersvar.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayVehicleCnossosParametersvar.java
new file mode 100644
index 000000000..8b86a000f
--- /dev/null
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/cnossosvar/RailwayVehicleCnossosParametersvar.java
@@ -0,0 +1,71 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+
+package org.noise_planet.noisemodelling.emission.railway.cnossosvar;
+
+/**
+ * Railway noise evaluation from Cnossos reference : COMMISSION DIRECTIVE (EU) 2015/996
+ * of 19 May 2015 establishing common noise assessment methods according to Directive 2002/49/EC
+ * of the European Parliament and of the Council
+ *
+ * amending, for the purposes of adapting to scientific and technical progress, Annex II to
+ * Directive 2002/49/EC of the European Parliament and of the Council as regards
+ * common noise assessment methods
+ *
+ * part 2.3. Railway noise
+ *
+ * @author Adrien Le Bellec, Université Gustave Eiffel
+ * @author Olivier Chiello, Université Gustave Eiffel
+ */
+
+import org.noise_planet.noisemodelling.emission.railway.RailwayVehicleParametersvar;
+
+/**
+ * Parameters Vehicule
+ */
+
+public class RailwayVehicleCnossosParametersvar extends RailwayVehicleParametersvar {
+
+ // set default value
+ private int runningCondition = 0; // 0 = constand speed, 1 = acceleration , 2 =decceleration, 3 = idling
+ private double idlingTime = 0; // if idling, idling time (seconds)
+ private String fileVersion = "FR"; // version of cnossos coefficient, if 2 == amendments 2019
+
+ public RailwayVehicleCnossosParametersvar(String typeVehicle, double speedVehicle, int runningCondition, double idlingTime) {
+ setTypeVehicle(typeVehicle);
+ setSpeedVehicle(speedVehicle);
+
+ setRunningCondition(runningCondition);
+ setIdlingTime(idlingTime);
+ }
+
+ public String getFileVersion() {
+ return this.fileVersion;
+ }
+
+ public void setFileVersion(String fileVersion) {
+ this.fileVersion = fileVersion;
+ }
+
+ public int getRunningCondition() {
+ return runningCondition;
+ }
+
+ public void setRunningCondition(int runningCondition) {
+ this.runningCondition = runningCondition;
+ }
+
+ public double getIdlingTime() {
+ return idlingTime;
+ }
+
+ public void setIdlingTime(double idlingTime) {
+ this.idlingTime = idlingTime;
+ }
+}
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/nmpb/RailwayNMPB.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/nmpb/RailwayNMPB.java
index e0b87aab7..10137c2e0 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/nmpb/RailwayNMPB.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/railway/nmpb/RailwayNMPB.java
@@ -238,7 +238,7 @@ public int getNbCoach(String typeVehicle) { //
public double getSpectre(String typeVehicle, String ref, int runningCondition, String sourceHeight, int spectreVer, int freqId) { //
int refId = getNMPBVehicleNode(typeVehicle).get(ref).intValue();
if (ref.equals("RefTraction")) {
- double tractionSpectre = 0;
+ double tractionSpectrum = 0;
String condition = "ConstantSpeed";
if (refId != 0) {
switch (runningCondition) {
@@ -256,14 +256,14 @@ public double getSpectre(String typeVehicle, String ref, int runningCondition, S
break;
}
try {
- tractionSpectre = getNMPBRailWayData(spectreVer).get("Vehicle").get(condition).get(String.valueOf(refId)).get("Values").get(sourceHeight).get(freqId).doubleValue();
+ tractionSpectrum = getNMPBRailWayData(spectreVer).get("Vehicle").get(condition).get(String.valueOf(refId)).get("Values").get(sourceHeight).get(freqId).doubleValue();
} catch (NullPointerException ex) {
throw new IllegalArgumentException(String.format(Locale.ROOT, "Could not find traction spectrum for the following parameters " +
"getNMPBRailWayData(%d).get(\"Vehicle\").get(%s).get(String.valueOf" +
"(%d)).get(\"Values\").get(%s).get(%d)", spectreVer, condition, refId, sourceHeight, freqId));
}
}
- return tractionSpectre;
+ return tractionSpectrum;
} else if (ref.equals("RefAerodynamic")) {
double aerodynamicNoise;
aerodynamicNoise = getNMPBRailWayData(spectreVer).get("Vehicle").get("AerodynamicNoise").get(String.valueOf(refId)).get("Values").get(sourceHeight).get(freqId).doubleValue();
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossos.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossos.java
index ba491a599..5ade32f6b 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossos.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossos.java
@@ -350,17 +350,19 @@ public static double evaluate(RoadCnossosParameters roadCnossosParameters) throw
*/
// Effect of the acceleration and deceleration of vehicles
// Todo Here, we should get the Junc_dist by another way that we are doing now to be more precise issue #524
- double coefficientJunctionDistance = Math.max(1 - Math.abs(Junc_dist) / 100, 0);
- // Effect of the acceleration and deceleration of vehicles - Rolling Noise Eq 2.2.17
- lvRoadLvl = lvRoadLvl + getCr("1", Junc_type, coeffVer) * coefficientJunctionDistance;
- medRoadLvl = medRoadLvl + getCr("2", Junc_type, coeffVer) * coefficientJunctionDistance;
- hgvRoadLvl = hgvRoadLvl + getCr("3", Junc_type, coeffVer) * coefficientJunctionDistance;
- // Effect of the acceleration and deceleration of vehicles - Propulsion Noise Eq 2.2.18
- lvMotorLvl = lvMotorLvl + getCp("1", Junc_type, coeffVer) * coefficientJunctionDistance;
- medMotorLvl = medMotorLvl + getCp("2", Junc_type, coeffVer) * coefficientJunctionDistance;
- hgvMotorLvl = hgvMotorLvl + getCp("3", Junc_type, coeffVer) * coefficientJunctionDistance;
- wheelaMotorLvl = wheelaMotorLvl + getCp("4a", Junc_type, coeffVer) * coefficientJunctionDistance;
- wheelbMotorLvl = wheelbMotorLvl + getCp("4b", Junc_type, coeffVer) * coefficientJunctionDistance;
+ if(Junc_type > 0) { // Junc_type = 0, no junction
+ double coefficientJunctionDistance = Math.max(1 - Math.abs(Junc_dist) / 100, 0);
+ // Effect of the acceleration and deceleration of vehicles - Rolling Noise Eq 2.2.17
+ lvRoadLvl = lvRoadLvl + getCr("1", Junc_type, coeffVer) * coefficientJunctionDistance;
+ medRoadLvl = medRoadLvl + getCr("2", Junc_type, coeffVer) * coefficientJunctionDistance;
+ hgvRoadLvl = hgvRoadLvl + getCr("3", Junc_type, coeffVer) * coefficientJunctionDistance;
+ // Effect of the acceleration and deceleration of vehicles - Propulsion Noise Eq 2.2.18
+ lvMotorLvl = lvMotorLvl + getCp("1", Junc_type, coeffVer) * coefficientJunctionDistance;
+ medMotorLvl = medMotorLvl + getCp("2", Junc_type, coeffVer) * coefficientJunctionDistance;
+ hgvMotorLvl = hgvMotorLvl + getCp("3", Junc_type, coeffVer) * coefficientJunctionDistance;
+ wheelaMotorLvl = wheelaMotorLvl + getCp("4a", Junc_type, coeffVer) * coefficientJunctionDistance;
+ wheelbMotorLvl = wheelbMotorLvl + getCp("4b", Junc_type, coeffVer) * coefficientJunctionDistance;
+ }
// Effect of the type of road surface - Eq. 2.2.19
lvRoadLvl = lvRoadLvl + getNoiseLvl(getA_RoadSurfaceCoeff(freqParam, "1", roadCnossosParameters.getRoadSurface(), coeffVer), getB_RoadSurfaceCoeff("1", roadSurface, coeffVer), roadCnossosParameters.getSpeedLv(), 70.);
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossosParameters.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossosParameters.java
index 4ffcbfc6b..f50ca5350 100644
--- a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossosParameters.java
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/road/cnossos/RoadCnossosParameters.java
@@ -35,7 +35,7 @@ public class RoadCnossosParameters {
private double Junc_dist = 250; // Distance to junction
- private int Junc_type; // Junction type (k=1 traffic lights, k=2 roundabout)
+ private int Junc_type; // Junction type (k=0 None, k=1 traffic lights, k=2 roundabout)
private double slopePercentage = 0; // slope s (in %), In the case of a bi-directional traffic flow, it is necessary to split the flow into two components and correct half for uphill and half for downhill.
private int way = 1; // 1 = direct, 2 = inverse, 3 = double
@@ -65,14 +65,14 @@ public RoadCnossosParameters() {
*
* @param lv_speed Average light vehicle speed
* @param mv_speed Average medium vehicle speed
- * @param hgv_speed Average heavy goods vehicle speed
+ * @param hgv_speed Average heavy vehicle speed
* @param wav_speed Average light 2 wheels vehicle speed
* @param wbv_speed Average heavy 2 wheels vehicle speed
* @param lvPerHour Average light vehicle per hour
- * @param mvPerHour Average heavy vehicle per hour
- * @param hgvPerHour Average heavy vehicle per hour
- * @param wavPerHour Average heavy vehicle per hour
- * @param wbvPerHour Average heavy vehicle per hour
+ * @param mvPerHour Average medium vehicles per hour
+ * @param hgvPerHour Average heavy vehicles per hour
+ * @param wavPerHour Average light 2 wheels vehicles per hour
+ * @param wbvPerHour Average heavy 2 wheels vehicles per hour
* @param frequency Studied Frequency (must be octave band)
* @param Temperature Temperature (Celsius)
* @param roadSurface roadSurface empty default, NL01 FR01 .. (look at src/main/resources/org/noise_planet/noisemodelling/emission/RoadCnossos_2020.json)
diff --git a/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/utils/UriUtils.java b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/utils/UriUtils.java
new file mode 100644
index 000000000..25b036315
--- /dev/null
+++ b/noisemodelling-emission/src/main/java/org/noise_planet/noisemodelling/emission/utils/UriUtils.java
@@ -0,0 +1,79 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+package org.noise_planet.noisemodelling.emission.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.Set;
+
+public class UriUtils {
+
+ private static final Set ALLOWED_SCHEMES = Set.of("http", "https", "file");
+
+ private static final HttpClient CLIENT = HttpClient.newBuilder()
+ .connectTimeout(Duration.ofSeconds(10))
+ .build();
+
+ /**
+ * Validates if a string is a well-formed URI and uses an approved scheme
+ * (http, https, or file).
+ *
+ * @param uriString the string to validate; can be null or empty
+ * @return {@code true} if the string is a valid URI with an allowed scheme,
+ * {@code false} otherwise
+ */
+ public static boolean isValidUri(String uriString) {
+ if (uriString == null || uriString.isBlank()) {
+ return false;
+ }
+
+ try {
+ URI uri = new URI(uriString);
+ String scheme = uri.getScheme();
+ return scheme != null && ALLOWED_SCHEMES.contains(scheme.toLowerCase());
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Fetches a resource as an InputStream using the modern HttpClient API.
+ * This method validates the HTTP status code before returning the stream.
+ *
+ * @param urlString The full URL of the resource.
+ * @return An InputStream of the response body.
+ * @throws IOException If a network error occurs, or the server returns a non-200 code or the operation is interrupted.
+ */
+ public static InputStream openSafeStream(String urlString) throws IOException {
+ HttpRequest request = HttpRequest.newBuilder()
+ .uri(URI.create(urlString))
+ .timeout(Duration.ofSeconds(30))
+ .GET()
+ .build();
+
+ try {
+ HttpResponse response = CLIENT.send(request, HttpResponse.BodyHandlers.ofInputStream());
+
+ if (response.statusCode() != 200) {
+ throw new IOException("Failed to fetch " + urlString + ". HTTP Status: " + response.statusCode());
+ }
+
+ return response.body();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IOException("Request was interrupted", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayEmissionCnossos.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayEmissionCnossos.json
new file mode 100644
index 000000000..988d1b075
--- /dev/null
+++ b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayEmissionCnossos.json
@@ -0,0 +1,6464 @@
+{
+ "Vehicle": {
+ "Transfer": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty vehicle transfer function",
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60,
+ 60
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel with diameter 920 mm, no measure",
+ "WheelDiameter": 920,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 75.4,
+ 77.3,
+ 81.1,
+ 84.1,
+ 83.3,
+ 84.3,
+ 86,
+ 90.1,
+ 89.8,
+ 89,
+ 88.8,
+ 90.4,
+ 92.4,
+ 94.9,
+ 100.4,
+ 104.6,
+ 109.6,
+ 114.9,
+ 115,
+ 115,
+ 115.5,
+ 115.6,
+ 116,
+ 116.7
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel with diameter 840 mm, no measure",
+ "WheelDiameter": 840,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 75.4,
+ 77.3,
+ 81.1,
+ 84.1,
+ 82.8,
+ 83.3,
+ 84.1,
+ 86.9,
+ 87.9,
+ 89.9,
+ 90.9,
+ 91.5,
+ 91.5,
+ 93,
+ 98.7,
+ 101.6,
+ 107.6,
+ 111.9,
+ 114.5,
+ 114.5,
+ 115,
+ 115.1,
+ 115.5,
+ 116.2
+ ]
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel with diameter 680 mm, no measure",
+ "WheelDiameter": 680,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 75.4,
+ 77.3,
+ 81.1,
+ 84.1,
+ 82.8,
+ 83.3,
+ 83.9,
+ 86.3,
+ 88,
+ 92.2,
+ 93.9,
+ 92.5,
+ 90.9,
+ 90.4,
+ 93.2,
+ 93.5,
+ 99.6,
+ 104.9,
+ 108,
+ 111,
+ 111.5,
+ 111.6,
+ 112,
+ 112.7
+ ]
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel with diameter 1200 mm, no measure",
+ "WheelDiameter": 1200,
+ "WheelDiameterCode": null,
+ "Spectre": [
+ 75.4,
+ 77.3,
+ 81.1,
+ 84.1,
+ 82.8,
+ 83.3,
+ 84.5,
+ 90.4,
+ 90.4,
+ 89.9,
+ 90.1,
+ 91.3,
+ 91.5,
+ 93.6,
+ 100.5,
+ 104.6,
+ 115.6,
+ 115.9,
+ 116,
+ 116,
+ 116.5,
+ 116.6,
+ 117,
+ 117.7
+ ]
+ },
+ "SNCF1": {
+ "Description": "Roue-de-diametre-1200-mm",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 66.2,
+ 70.2,
+ 78.2,
+ 90.9,
+ 89.1,
+ 91.8,
+ 93.8,
+ 96.9,
+ 97.3,
+ 100.8,
+ 106.1,
+ 112.8,
+ 119.1,
+ 121.0,
+ 121.9,
+ 123.4,
+ 126.4,
+ 128.0,
+ 128.9,
+ 130.3,
+ 131.0,
+ 126.7,
+ 124.7,
+ 122.7
+ ]
+ },
+ "SNCF2": {
+ "Description": "Roue-de-diametre-920-mm",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 66.2,
+ 68.2,
+ 72.2,
+ 79.0,
+ 80.0,
+ 82.3,
+ 84.4,
+ 87.4,
+ 89.4,
+ 92.3,
+ 94.9,
+ 101.8,
+ 108.3,
+ 112.2,
+ 113.7,
+ 115.2,
+ 119.0,
+ 122.4,
+ 124.8,
+ 125.0,
+ 124.1,
+ 120.1,
+ 121.2,
+ 122.2
+ ]
+ },
+ "SNCF3": {
+ "Description": "Roue-de-diametre-840-mm",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 65.9,
+ 67.6,
+ 73.5,
+ 73.3,
+ 78.0,
+ 81.8,
+ 86.4,
+ 86.1,
+ 86.7,
+ 90.8,
+ 96.7,
+ 103.1,
+ 107.4,
+ 111.6,
+ 115.2,
+ 112.4,
+ 116.0,
+ 118.1,
+ 119.7,
+ 120.1,
+ 119.4,
+ 116.4,
+ 116.8,
+ 117.5
+ ]
+ },
+ "SNCF4": {
+ "Description": "Roue-de-diametre-680-mm",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 64.6,
+ 66.1,
+ 72.9,
+ 73.7,
+ 77.2,
+ 78.1,
+ 85.4,
+ 85.6,
+ 86.1,
+ 90.4,
+ 96.7,
+ 102.3,
+ 105.5,
+ 108.9,
+ 113.1,
+ 107.0,
+ 107.4,
+ 104.6,
+ 106.5,
+ 106.8,
+ 104.9,
+ 102.6,
+ 103.0,
+ 103.7
+ ]
+ }
+ },
+ "WheelRoughness": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty wheel roughness",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Values": [
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Values": [
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25,
+ 25
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "cast iron tread",
+ "Values": [
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.168461586134174,
+ 2.367004589322595,
+ 0.614781263900754,
+ 2.610924413363449,
+ 5.756528561202869,
+ 8.778461586134174,
+ 11.11001151291529,
+ 10.95837155865965,
+ 9.791459375057219,
+ 7.491604590973592,
+ 5.148695207491488,
+ 3.022589893365378,
+ 1.28616199715323,
+ 0.167997434442427,
+ -0.702413370803574,
+ -1.167439464293351,
+ -1.02041103167661,
+ 0.271557355244967,
+ 0.238403459692385,
+ 1.321311142906995,
+ 3.097108457482452,
+ 3.097108457482452,
+ 3.097108457482452,
+ 3.097108457482452,
+ 3.097108457482452
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "composite",
+ "Values": [
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -3.95419018689975,
+ -4.341299537395543,
+ -4.620974767837635,
+ -4.897055026064954,
+ -5.222436281288831,
+ -6.308578167786185,
+ -6.798467723797528,
+ -7.240263355022869,
+ -7.269624675426479,
+ -7.271295786579055,
+ -7.139967758266058,
+ -6.937736627618217,
+ -6.6786309297519,
+ -5.991369963895746,
+ -3.748676578106019,
+ -2.376692988528715,
+ -2.590206897753383,
+ -2.464430278717203,
+ -2.464430278717203,
+ -2.464430278717203,
+ -2.464430278717203,
+ -2.464430278717203
+ ]
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "disc brake",
+ "Values": [
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ -5.93,
+ 2.32,
+ 2.82,
+ 2.56,
+ 1.22,
+ 2.06,
+ 0.92,
+ -0.25,
+ -1.57,
+ -2.85,
+ -4.94,
+ -7,
+ -8.58,
+ -9.32,
+ -9.51,
+ -10.12,
+ -10.25,
+ -10.33,
+ -10.81,
+ -10.91,
+ -9.52,
+ -9.52,
+ -9.52,
+ -9.52,
+ -9.52,
+ -9.52,
+ -9.52
+ ]
+ },
+ "SNCF1": {
+ "Description": "Freins-semelles-fonte",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 1.7,
+ 1.7,
+ 1.8,
+ 2.2,
+ 2.8,
+ 3.4,
+ 3.8,
+ 4.1,
+ 4.4,
+ 4.6,
+ 4.8,
+ 5.3,
+ 5.3,
+ 5.8,
+ 6.6,
+ 7.9,
+ 7.8,
+ 7,
+ 5.7,
+ 3.9,
+ 1.3,
+ -0.9,
+ -2.5,
+ -4.4,
+ -6.5,
+ -8.8,
+ -11.4,
+ -12.7,
+ -13.6,
+ -14.2,
+ -14.6,
+ -14.8,
+ -15,
+ -15,
+ -15
+ ]
+ },
+ "SNCF2": {
+ "Description": "Freins-semelles-composites",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 2,
+ 2,
+ 2,
+ 2,
+ 2.1,
+ 2.1,
+ 2.1,
+ 2.1,
+ 2.1,
+ 2.1,
+ 0.6,
+ 0,
+ -0.9,
+ -1.7,
+ -2.2,
+ -3.3,
+ -4.2,
+ -5.7,
+ -7.2,
+ -8.1,
+ -9.5,
+ -10.2,
+ -10.9,
+ -11.7,
+ -12.7,
+ -13.8,
+ -15.3,
+ -17.8,
+ -17.8,
+ -17.8,
+ -17.8,
+ -17.8,
+ -17.8,
+ -17.8,
+ -17.8
+ ]
+ },
+ "SNCF3": {
+ "Description": "Freins-semelles-composites-+-disque",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -12.5,
+ -14.5,
+ -10.9,
+ -13.3,
+ -14.8,
+ -13.5,
+ -14.3,
+ -14.7,
+ -15,
+ -14.5,
+ -15,
+ -14.5,
+ -14.5,
+ -16.7,
+ -17.5,
+ -17.5,
+ -17.9,
+ -18.9,
+ -20.7,
+ -20.7,
+ -20.7,
+ -20.6,
+ -20.6,
+ -20.6,
+ -20.5,
+ -20.5
+ ]
+ },
+ "SNCF4": {
+ "Description": "Freins-a-disque",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 7,
+ 7,
+ 7,
+ 7,
+ 6.5,
+ 1,
+ 2,
+ 2.4,
+ 1,
+ 0.2,
+ 1.2,
+ -1.2,
+ -2.1,
+ -2.7,
+ -3.5,
+ -4.3,
+ -5.4,
+ -6.2,
+ -6.6,
+ -7,
+ -7.6,
+ -7.7,
+ -8,
+ -8.5,
+ -8.9,
+ -9.5,
+ -10.8,
+ -12.1,
+ -13.7,
+ -15.8,
+ -18.3,
+ -21,
+ -23.5,
+ -25.6,
+ -28
+ ]
+ }
+ },
+ "ContactFilter": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty contact filter",
+ "Load": null,
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Load": null,
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Values": [
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30,
+ -30
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Load": null,
+ "WheelDiameter": null,
+ "WheelDiameterCode": null,
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel load 50kN wheel diameter 360mm",
+ "Load": "50",
+ "WheelDiameter": "360",
+ "WheelDiameterCode": "small, <500mm",
+ "Values": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.6,
+ -1.0,
+ -1.8,
+ -3.2,
+ -5.4,
+ -8.7,
+ -12.2,
+ -16.7,
+ -17.7,
+ -17.8,
+ -20.7,
+ -22.1,
+ -22.8,
+ -24.0,
+ -24.5,
+ -24.7,
+ -27.0,
+ -27.8
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel load 50kN wheel diameter 680mm",
+ "Load": "50",
+ "WheelDiameter": "680",
+ "WheelDiameterCode": "medium, 500 to 800mm",
+ "Values": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.7,
+ -1.2,
+ -2.0,
+ -4.1,
+ -6.0,
+ -9.2,
+ -13.8,
+ -17.2,
+ -17.7,
+ -18.6,
+ -21.5,
+ -22.3,
+ -23.1,
+ -24.4,
+ -24.5,
+ -25.0,
+ -28.0,
+ -28.8,
+ -29.6
+ ]
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel load 25kN wheel diameter 920mm",
+ "Load": "25",
+ "WheelDiameter": "920",
+ "WheelDiameterCode": "large, >800mm",
+ "Values": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ -0.1,
+ -0.3,
+ -0.5,
+ -1.1,
+ -1.8,
+ -3.3,
+ -5.3,
+ -7.9,
+ -12.8,
+ -16.8,
+ -17.7,
+ -18.2,
+ -20.5,
+ -22.0,
+ -22.8,
+ -24.2,
+ -24.5,
+ -25.0,
+ -27.4,
+ -28.2,
+ -29.0
+ ]
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel load 50kN wheel diameter 920mm",
+ "Load": "50",
+ "WheelDiameter": "920",
+ "WheelDiameterCode": "large, >800mm",
+ "Values": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ -0.1,
+ -0.1,
+ -0.3,
+ -0.6,
+ -1.1,
+ -1.3,
+ -3.5,
+ -5.3,
+ -8.0,
+ -12.0,
+ -16.8,
+ -17.7,
+ -18.0,
+ -21.5,
+ -21.8,
+ -22.8,
+ -24.0,
+ -24.5,
+ -25.0,
+ -27.3,
+ -28.1,
+ -28.9,
+ -29.7
+ ]
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wheel load 100kN wheel diameter 920mm",
+ "Load": "100",
+ "WheelDiameter": "920",
+ "WheelDiameterCode": "large, >800mm",
+ "Values": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.6,
+ -1.0,
+ -1.8,
+ -3.2,
+ -5.4,
+ -8.7,
+ -12.2,
+ -16.7,
+ -17.7,
+ -17.8,
+ -20.7,
+ -22.1,
+ -22.8,
+ -24.0,
+ -24.5,
+ -24.7,
+ -27.0,
+ -27.8,
+ -28.6,
+ -29.4,
+ -30.2
+ ]
+ },
+ "SNCF1": {
+ "Description": "Charge-a-l-es-sieu-50kN-Diametre-de-roue-360mm",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.6,
+ -1,
+ -1.8,
+ -3.2,
+ -5.4,
+ -8.7,
+ -12.2,
+ -16.7,
+ -17.7,
+ -17.8,
+ -20.7,
+ -22.1,
+ -22.8,
+ -24,
+ -24.5,
+ -24.7,
+ -27,
+ -27.8
+ ]
+ },
+ "SNCF2": {
+ "Description": "Charge-a-l-es-sieu-50kN-Diametre-de-roue-680mm",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.7,
+ -1.2,
+ -2,
+ -4.1,
+ -6,
+ -9.2,
+ -13.8,
+ -17.2,
+ -17.7,
+ -18.6,
+ -21.5,
+ -22.3,
+ -23.1,
+ -24.4,
+ -24.5,
+ -25,
+ -28,
+ -28.8,
+ -29.6
+ ]
+ },
+ "SNCF3": {
+ "Description": "Charge-a-l-es-sieu-25kN-Diametre-de-roue-920mm",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ -0.1,
+ -0.3,
+ -0.5,
+ -1.1,
+ -1.8,
+ -3.3,
+ -5.3,
+ -7.9,
+ -12.8,
+ -16.8,
+ -17.7,
+ -18.2,
+ -20.5,
+ -22,
+ -22.8,
+ -24.2,
+ -24.5,
+ -25,
+ -27.4,
+ -28.2,
+ -29
+ ]
+ },
+ "SNCF4": {
+ "Description": "Charge-a-l-es-sieu-50kN-Diametre-de-roue-920mm",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ -0.1,
+ -0.1,
+ -0.3,
+ -0.6,
+ -1.1,
+ -1.3,
+ -3.5,
+ -5.3,
+ -8,
+ -12,
+ -16.8,
+ -17.7,
+ -18,
+ -21.5,
+ -21.8,
+ -22.8,
+ -24,
+ -24.5,
+ -25,
+ -27.3,
+ -28.1,
+ -28.9,
+ -29.7
+ ]
+ },
+ "SNCF5": {
+ "Description": "Charge-a-l-es-sieu-100kN-Diametre-de-roue-920mm",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ -0.1,
+ -0.2,
+ -0.3,
+ -0.6,
+ -1,
+ -1.8,
+ -3.2,
+ -5.4,
+ -8.7,
+ -12.2,
+ -16.7,
+ -17.7,
+ -17.8,
+ -20.7,
+ -22.1,
+ -22.8,
+ -24,
+ -24.5,
+ -24.7,
+ -27,
+ -27.8,
+ -28.6,
+ -29.4,
+ -30.2
+ ]
+ }
+ },
+ "ConstantSpeed": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty traction",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Values": {
+ "A": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ],
+ "B": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ }
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 800kW)",
+ "Values": {
+ "A": [
+ 98.8613,
+ 94.7613,
+ 92.5613,
+ 94.5613,
+ 92.7613,
+ 92.7613,
+ 92.9613,
+ 94.7613,
+ 94.5613,
+ 95.6613,
+ 95.5613,
+ 98.5613,
+ 95.1613,
+ 95.0613,
+ 95.0613,
+ 94.0613,
+ 94.0613,
+ 99.3613,
+ 92.4613,
+ 89.4613,
+ 86.9613,
+ 84.0613,
+ 81.4613,
+ 79.1613
+ ],
+ "B": [
+ 103.1613,
+ 99.9613,
+ 95.4613,
+ 93.9613,
+ 93.2613,
+ 93.5613,
+ 92.8613,
+ 92.6613,
+ 92.3613,
+ 92.7613,
+ 92.7613,
+ 96.7613,
+ 92.6613,
+ 92.9613,
+ 92.8613,
+ 93.0613,
+ 93.1613,
+ 98.2613,
+ 91.4613,
+ 88.6613,
+ 85.9613,
+ 83.3613,
+ 80.8613,
+ 78.6613
+ ]
+ }
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 2200kW)",
+ "Values": {
+ "A": [
+ 99.4103,
+ 107.3103,
+ 103.1103,
+ 102.1103,
+ 99.3103,
+ 99.3103,
+ 99.5103,
+ 101.3103,
+ 101.1103,
+ 102.2103,
+ 102.1103,
+ 101.1103,
+ 101.7103,
+ 101.6103,
+ 99.3103,
+ 96.0103,
+ 93.7103,
+ 101.9103,
+ 89.5103,
+ 87.1103,
+ 90.5103,
+ 31.4103,
+ 81.2103,
+ 79.6103
+ ],
+ "B": [
+ 103.7103,
+ 112.5103,
+ 106.0103,
+ 101.5103,
+ 99.8103,
+ 100.1103,
+ 99.4103,
+ 99.2103,
+ 98.9103,
+ 99.3103,
+ 99.3103,
+ 99.3103,
+ 99.2103,
+ 99.5103,
+ 97.1103,
+ 95.0103,
+ 92.8103,
+ 100.8103,
+ 88.5103,
+ 86.3103,
+ 89.5103,
+ 30.7103,
+ 80.6103,
+ 79.1103
+ ]
+ }
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/RENFE Dloco/1155kW",
+ "Values": {
+ "A": [
+ 99.3003,
+ 103.2003,
+ 109.0003,
+ 114.0003,
+ 109.2003,
+ 106.2003,
+ 103.4003,
+ 105.2003,
+ 105.0003,
+ 106.1003,
+ 106.0003,
+ 105.0003,
+ 105.6003,
+ 104.5003,
+ 102.9003,
+ 99.4003,
+ 96.8003,
+ 94.6003,
+ 92.1003,
+ 89.6003,
+ 87.5003,
+ 85.1003,
+ 82.9003,
+ 81.1003
+ ],
+ "B": [
+ 103.6003,
+ 108.4003,
+ 111.9003,
+ 113.4003,
+ 109.7003,
+ 107.0003,
+ 103.3003,
+ 103.1003,
+ 102.8003,
+ 103.2003,
+ 103.2003,
+ 103.2003,
+ 103.1003,
+ 102.4003,
+ 100.7003,
+ 98.4003,
+ 95.9003,
+ 93.5003,
+ 91.1003,
+ 88.8003,
+ 86.5003,
+ 84.4003,
+ 82.3003,
+ 80.6003
+ ]
+ }
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/NS6400 Dloco/1180kW",
+ "Values": {
+ "A": [
+ 100.9836,
+ 89.8836,
+ 92.6836,
+ 104.6836,
+ 94.8836,
+ 87.8836,
+ 83.0836,
+ 84.8836,
+ 84.6836,
+ 85.7836,
+ 85.6836,
+ 84.6836,
+ 85.2836,
+ 85.1836,
+ 85.1836,
+ 84.1836,
+ 84.1836,
+ 84.4836,
+ 81.5836,
+ 78.5836,
+ 76.0836,
+ 73.1836,
+ 70.5836,
+ 68.2836
+ ],
+ "B": [
+ 105.2836,
+ 95.0836,
+ 95.5836,
+ 104.0836,
+ 95.3836,
+ 88.6836,
+ 82.9836,
+ 82.7836,
+ 82.4836,
+ 82.8836,
+ 82.8836,
+ 82.8836,
+ 82.7836,
+ 83.0836,
+ 82.9836,
+ 83.1836,
+ 83.2836,
+ 83.3836,
+ 80.5836,
+ 77.7836,
+ 75.0836,
+ 72.4836,
+ 69.9836,
+ 67.7836
+ ]
+ }
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/TKOJ JT42CWR/Class66/2200kW",
+ "Values": {
+ "A": [
+ 95.432,
+ 104.332,
+ 102.132,
+ 99.132,
+ 93.332,
+ 96.332,
+ 96.532,
+ 98.332,
+ 98.132,
+ 99.232,
+ 99.132,
+ 98.132,
+ 98.732,
+ 96.632,
+ 94.632,
+ 91.632,
+ 89.632,
+ 87.932,
+ 86.032,
+ 84.032,
+ 82.532,
+ 80.632,
+ 79.032,
+ 77.732
+ ],
+ "B": [
+ 99.732,
+ 109.532,
+ 105.032,
+ 98.532,
+ 93.832,
+ 97.132,
+ 96.432,
+ 96.232,
+ 95.932,
+ 96.332,
+ 96.332,
+ 96.332,
+ 96.232,
+ 94.532,
+ 92.432,
+ 90.632,
+ 88.732,
+ 86.832,
+ 85.032,
+ 83.232,
+ 81.532,
+ 79.932,
+ 78.432,
+ 77.232
+ ]
+ }
+ },
+ "EU8": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel multiple unit",
+ "Values": {
+ "A": [
+ 82.583,
+ 82.483,
+ 89.283,
+ 90.283,
+ 93.483,
+ 99.483,
+ 98.683,
+ 95.483,
+ 90.283,
+ 91.383,
+ 91.283,
+ 90.283,
+ 90.883,
+ 91.783,
+ 92.783,
+ 92.783,
+ 90.783,
+ 88.083,
+ 85.183,
+ 83.183,
+ 81.683,
+ 78.783,
+ 76.183,
+ 73.883
+ ],
+ "B": [
+ 86.883,
+ 87.683,
+ 92.183,
+ 89.683,
+ 93.983,
+ 100.283,
+ 98.583,
+ 93.383,
+ 88.083,
+ 88.483,
+ 88.483,
+ 88.483,
+ 88.383,
+ 89.683,
+ 90.583,
+ 91.783,
+ 89.883,
+ 86.983,
+ 84.183,
+ 82.383,
+ 80.683,
+ 78.083,
+ 75.583,
+ 73.383
+ ]
+ }
+ },
+ "EU9": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric locomotive",
+ "Values": {
+ "A": [
+ 87.8551,
+ 90.7551,
+ 91.5551,
+ 94.5551,
+ 94.7551,
+ 96.7551,
+ 103.9551,
+ 100.7551,
+ 99.5551,
+ 101.6551,
+ 98.5551,
+ 95.5551,
+ 95.1551,
+ 96.0551,
+ 92.0551,
+ 89.0551,
+ 87.0551,
+ 85.3551,
+ 83.4551,
+ 81.4551,
+ 79.9551,
+ 78.0551,
+ 76.4551,
+ 75.1551
+ ],
+ "B": [
+ 92.1551,
+ 95.9551,
+ 94.4551,
+ 93.9551,
+ 95.2551,
+ 97.5551,
+ 103.8551,
+ 98.6551,
+ 97.3551,
+ 98.7551,
+ 95.7551,
+ 93.7551,
+ 92.6551,
+ 93.9551,
+ 89.8551,
+ 88.0551,
+ 86.1551,
+ 84.2551,
+ 82.4551,
+ 80.6551,
+ 78.9551,
+ 77.3551,
+ 75.8551,
+ 74.6551
+ ]
+ }
+ },
+ "EU10": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric multiple unit ",
+ "Values": {
+ "A": [
+ 80.5497,
+ 81.4497,
+ 80.5497,
+ 82.2497,
+ 80.0497,
+ 79.7497,
+ 79.6497,
+ 96.4497,
+ 80.5497,
+ 81.3497,
+ 97.2497,
+ 79.5497,
+ 79.8497,
+ 86.7497,
+ 81.7497,
+ 82.7497,
+ 80.7497,
+ 78.0497,
+ 75.1497,
+ 72.1497,
+ 69.6497,
+ 66.7497,
+ 64.1497,
+ 61.8497
+ ],
+ "B": [
+ 84.8497,
+ 86.6497,
+ 83.4497,
+ 81.6497,
+ 80.5497,
+ 80.5497,
+ 79.5497,
+ 94.3497,
+ 78.3497,
+ 78.4497,
+ 94.4497,
+ 77.7497,
+ 77.3497,
+ 84.6497,
+ 79.5497,
+ 81.7497,
+ 79.8497,
+ 76.9497,
+ 74.1497,
+ 71.3497,
+ 68.6497,
+ 66.0497,
+ 63.5497,
+ 61.3497
+ ]
+ }
+ },
+ "SNCF1": {
+ "Description": "Trains-a-grande-vitesse",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 88.9,
+ 88.8,
+ 88.7,
+ 89.2,
+ 86.1,
+ 93.2,
+ 89.8,
+ 83.3,
+ 85.9,
+ 86.1,
+ 84.7,
+ 84,
+ 82.5,
+ 83.6,
+ 80.6,
+ 80.2,
+ 78,
+ 76.4,
+ 75.5,
+ 74.4,
+ 70.8,
+ 66.7,
+ 61.5,
+ 59.5
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF2": {
+ "Description": "Automoteur-type-A",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 95.2,
+ 94.8,
+ 92.1,
+ 90.3,
+ 88.9,
+ 90,
+ 91.4,
+ 95.6,
+ 96.1,
+ 91,
+ 95,
+ 92.5,
+ 91,
+ 91.8,
+ 90.8,
+ 89.7,
+ 88.4,
+ 87.4,
+ 86.3,
+ 82.5,
+ 80.3,
+ 78.4,
+ 72.5,
+ 70.7
+ ]
+ }
+ },
+ "SNCF3": {
+ "Description": "Automoteur-type-B",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 82.1,
+ 81.7,
+ 79.0,
+ 77.2,
+ 75.8,
+ 76.9,
+ 78.3,
+ 82.5,
+ 83.0,
+ 77.9,
+ 81.9,
+ 79.4,
+ 77.9,
+ 78.7,
+ 77.7,
+ 76.6,
+ 75.3,
+ 74.3,
+ 73.2,
+ 69.4,
+ 67.2,
+ 65.3,
+ 59.4,
+ 58.8
+ ]
+ }
+ },
+ "SNCF4": {
+ "Description": "Automoteur-type-C",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "B": [
+ 88,
+ 87.5,
+ 85,
+ 83.5,
+ 80.2,
+ 85.2,
+ 82.5,
+ 80.8,
+ 80.9,
+ 76.6,
+ 77.5,
+ 77,
+ 75.9,
+ 75.6,
+ 75.5,
+ 75.2,
+ 73.3,
+ 70.5,
+ 69.6,
+ 67.6,
+ 65.4,
+ 63.3,
+ 56.9,
+ 55.4
+ ],
+ "A": [
+ 69.7,
+ 69.7,
+ 69.7,
+ 77.9,
+ 74.5,
+ 79.9,
+ 74.4,
+ 69.4,
+ 69,
+ 67.9,
+ 67.8,
+ 66.5,
+ 65.4,
+ 64.9,
+ 64.5,
+ 63.4,
+ 62.3,
+ 60.8,
+ 67.5,
+ 65.5,
+ 59.7,
+ 58,
+ 57.9,
+ 57.8
+ ]
+ }
+ },
+ "SNCF5": {
+ "Description": "Automoteur-type-D",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 77.5,
+ 77,
+ 75.6,
+ 80.3,
+ 79,
+ 78.1,
+ 79.1,
+ 78.6,
+ 76.9,
+ 75.9,
+ 73.8,
+ 74.4,
+ 73.8,
+ 75,
+ 75.3,
+ 69.1,
+ 69,
+ 68.3,
+ 66.3,
+ 63.6,
+ 60.6,
+ 57.9,
+ 55.5,
+ 55.1
+ ]
+ }
+ },
+ "SNCF6": {
+ "Description": "Automoteur-type-E",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 78.8,
+ 79.5,
+ 85.1,
+ 89.2,
+ 84.7,
+ 83.4,
+ 83.9,
+ 86.9,
+ 85.3,
+ 80.9,
+ 79.4,
+ 80.8,
+ 82.7,
+ 79.7,
+ 77.8,
+ 77.2,
+ 77.6,
+ 74.8,
+ 73.8,
+ 72.6,
+ 66.8,
+ 63.8,
+ 59,
+ 58.6
+ ]
+ }
+ },
+ "SNCF7": {
+ "Description": "Automoteur-type-F",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 93.3,
+ 94.7,
+ 100.3,
+ 104.4,
+ 99.9,
+ 98.6,
+ 99.1,
+ 102.1,
+ 100.5,
+ 96.1,
+ 94.6,
+ 96.1,
+ 97.9,
+ 95,
+ 93,
+ 92.4,
+ 92.8,
+ 90,
+ 89.1,
+ 87.8,
+ 82.1,
+ 79,
+ 74.2,
+ 73.7
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF8": {
+ "Description": "Automoteur-type-G",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 88.5,
+ 90.5,
+ 96.1,
+ 100.2,
+ 95.7,
+ 94.4,
+ 94.9,
+ 97.9,
+ 96.3,
+ 91.9,
+ 90.4,
+ 91.9,
+ 93.7,
+ 90.8,
+ 88.8,
+ 88.2,
+ 88.6,
+ 85.8,
+ 84.9,
+ 83.6,
+ 77.9,
+ 74.8,
+ 70,
+ 68.1
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF9": {
+ "Description": "Automoteur-type-H",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 61.4,
+ 61.8,
+ 62.6,
+ 65,
+ 69.4,
+ 73.3,
+ 71.6,
+ 70.9,
+ 76.3,
+ 75.2,
+ 76,
+ 75.6,
+ 83.5,
+ 76.9,
+ 72.8,
+ 73.1,
+ 73.6,
+ 68.9,
+ 66.2,
+ 64.1,
+ 62.4,
+ 54.8,
+ 43.4,
+ 39.6
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF10": {
+ "Description": "Rames-remorquees",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF11": {
+ "Description": "Voitures-remorquees-type-A",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 82.1,
+ 81.4,
+ 78.7,
+ 76.9,
+ 75.5,
+ 76.6,
+ 78,
+ 82.2,
+ 82.7,
+ 77.6,
+ 81.6,
+ 79.1,
+ 77.6,
+ 78.4,
+ 77.4,
+ 76.3,
+ 75,
+ 74,
+ 72.9,
+ 69.1,
+ 66.9,
+ 65,
+ 59.1,
+ 57.8
+ ]
+ }
+ },
+ "SNCF12": {
+ "Description": "Voitures-remorquees-type-B",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 66.7,
+ 67.2,
+ 68,
+ 70.4,
+ 74.8,
+ 78.7,
+ 77,
+ 76.3,
+ 81.7,
+ 80.6,
+ 81.4,
+ 81,
+ 88.9,
+ 82.3,
+ 78.2,
+ 78.5,
+ 79,
+ 74.3,
+ 71.6,
+ 69.5,
+ 67.8,
+ 60.2,
+ 48.8,
+ 46.6
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF13": {
+ "Description": "Tram-train",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 78.2,
+ 78,
+ 77.7,
+ 78.4,
+ 75.8,
+ 80.6,
+ 82.1,
+ 82.6,
+ 80,
+ 77.2,
+ 76.2,
+ 74.8,
+ 75.1,
+ 73.9,
+ 71.4,
+ 70.9,
+ 69.6,
+ 67.5,
+ 66.5,
+ 60.1,
+ 58.2,
+ 54.7,
+ 50.7,
+ 48.6
+ ]
+ }
+ },
+ "SNCF14": {
+ "Description": "Locomotive-type-A",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 96.4,
+ 96.7,
+ 97.2,
+ 91.5,
+ 92.1,
+ 89.9,
+ 89.2,
+ 88.6,
+ 88.6,
+ 90.2,
+ 91.8,
+ 92.3,
+ 90,
+ 90.2,
+ 88.3,
+ 84.6,
+ 86.2,
+ 78.6,
+ 74.7,
+ 71,
+ 70.2,
+ 70,
+ 66.8,
+ 65.7
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "SNCF15": {
+ "Description": "Locomotive-type-B",
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 90.9,
+ 90.5,
+ 89.2,
+ 87.1,
+ 87.3,
+ 84.5,
+ 88.2,
+ 92.3,
+ 96,
+ 89.6,
+ 92.8,
+ 87.9,
+ 87.1,
+ 87.7,
+ 86.4,
+ 84.6,
+ 86,
+ 83.3,
+ 86.9,
+ 76.7,
+ 75,
+ 71.4,
+ 66.2,
+ 64.8
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ }
+ },
+ "AccelerationSpeed": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty traction",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Values": {
+ "A": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ],
+ "B": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ }
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 800kW)",
+ "Values": {
+ "A": [
+ 96.8613,
+ 102.7613,
+ 100.5613,
+ 97.5613,
+ 95.7613,
+ 95.7613,
+ 95.9613,
+ 97.7613,
+ 97.5613,
+ 98.6613,
+ 98.5613,
+ 102.5613,
+ 98.1613,
+ 98.0613,
+ 98.0613,
+ 97.0613,
+ 97.0613,
+ 97.3613,
+ 105.4613,
+ 97.4613,
+ 94.9613,
+ 92.0613,
+ 89.4613,
+ 87.1613
+ ],
+ "B": [
+ 101.1613,
+ 107.9613,
+ 103.4613,
+ 96.9613,
+ 96.2613,
+ 96.5613,
+ 95.8613,
+ 95.6613,
+ 95.3613,
+ 95.7613,
+ 95.7613,
+ 100.7613,
+ 95.6613,
+ 95.9613,
+ 95.8613,
+ 96.0613,
+ 96.1613,
+ 96.2613,
+ 104.4613,
+ 96.6613,
+ 93.9613,
+ 91.3613,
+ 88.8613,
+ 86.6613
+ ]
+ }
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 2200kW)",
+ "Values": {
+ "A": [
+ 99.4103,
+ 102.3103,
+ 113.1103,
+ 107.1103,
+ 103.3103,
+ 102.3103,
+ 102.5103,
+ 104.3103,
+ 104.1103,
+ 105.2103,
+ 105.1103,
+ 104.1103,
+ 104.7103,
+ 104.6103,
+ 102.3103,
+ 99.0103,
+ 96.7103,
+ 94.7103,
+ 105.0103,
+ 90.1103,
+ 88.3103,
+ 93.6103,
+ 84.2103,
+ 82.6103
+ ],
+ "B": [
+ 103.7103,
+ 107.5103,
+ 116.0103,
+ 106.5103,
+ 103.8103,
+ 103.1103,
+ 102.4103,
+ 102.2103,
+ 101.9103,
+ 102.3103,
+ 102.3103,
+ 102.3103,
+ 102.2103,
+ 102.5103,
+ 100.1103,
+ 98.0103,
+ 95.8103,
+ 93.6103,
+ 104.0103,
+ 89.3103,
+ 87.3103,
+ 92.9103,
+ 83.6103,
+ 82.1103
+ ]
+ }
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/RENFE Dloco/1155kW",
+ "Values": {
+ "A": [
+ 99.3003,
+ 103.2003,
+ 109.0003,
+ 114.0003,
+ 115.2003,
+ 112.2003,
+ 109.4003,
+ 108.2003,
+ 108.0003,
+ 109.1003,
+ 109.0003,
+ 108.0003,
+ 108.6003,
+ 108.5003,
+ 107.5003,
+ 104.9003,
+ 102.4003,
+ 100.1003,
+ 97.7003,
+ 95.1003,
+ 93.1003,
+ 90.6003,
+ 88.5003,
+ 86.6003
+ ],
+ "B": [
+ 103.6003,
+ 108.4003,
+ 111.9003,
+ 113.4003,
+ 115.7003,
+ 113.0003,
+ 109.3003,
+ 106.1003,
+ 105.8003,
+ 106.2003,
+ 106.2003,
+ 106.2003,
+ 106.1003,
+ 106.4003,
+ 105.3003,
+ 103.9003,
+ 101.5003,
+ 99.0003,
+ 96.7003,
+ 94.3003,
+ 92.1003,
+ 89.9003,
+ 87.9003,
+ 86.1003
+ ]
+ }
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/NS6400 Dloco/1180kW",
+ "Values": {
+ "A": [
+ 98.9836,
+ 104.8836,
+ 95.6836,
+ 97.6836,
+ 105.8836,
+ 97.8836,
+ 91.0836,
+ 87.8836,
+ 87.6836,
+ 88.7836,
+ 88.6836,
+ 87.6836,
+ 88.2836,
+ 88.1836,
+ 88.1836,
+ 87.1836,
+ 87.1836,
+ 87.4836,
+ 87.5836,
+ 84.5836,
+ 82.0836,
+ 79.1836,
+ 76.5836,
+ 74.2836
+ ],
+ "B": [
+ 103.2836,
+ 110.0836,
+ 98.5836,
+ 97.0836,
+ 106.3836,
+ 98.6836,
+ 90.9836,
+ 85.7836,
+ 85.4836,
+ 85.8836,
+ 85.8836,
+ 85.8836,
+ 85.7836,
+ 86.0836,
+ 85.9836,
+ 86.1836,
+ 86.2836,
+ 86.3836,
+ 86.5836,
+ 83.7836,
+ 81.0836,
+ 78.4836,
+ 75.9836,
+ 73.7836
+ ]
+ }
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/TKOJ JT42CWR/Class66/2200kW",
+ "Values": {
+ "A": [
+ 91.432,
+ 99.332,
+ 110.132,
+ 107.132,
+ 100.332,
+ 96.332,
+ 99.532,
+ 101.332,
+ 101.132,
+ 102.232,
+ 102.132,
+ 101.132,
+ 101.732,
+ 101.632,
+ 99.632,
+ 96.632,
+ 94.632,
+ 92.932,
+ 91.032,
+ 89.032,
+ 87.532,
+ 85.632,
+ 84.032,
+ 82.732
+ ],
+ "B": [
+ 95.732,
+ 104.532,
+ 113.032,
+ 106.532,
+ 100.832,
+ 97.132,
+ 99.432,
+ 99.232,
+ 98.932,
+ 99.332,
+ 99.332,
+ 99.332,
+ 99.232,
+ 99.532,
+ 97.432,
+ 95.632,
+ 93.732,
+ 91.832,
+ 90.032,
+ 88.232,
+ 86.532,
+ 84.932,
+ 83.432,
+ 82.232
+ ]
+ }
+ },
+ "EU8": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel multiple unit",
+ "Values": {
+ "A": [
+ 80.583,
+ 86.483,
+ 88.283,
+ 94.283,
+ 91.483,
+ 96.483,
+ 102.683,
+ 103.483,
+ 98.283,
+ 94.383,
+ 94.283,
+ 93.283,
+ 93.883,
+ 93.783,
+ 94.783,
+ 94.783,
+ 95.783,
+ 94.083,
+ 91.183,
+ 88.183,
+ 86.683,
+ 84.783,
+ 82.183,
+ 79.883
+ ],
+ "B": [
+ 84.883,
+ 91.683,
+ 91.183,
+ 93.683,
+ 91.983,
+ 97.283,
+ 102.583,
+ 101.383,
+ 96.083,
+ 91.483,
+ 91.483,
+ 91.483,
+ 91.383,
+ 91.683,
+ 92.583,
+ 93.783,
+ 94.883,
+ 92.983,
+ 90.183,
+ 87.383,
+ 85.683,
+ 84.083,
+ 81.583,
+ 79.383
+ ]
+ }
+ },
+ "EU9": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric locomotive",
+ "Values": {
+ "A": [
+ 87.8551,
+ 94.7551,
+ 91.5551,
+ 94.5551,
+ 94.7551,
+ 96.7551,
+ 107.9551,
+ 100.7551,
+ 99.5551,
+ 105.6551,
+ 98.5551,
+ 95.5551,
+ 95.1551,
+ 100.0551,
+ 92.0551,
+ 89.0551,
+ 87.0551,
+ 85.3551,
+ 83.4551,
+ 81.4551,
+ 79.9551,
+ 78.0551,
+ 76.4551,
+ 75.1551
+ ],
+ "B": [
+ 92.1551,
+ 99.9551,
+ 94.4551,
+ 93.9551,
+ 95.2551,
+ 97.5551,
+ 107.8551,
+ 98.6551,
+ 97.3551,
+ 102.7551,
+ 95.7551,
+ 93.7551,
+ 92.6551,
+ 97.9551,
+ 89.8551,
+ 88.0551,
+ 86.1551,
+ 84.2551,
+ 82.4551,
+ 80.6551,
+ 78.9551,
+ 77.3551,
+ 75.8551,
+ 74.6551
+ ]
+ }
+ },
+ "EU10": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric multiple unit ",
+ "Values": {
+ "A": [
+ 80.5497,
+ 81.4497,
+ 80.5497,
+ 82.2497,
+ 94.4497,
+ 79.7497,
+ 79.6497,
+ 96.4497,
+ 80.5497,
+ 81.3497,
+ 80.8497,
+ 79.5497,
+ 79.8497,
+ 86.7497,
+ 81.7497,
+ 82.7497,
+ 80.7497,
+ 78.0497,
+ 75.1497,
+ 72.1497,
+ 69.6497,
+ 66.7497,
+ 64.1497,
+ 61.8497
+ ],
+ "B": [
+ 84.8497,
+ 86.6497,
+ 83.4497,
+ 81.6497,
+ 94.9497,
+ 80.5497,
+ 79.5497,
+ 94.3497,
+ 78.3497,
+ 78.4497,
+ 78.0497,
+ 77.7497,
+ 77.3497,
+ 84.6497,
+ 79.5497,
+ 81.7497,
+ 79.8497,
+ 76.9497,
+ 74.1497,
+ 71.3497,
+ 68.6497,
+ 66.0497,
+ 63.5497,
+ 61.3497
+ ]
+ }
+ }
+ },
+ "DecelerationSpeed": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty traction",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Values": {
+ "A": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ],
+ "B": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ }
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 800kW)",
+ "Values": {
+ "A": [
+ 87.8613,
+ 83.7613,
+ 86.5613,
+ 88.5613,
+ 86.7613,
+ 86.7613,
+ 86.9613,
+ 88.7613,
+ 88.5613,
+ 89.6613,
+ 89.5613,
+ 93.5613,
+ 89.1613,
+ 89.0613,
+ 89.0613,
+ 88.0613,
+ 88.0613,
+ 86.3613,
+ 83.4613,
+ 80.4613,
+ 77.9613,
+ 75.0613,
+ 72.4613,
+ 70.1613
+ ],
+ "B": [
+ 92.1613,
+ 88.9613,
+ 89.4613,
+ 87.9613,
+ 87.2613,
+ 87.5613,
+ 86.8613,
+ 86.6613,
+ 86.3613,
+ 86.7613,
+ 86.7613,
+ 91.7613,
+ 86.6613,
+ 86.9613,
+ 86.8613,
+ 87.0613,
+ 87.1613,
+ 85.2613,
+ 82.4613,
+ 79.6613,
+ 76.9613,
+ 74.3613,
+ 71.8613,
+ 69.6613
+ ]
+ }
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 2200kW)",
+ "Values": {
+ "A": [
+ 89.4103,
+ 90.3103,
+ 103.1103,
+ 95.1103,
+ 93.3103,
+ 93.3103,
+ 93.5103,
+ 95.3103,
+ 95.1103,
+ 96.2103,
+ 96.1103,
+ 95.1103,
+ 95.7103,
+ 95.6103,
+ 93.3103,
+ 96.6103,
+ 87.7103,
+ 85.7103,
+ 87.0103,
+ 81.1103,
+ 79.3103,
+ 77.1103,
+ 75.2103,
+ 73.6103
+ ],
+ "B": [
+ 93.7103,
+ 95.5103,
+ 106.0103,
+ 94.5103,
+ 93.8103,
+ 94.1103,
+ 93.4103,
+ 93.2103,
+ 92.9103,
+ 93.3103,
+ 93.3103,
+ 93.3103,
+ 93.2103,
+ 93.5103,
+ 91.1103,
+ 95.6103,
+ 86.8103,
+ 84.6103,
+ 86.0103,
+ 80.3103,
+ 78.3103,
+ 76.4103,
+ 74.6103,
+ 73.1103
+ ]
+ }
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/RENFE Dloco/1155kW",
+ "Values": {
+ "A": [
+ 99.3003,
+ 103.2003,
+ 103.0003,
+ 102.0003,
+ 97.2003,
+ 97.2003,
+ 97.4003,
+ 99.2003,
+ 99.0003,
+ 100.1003,
+ 100.0003,
+ 98.0003,
+ 97.0003,
+ 94.4003,
+ 91.8003,
+ 88.3003,
+ 85.7003,
+ 83.5003,
+ 81.0003,
+ 78.5003,
+ 76.4003,
+ 74.0003,
+ 71.9003,
+ 70.1003
+ ],
+ "B": [
+ 103.6003,
+ 108.4003,
+ 105.9003,
+ 101.4003,
+ 97.7003,
+ 98.0003,
+ 97.3003,
+ 97.1003,
+ 96.8003,
+ 97.2003,
+ 97.2003,
+ 96.2003,
+ 94.5003,
+ 92.3003,
+ 89.6003,
+ 87.3003,
+ 84.8003,
+ 82.4003,
+ 80.0003,
+ 77.7003,
+ 75.4003,
+ 73.3003,
+ 71.3003,
+ 69.6003
+ ]
+ }
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/NS6400 Dloco/1180kW",
+ "Values": {
+ "A": [
+ 82.9836,
+ 93.8836,
+ 88.6836,
+ 83.6836,
+ 76.8836,
+ 76.8836,
+ 77.0836,
+ 78.8836,
+ 78.6836,
+ 79.7836,
+ 79.6836,
+ 78.6836,
+ 79.2836,
+ 79.1836,
+ 79.1836,
+ 78.1836,
+ 75.1836,
+ 72.4836,
+ 69.5836,
+ 66.5836,
+ 64.0836,
+ 61.1836,
+ 58.5836,
+ 56.2836
+ ],
+ "B": [
+ 87.2836,
+ 99.0836,
+ 91.5836,
+ 83.0836,
+ 77.3836,
+ 77.6836,
+ 76.9836,
+ 76.7836,
+ 76.4836,
+ 76.8836,
+ 76.8836,
+ 76.8836,
+ 76.7836,
+ 77.0836,
+ 76.9836,
+ 77.1836,
+ 74.2836,
+ 71.3836,
+ 68.5836,
+ 65.7836,
+ 63.0836,
+ 60.4836,
+ 57.9836,
+ 55.7836
+ ]
+ }
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/TKOJ JT42CWR/Class66/2200kW",
+ "Values": {
+ "A": [
+ 92.432,
+ 88.332,
+ 87.132,
+ 92.132,
+ 90.332,
+ 90.332,
+ 90.532,
+ 92.332,
+ 92.132,
+ 93.232,
+ 93.132,
+ 90.132,
+ 88.732,
+ 86.632,
+ 84.632,
+ 81.632,
+ 79.632,
+ 77.932,
+ 76.032,
+ 74.032,
+ 72.532,
+ 70.632,
+ 69.032,
+ 67.732
+ ],
+ "B": [
+ 96.732,
+ 93.532,
+ 90.032,
+ 91.532,
+ 90.832,
+ 91.132,
+ 90.432,
+ 90.232,
+ 89.932,
+ 90.332,
+ 90.332,
+ 88.332,
+ 86.232,
+ 84.532,
+ 82.432,
+ 80.632,
+ 78.732,
+ 76.832,
+ 75.032,
+ 73.232,
+ 71.532,
+ 69.932,
+ 68.432,
+ 67.232
+ ]
+ }
+ },
+ "EU8": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel multiple unit",
+ "Values": {
+ "A": [
+ 78.583,
+ 79.483,
+ 88.283,
+ 84.283,
+ 82.483,
+ 82.483,
+ 88.683,
+ 84.483,
+ 84.283,
+ 85.383,
+ 85.283,
+ 84.283,
+ 90.883,
+ 84.783,
+ 84.783,
+ 83.783,
+ 83.783,
+ 84.083,
+ 82.183,
+ 79.183,
+ 75.683,
+ 71.783,
+ 68.183,
+ 64.883
+ ],
+ "B": [
+ 82.883,
+ 84.683,
+ 91.183,
+ 83.683,
+ 82.983,
+ 83.283,
+ 88.583,
+ 82.383,
+ 82.083,
+ 82.483,
+ 82.483,
+ 82.483,
+ 88.383,
+ 82.683,
+ 82.583,
+ 82.783,
+ 82.883,
+ 82.983,
+ 81.183,
+ 78.383,
+ 74.683,
+ 71.083,
+ 67.583,
+ 64.383
+ ]
+ }
+ },
+ "EU9": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric locomotive",
+ "Values": {
+ "A": [
+ 78.8551,
+ 84.7551,
+ 82.5551,
+ 85.5551,
+ 85.7551,
+ 87.7551,
+ 97.9551,
+ 91.7551,
+ 90.5551,
+ 95.6551,
+ 89.5551,
+ 86.5551,
+ 86.1551,
+ 90.0551,
+ 83.0551,
+ 80.0551,
+ 78.0551,
+ 76.3551,
+ 74.4551,
+ 72.4551,
+ 70.9551,
+ 69.0551,
+ 67.4551,
+ 66.1551
+ ],
+ "B": [
+ 83.1551,
+ 89.9551,
+ 85.4551,
+ 84.9551,
+ 86.2551,
+ 88.5551,
+ 97.8551,
+ 89.6551,
+ 88.3551,
+ 92.7551,
+ 86.7551,
+ 84.7551,
+ 83.6551,
+ 87.9551,
+ 80.8551,
+ 79.0551,
+ 77.1551,
+ 75.2551,
+ 73.4551,
+ 71.6551,
+ 69.9551,
+ 68.3551,
+ 66.8551,
+ 65.6551
+ ]
+ }
+ },
+ "EU10": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric multiple unit ",
+ "Values": {
+ "A": [
+ 80.5497,
+ 81.4497,
+ 80.5497,
+ 82.2497,
+ 80.0497,
+ 79.7497,
+ 79.6497,
+ 81.0497,
+ 80.5497,
+ 81.3497,
+ 80.8497,
+ 79.5497,
+ 79.8497,
+ 86.7497,
+ 81.7497,
+ 82.7497,
+ 80.7497,
+ 78.0497,
+ 75.1497,
+ 72.1497,
+ 69.6497,
+ 66.7497,
+ 64.1497,
+ 61.8497
+ ],
+ "B": [
+ 84.8497,
+ 86.6497,
+ 83.4497,
+ 81.6497,
+ 80.5497,
+ 80.5497,
+ 79.5497,
+ 78.9497,
+ 78.3497,
+ 78.4497,
+ 78.0497,
+ 77.7497,
+ 77.3497,
+ 84.6497,
+ 79.5497,
+ 81.7497,
+ 79.8497,
+ 76.9497,
+ 74.1497,
+ 71.3497,
+ 68.6497,
+ 66.0497,
+ 63.5497,
+ 61.3497
+ ]
+ }
+ }
+ },
+ "IdlingSpeed": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty traction",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Values": {
+ "A": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ],
+ "B": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ }
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 800kW)",
+ "Values": {
+ "A": [
+ 87.8613,
+ 83.7613,
+ 86.5613,
+ 88.5613,
+ 86.7613,
+ 86.7613,
+ 86.9613,
+ 88.7613,
+ 88.5613,
+ 89.6613,
+ 89.5613,
+ 93.5613,
+ 89.1613,
+ 89.0613,
+ 89.0613,
+ 88.0613,
+ 88.0613,
+ 86.3613,
+ 83.4613,
+ 80.4613,
+ 77.9613,
+ 75.0613,
+ 72.4613,
+ 70.1613
+ ],
+ "B": [
+ 92.1613,
+ 88.9613,
+ 89.4613,
+ 87.9613,
+ 87.2613,
+ 87.5613,
+ 86.8613,
+ 86.6613,
+ 86.3613,
+ 86.7613,
+ 86.7613,
+ 91.7613,
+ 86.6613,
+ 86.9613,
+ 86.8613,
+ 87.0613,
+ 87.1613,
+ 85.2613,
+ 82.4613,
+ 79.6613,
+ 76.9613,
+ 74.3613,
+ 71.8613,
+ 69.6613
+ ]
+ }
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel locomotive (c. 2200kW)",
+ "Values": {
+ "A": [
+ 89.4103,
+ 90.3103,
+ 103.1103,
+ 95.1103,
+ 93.3103,
+ 93.3103,
+ 93.5103,
+ 95.3103,
+ 95.1103,
+ 96.2103,
+ 96.1103,
+ 95.1103,
+ 95.7103,
+ 95.6103,
+ 93.3103,
+ 96.6103,
+ 87.7103,
+ 85.7103,
+ 87.0103,
+ 81.1103,
+ 79.3103,
+ 77.1103,
+ 75.2103,
+ 73.6103
+ ],
+ "B": [
+ 93.7103,
+ 95.5103,
+ 106.0103,
+ 94.5103,
+ 93.8103,
+ 94.1103,
+ 93.4103,
+ 93.2103,
+ 92.9103,
+ 93.3103,
+ 93.3103,
+ 93.3103,
+ 93.2103,
+ 93.5103,
+ 91.1103,
+ 95.6103,
+ 86.8103,
+ 84.6103,
+ 86.0103,
+ 80.3103,
+ 78.3103,
+ 76.4103,
+ 74.6103,
+ 73.1103
+ ]
+ }
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/RENFE Dloco/1155kW",
+ "Values": {
+ "A": [
+ 99.3003,
+ 103.2003,
+ 103.0003,
+ 102.0003,
+ 97.2003,
+ 97.2003,
+ 97.4003,
+ 99.2003,
+ 99.0003,
+ 100.1003,
+ 100.0003,
+ 98.0003,
+ 97.0003,
+ 94.4003,
+ 91.8003,
+ 88.3003,
+ 85.7003,
+ 83.5003,
+ 81.0003,
+ 78.5003,
+ 76.4003,
+ 74.0003,
+ 71.9003,
+ 70.1003
+ ],
+ "B": [
+ 103.6003,
+ 108.4003,
+ 105.9003,
+ 101.4003,
+ 97.7003,
+ 98.0003,
+ 97.3003,
+ 97.1003,
+ 96.8003,
+ 97.2003,
+ 97.2003,
+ 96.2003,
+ 94.5003,
+ 92.3003,
+ 89.6003,
+ 87.3003,
+ 84.8003,
+ 82.4003,
+ 80.0003,
+ 77.7003,
+ 75.4003,
+ 73.3003,
+ 71.3003,
+ 69.6003
+ ]
+ }
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/NS6400 Dloco/1180kW",
+ "Values": {
+ "A": [
+ 82.9836,
+ 93.8836,
+ 88.6836,
+ 83.6836,
+ 76.8836,
+ 76.8836,
+ 77.0836,
+ 78.8836,
+ 78.6836,
+ 79.7836,
+ 79.6836,
+ 78.6836,
+ 79.2836,
+ 79.1836,
+ 79.1836,
+ 78.1836,
+ 75.1836,
+ 72.4836,
+ 69.5836,
+ 66.5836,
+ 64.0836,
+ 61.1836,
+ 58.5836,
+ 56.2836
+ ],
+ "B": [
+ 87.2836,
+ 99.0836,
+ 91.5836,
+ 83.0836,
+ 77.3836,
+ 77.6836,
+ 76.9836,
+ 76.7836,
+ 76.4836,
+ 76.8836,
+ 76.8836,
+ 76.8836,
+ 76.7836,
+ 77.0836,
+ 76.9836,
+ 77.1836,
+ 74.2836,
+ 71.3836,
+ 68.5836,
+ 65.7836,
+ 63.0836,
+ 60.4836,
+ 57.9836,
+ 55.7836
+ ]
+ }
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel loc/TKOJ JT42CWR/Class66/2200kW",
+ "Values": {
+ "A": [
+ 92.432,
+ 88.332,
+ 87.132,
+ 92.132,
+ 90.332,
+ 90.332,
+ 90.532,
+ 92.332,
+ 92.132,
+ 93.232,
+ 93.132,
+ 90.132,
+ 88.732,
+ 86.632,
+ 84.632,
+ 81.632,
+ 79.632,
+ 77.932,
+ 76.032,
+ 74.032,
+ 72.532,
+ 70.632,
+ 69.032,
+ 67.732
+ ],
+ "B": [
+ 96.732,
+ 93.532,
+ 90.032,
+ 91.532,
+ 90.832,
+ 91.132,
+ 90.432,
+ 90.232,
+ 89.932,
+ 90.332,
+ 90.332,
+ 88.332,
+ 86.232,
+ 84.532,
+ 82.432,
+ 80.632,
+ 78.732,
+ 76.832,
+ 75.032,
+ 73.232,
+ 71.532,
+ 69.932,
+ 68.432,
+ 67.232
+ ]
+ }
+ },
+ "EU8": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Diesel multiple unit",
+ "Values": {
+ "A": [
+ 78.583,
+ 79.483,
+ 88.283,
+ 84.283,
+ 82.483,
+ 82.483,
+ 88.683,
+ 84.483,
+ 84.283,
+ 85.383,
+ 85.283,
+ 84.283,
+ 90.883,
+ 84.783,
+ 84.783,
+ 83.783,
+ 83.783,
+ 84.083,
+ 82.183,
+ 79.183,
+ 75.683,
+ 71.783,
+ 68.183,
+ 64.883
+ ],
+ "B": [
+ 82.883,
+ 84.683,
+ 91.183,
+ 83.683,
+ 82.983,
+ 83.283,
+ 88.583,
+ 82.383,
+ 82.083,
+ 82.483,
+ 82.483,
+ 82.483,
+ 88.383,
+ 82.683,
+ 82.583,
+ 82.783,
+ 82.883,
+ 82.983,
+ 81.183,
+ 78.383,
+ 74.683,
+ 71.083,
+ 67.583,
+ 64.383
+ ]
+ }
+ },
+ "EU9": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric locomotive",
+ "Values": {
+ "A": [
+ 78.8551,
+ 84.7551,
+ 82.5551,
+ 85.5551,
+ 85.7551,
+ 87.7551,
+ 97.9551,
+ 91.7551,
+ 90.5551,
+ 95.6551,
+ 89.5551,
+ 86.5551,
+ 86.1551,
+ 90.0551,
+ 83.0551,
+ 80.0551,
+ 78.0551,
+ 76.3551,
+ 74.4551,
+ 72.4551,
+ 70.9551,
+ 69.0551,
+ 67.4551,
+ 66.1551
+ ],
+ "B": [
+ 83.1551,
+ 89.9551,
+ 85.4551,
+ 84.9551,
+ 86.2551,
+ 88.5551,
+ 97.8551,
+ 89.6551,
+ 88.3551,
+ 92.7551,
+ 86.7551,
+ 84.7551,
+ 83.6551,
+ 87.9551,
+ 80.8551,
+ 79.0551,
+ 77.1551,
+ 75.2551,
+ 73.4551,
+ 71.6551,
+ 69.9551,
+ 68.3551,
+ 66.8551,
+ 65.6551
+ ]
+ }
+ },
+ "EU10": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Electric multiple unit ",
+ "Values": {
+ "A": [
+ 80.5497,
+ 81.4497,
+ 80.5497,
+ 82.2497,
+ 80.0497,
+ 79.7497,
+ 79.6497,
+ 81.0497,
+ 80.5497,
+ 81.3497,
+ 80.8497,
+ 79.5497,
+ 79.8497,
+ 86.7497,
+ 81.7497,
+ 82.7497,
+ 80.7497,
+ 78.0497,
+ 75.1497,
+ 72.1497,
+ 69.6497,
+ 66.7497,
+ 64.1497,
+ 61.8497
+ ],
+ "B": [
+ 84.8497,
+ 86.6497,
+ 83.4497,
+ 81.6497,
+ 80.5497,
+ 80.5497,
+ 79.5497,
+ 78.9497,
+ 78.3497,
+ 78.4497,
+ 78.0497,
+ 77.7497,
+ 77.3497,
+ 84.6497,
+ 79.5497,
+ 81.7497,
+ 79.8497,
+ 76.9497,
+ 74.1497,
+ 71.3497,
+ 68.6497,
+ 66.0497,
+ 63.5497,
+ 61.3497
+ ]
+ }
+ }
+ },
+ "AerodynamicNoise": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty aerodynamic noise",
+ "V0": 0,
+ "Alpha": 0,
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "V0": 0,
+ "Alpha": 0,
+ "Values": {
+ "A": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ],
+ "B": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "V0": "400",
+ "Alpha": "100",
+ "Values": {
+ "A": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ],
+ "B": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ }
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Aerodynamic noise given at 300 km/h",
+ "V0": "300",
+ "Alpha": "50",
+ "Values": {
+ "A": [
+ 112.5778,
+ 113.1665,
+ 115.6758,
+ 117.385,
+ 115.2634,
+ 114.9727,
+ 114.882,
+ 116.3808,
+ 115.8696,
+ 116.2758,
+ 116.1758,
+ 115.1758,
+ 115.7758,
+ 115.6758,
+ 115.6758,
+ 114.6758,
+ 114.6758,
+ 114.9758,
+ 114.5449,
+ 113.0912,
+ 112.0857,
+ 110.6294,
+ 109.5758,
+ 108.8221
+ ],
+ "B": [
+ 36.7103,
+ 38.5103,
+ 39.0103,
+ 37.5103,
+ 36.8103,
+ 37.1103,
+ 36.4103,
+ 36.2103,
+ 35.9103,
+ 36.3103,
+ 36.3103,
+ 36.3103,
+ 36.2103,
+ 36.5103,
+ 36.4103,
+ 105.2103,
+ 110.3103,
+ 110.4103,
+ 105.6103,
+ 37.2103,
+ 37.5103,
+ 37.9103,
+ 38.4103,
+ 39.2103
+ ]
+ }
+ },
+ "SNCF1": {
+ "DescriptionA": "LW.0-1",
+ "DescriptionB": "LW.0-2",
+ "V0": 300,
+ "Alpha": 50,
+ "Reference": "SNCF Juin 2021",
+ "Values": {
+ "A": [
+ 101.2,
+ 102.4,
+ 100.9,
+ 101.2,
+ 100.4,
+ 100.8,
+ 103.4,
+ 106.1,
+ 106.8,
+ 107.1,
+ 107.9,
+ 106.3,
+ 89.8,
+ 87.2,
+ 84.3,
+ 81.2,
+ 77.6,
+ 75,
+ 68.8,
+ 29.2,
+ -14,
+ -13.9,
+ -15.7,
+ -15.8
+ ],
+ "B": [
+ 110.2,
+ 111.3,
+ 112.6,
+ 113.7,
+ 112.7,
+ 111.1,
+ 111.1,
+ 110.2,
+ 110.8,
+ 111.1,
+ 111,
+ 109.4,
+ 107.3,
+ 106.1,
+ 100.6,
+ 97.7,
+ 91.6,
+ 65.1,
+ 13.8,
+ -7.8,
+ -7.6,
+ -7,
+ -13,
+ -13.7
+ ]
+ }
+ }
+ }
+ },
+ "Track": {
+ "TrackTransfer": {
+ "EU0": {
+ "Default": "true",
+ "Reference": null,
+ "Description": "Empty track transfer function",
+ "Sleeper": null,
+ "Railpad": null,
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Sleeper": null,
+ "Railpad": null,
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Sleeper": null,
+ "Railpad": null,
+ "Spectre": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Mono-block sleeper on soft rail pad",
+ "Sleeper": "Concrete mono-block",
+ "Railpad": "Soft",
+ "Spectre": [
+ 53.3,
+ 59.3,
+ 67.2,
+ 75.9,
+ 79.2,
+ 81.8,
+ 84.2,
+ 88.6,
+ 91,
+ 94.5,
+ 97,
+ 99.2,
+ 104,
+ 107.1,
+ 108.3,
+ 108.5,
+ 109.7,
+ 110,
+ 110,
+ 110,
+ 110.3,
+ 110,
+ 110.1,
+ 110.6
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Mono-block sleeper on medium stiffness rail pad",
+ "Sleeper": "Concrete mono-block",
+ "Railpad": "Medium",
+ "Spectre": [
+ 50.9,
+ 57.8,
+ 66.5,
+ 76.8,
+ 80.9,
+ 83.3,
+ 85.8,
+ 90,
+ 91.6,
+ 93.9,
+ 95.6,
+ 97.4,
+ 101.7,
+ 104.4,
+ 106,
+ 106.8,
+ 108.3,
+ 108.9,
+ 109.1,
+ 109.4,
+ 109.9,
+ 109.9,
+ 110.3,
+ 111
+ ]
+ },
+ "EU5": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Mono-block on hard rail pad",
+ "Sleeper": "Concrete mono-block",
+ "Railpad": "Stiff",
+ "Spectre": [
+ 50.1,
+ 57.2,
+ 66.3,
+ 77.2,
+ 81.6,
+ 84,
+ 86.5,
+ 90.7,
+ 92.1,
+ 94.3,
+ 95.8,
+ 97,
+ 100.3,
+ 102.5,
+ 104.2,
+ 105.4,
+ 107.1,
+ 107.9,
+ 108.2,
+ 108.7,
+ 109.4,
+ 109.7,
+ 110.4,
+ 111.4
+ ]
+ },
+ "EU6": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Bi-block sleeper on soft rail pad",
+ "Sleeper": "Concrete bi-block",
+ "Railpad": "Soft",
+ "Spectre": [
+ 50.9,
+ 56.6,
+ 64.3,
+ 72.3,
+ 75.4,
+ 78.5,
+ 81.8,
+ 86.6,
+ 89.1,
+ 91.9,
+ 94.5,
+ 97.5,
+ 104,
+ 107.9,
+ 108.9,
+ 108.8,
+ 109.8,
+ 110.2,
+ 110.1,
+ 110.1,
+ 110.3,
+ 109.9,
+ 110,
+ 110.4
+ ]
+ },
+ "EU7": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Bi-block sleeper on medium stiffness rail pad",
+ "Sleeper": "Concrete bi-block",
+ "Railpad": "Medium",
+ "Spectre": [
+ 50,
+ 56.1,
+ 64.1,
+ 72.5,
+ 75.8,
+ 79.1,
+ 83.6,
+ 88.7,
+ 89.6,
+ 89.7,
+ 90.6,
+ 93.8,
+ 100.6,
+ 104.7,
+ 106.3,
+ 107.1,
+ 108.8,
+ 109.3,
+ 109.4,
+ 109.7,
+ 110,
+ 109.8,
+ 110,
+ 110.5
+ ]
+ },
+ "EU8": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Bi-block sleeper on hard rail pad",
+ "Sleeper": "Concrete bi-block",
+ "Railpad": "Stiff",
+ "Spectre": [
+ 49.8,
+ 55.9,
+ 64,
+ 72.5,
+ 75.9,
+ 79.4,
+ 84.4,
+ 89.7,
+ 90.2,
+ 90.2,
+ 90.8,
+ 93.1,
+ 97.9,
+ 101.1,
+ 103.4,
+ 105.4,
+ 107.7,
+ 108.5,
+ 108.7,
+ 109.1,
+ 109.6,
+ 109.6,
+ 109.9,
+ 110.6
+ ]
+ },
+ "EU9": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Wooden sleepers",
+ "Sleeper": "Wood",
+ "Railpad": null,
+ "Spectre": [
+ 44,
+ 51,
+ 59.9,
+ 70.8,
+ 75.1,
+ 76.9,
+ 77.2,
+ 80.9,
+ 85.3,
+ 92.5,
+ 97,
+ 98.7,
+ 102.8,
+ 105.4,
+ 106.5,
+ 106.4,
+ 107.5,
+ 108.1,
+ 108.4,
+ 108.7,
+ 109.1,
+ 109.1,
+ 109.5,
+ 110.2
+ ]
+ },
+ "EU10": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Direct fastening on bridges",
+ "Sleeper": null,
+ "Railpad": null,
+ "Spectre": [
+ 75.4,
+ 77.4,
+ 81.4,
+ 87.1,
+ 88.0,
+ 89.7,
+ 83.4,
+ 87.7,
+ 89.8,
+ 97.5,
+ 99.0,
+ 100.8,
+ 104.9,
+ 111.8,
+ 113.9,
+ 115.5,
+ 114.9,
+ 118.2,
+ 118.3,
+ 118.4,
+ 118.9,
+ 117.5,
+ 117.9,
+ 118.6
+ ]
+ },
+ "SNCF1": {
+ "Description": "Traverse-monobloc-sur-semelle-souple",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 42.1,
+ 45.3,
+ 49.4,
+ 53.9,
+ 59.3,
+ 66.1,
+ 74.5,
+ 80.6,
+ 80.7,
+ 79.6,
+ 83,
+ 90.3,
+ 98.8,
+ 101.6,
+ 101.9,
+ 103.6,
+ 104.9,
+ 105.8,
+ 106.7,
+ 107.6,
+ 108.5,
+ 108.2,
+ 108.3,
+ 108.8
+ ]
+ },
+ "SNCF2": {
+ "Description": "Traverse-monobloc-sur-semelle-de-rigite-moyenne",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 34.6,
+ 40.9,
+ 47.4,
+ 52.9,
+ 58.8,
+ 65.3,
+ 72.7,
+ 77.4,
+ 81.4,
+ 83.3,
+ 83.9,
+ 88.5,
+ 97.5,
+ 101.4,
+ 103,
+ 103.8,
+ 105.3,
+ 105.9,
+ 106.1,
+ 106.4,
+ 106.9,
+ 106.9,
+ 107.3,
+ 108
+ ]
+ },
+ "SNCF3": {
+ "Description": "Traverse-monobloc-sur-semelle-ri-ge",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 32.5,
+ 39.4,
+ 46.6,
+ 53.2,
+ 59.1,
+ 65.5,
+ 72.3,
+ 79.1,
+ 86.2,
+ 89.3,
+ 87.9,
+ 88.5,
+ 94.4,
+ 98.4,
+ 99.1,
+ 102.6,
+ 103.8,
+ 104.6,
+ 104.9,
+ 105.4,
+ 106.1,
+ 106.4,
+ 107.1,
+ 108.1
+ ]
+ },
+ "SNCF4": {
+ "Description": "Traverse-bibloc-sur-semelle-souple",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 29.2,
+ 37,
+ 45,
+ 52.3,
+ 59.8,
+ 65.6,
+ 70.8,
+ 73.4,
+ 76.7,
+ 82,
+ 85.5,
+ 90.4,
+ 99.1,
+ 102.4,
+ 103,
+ 104.7,
+ 105.3,
+ 105.3,
+ 105.3,
+ 105.3,
+ 105.3,
+ 105.3,
+ 105.3,
+ 105.3
+ ]
+ },
+ "SNCF5": {
+ "Description": "Traverse-bibloc-sur-semelle-de-rigite-moyenne",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 34.4,
+ 41,
+ 47.7,
+ 53.2,
+ 59.4,
+ 64.5,
+ 70,
+ 75.7,
+ 80.3,
+ 82.6,
+ 84.7,
+ 89.1,
+ 98.1,
+ 102.5,
+ 104.3,
+ 105.1,
+ 106.8,
+ 107.3,
+ 107.4,
+ 107.7,
+ 108,
+ 107.8,
+ 108,
+ 108.5
+ ]
+ },
+ "SNCF6": {
+ "Description": "Traverse-bibloc-sur-semelle-ri-ge",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 14.7,
+ 26.3,
+ 38.4,
+ 49.1,
+ 60.1,
+ 64.7,
+ 68.6,
+ 73.9,
+ 80.5,
+ 87,
+ 88.5,
+ 85.2,
+ 90.1,
+ 97,
+ 98.5,
+ 101.1,
+ 103,
+ 103.5,
+ 103.7,
+ 104.1,
+ 104.6,
+ 104.6,
+ 104.9,
+ 105.6
+ ]
+ },
+ "SNCF7": {
+ "Description": "Traverse-en-bois",
+ "Reference": "SNCF Juin 2021",
+ "Spectre": [
+ 44,
+ 51,
+ 59.9,
+ 65.4,
+ 70.6,
+ 74.7,
+ 79.3,
+ 83.3,
+ 86.7,
+ 89.6,
+ 92.4,
+ 98.7,
+ 106.1,
+ 109.5,
+ 111.9,
+ 113.2,
+ 113,
+ 112.9,
+ 112.8,
+ 112.7,
+ 112.6,
+ 112.6,
+ 112.6,
+ 112.6
+ ]
+ },
+ "SNCF8": {
+ "Description": "Fixation-directe-sur-les-ponts-métalliques-(pose-ans-ballast)",
+ "Reference": "SNCF Fevrier 2022",
+ "Spectre": [
+ 91.4,
+ 92.7,
+ 95.8,
+ 101.2,
+ 100.5,
+ 101.2,
+ 101.4,
+ 102.5,
+ 102.6,
+ 103.4,
+ 103.4,
+ 107.3,
+ 111.0,
+ 111.4,
+ 109.3,
+ 107.4,
+ 107.4,
+ 107.1,
+ 106.4,
+ 104.1,
+ 100.7,
+ 94.4,
+ 93.2,
+ 91.7
+ ]
+ }
+ },
+ "SuperstructureTransfer": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty superstructure transfer function",
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Spectre": [
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140,
+ 140
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "CNOSSOS",
+ "Description": "CNOSSOS-EU Default",
+ "Spectre": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ }
+ },
+ "RailRoughness": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty rail roughness",
+ "RoughnessCode": "",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "RoughnessCode": "Well maintained and very smooth",
+ "Values": [
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15,
+ -15
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "RoughnessCode": "Not maintained and bad condition",
+ "Values": [
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22,
+ 22
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "EN ISO 3095 2013",
+ "RoughnessCode": "Well maintained and very smooth",
+ "Values": [
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 15.0,
+ 13.0,
+ 11.0,
+ 9.0,
+ 7.0,
+ 4.9,
+ 2.9,
+ 0.9,
+ -1.1,
+ -3.2,
+ -5.0,
+ -5.6,
+ -6.2,
+ -6.8,
+ -7.4,
+ -8.0,
+ -8.6,
+ -9.2,
+ -9.8,
+ -10.4,
+ -11.0,
+ -11.6,
+ -12.2,
+ -12.8,
+ -13.4,
+ -14.0,
+ -14.0
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Average network",
+ "RoughnessCode": "Normally maintained smooth",
+ "Values": [
+ 35.0,
+ 31.0,
+ 28.0,
+ 25.0,
+ 23.0,
+ 20.0,
+ 17.0,
+ 13.5,
+ 10.5,
+ 9.0,
+ 6.5,
+ 5.5,
+ 5.0,
+ 3.5,
+ 2.0,
+ 0.1,
+ -0.2,
+ -0.3,
+ -0.8,
+ -3.0,
+ -5.0,
+ -7.0,
+ -8.0,
+ -9.0,
+ -10.0,
+ -12.0,
+ -13.0,
+ -14.0,
+ -15.0,
+ -16.0,
+ -17.0,
+ -18.0,
+ -19.0,
+ -19.0,
+ -19.0
+ ]
+ },
+ "SNCF1": {
+ "Description": "Lignes-Classiques",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 17.1,
+ 15,
+ 13,
+ 11,
+ 9,
+ 5.9,
+ 4.2,
+ 2,
+ -0.4,
+ -2.6,
+ -4.8,
+ -7.1,
+ -8.4,
+ -9.5,
+ -10.6,
+ -12,
+ -12.9,
+ -14,
+ -15.2,
+ -16.3,
+ -17.4,
+ -20.5,
+ -22.8,
+ -24.5,
+ -25.9,
+ -27.2,
+ -27.9,
+ -28.5
+ ]
+ },
+ "SNCF2": {
+ "Description": "LGV",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 11.3,
+ 10.2,
+ 9.1,
+ 7.9,
+ 6.6,
+ 5.3,
+ 4,
+ 2.8,
+ 1.5,
+ 0.2,
+ -1,
+ -2.3,
+ -3.9,
+ -4.9,
+ -6.1,
+ -7.1,
+ -7.6,
+ -8.1,
+ -8.6,
+ -9.1,
+ -9.6,
+ -10,
+ -10.7,
+ -11.1,
+ -11.6,
+ -12.6,
+ -13.7,
+ -15.4,
+ -17.8,
+ -20,
+ -22.1,
+ -24.1,
+ -26.4,
+ -28.3,
+ -30.3
+ ]
+ }
+ },
+ "ImpactNoise": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty impact noise",
+ "RoughnessCode": "",
+ "JoinDensity": null,
+ "JoinDensityDescription": "",
+ "Values": [
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0
+ ]
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "RoughnessCode": "Normally maintained smooth",
+ "JoinDensity": 0,
+ "JoinDensityDescription": "none",
+ "Values": [
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40,
+ -40
+ ]
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "RoughnessCode": "Normally maintained smooth",
+ "JoinDensity": 3,
+ "JoinDensityDescription": ">2 switches/joints/crossings/100m",
+ "Values": [
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30,
+ 30
+ ]
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "Single switch/joint/crossing/100m",
+ "RoughnessCode": "Normally maintained smooth",
+ "JoinDensity": 1,
+ "JoinDensityDescription": "single switch/joint/crossing/100m",
+ "Values": [
+ 22.0,
+ 22.0,
+ 22.0,
+ 22.0,
+ 22.0,
+ 20.0,
+ 16.0,
+ 15.0,
+ 14.0,
+ 15.0,
+ 14.0,
+ 12.0,
+ 11.0,
+ 10.0,
+ 9.0,
+ 8.0,
+ 6.0,
+ 3.0,
+ 2.0,
+ -3.0,
+ -8.0,
+ -13.0,
+ -17.0,
+ -19.0,
+ -22.0,
+ -25.0,
+ -26.0,
+ -32.0,
+ -35.0,
+ -40.0,
+ -43.0,
+ -45.0,
+ -47.0,
+ -49.0,
+ -50.0
+ ]
+ },
+ "SNCF1": {
+ "Description": "Aiguillage/joint/croisement/100m",
+ "Reference": "SNCF Juin 2021",
+ "Values": [
+ 18.0,
+ 18.0,
+ 18.0,
+ 18.0,
+ 18.0,
+ 16.0,
+ 12.0,
+ 11.0,
+ 10.0,
+ 11.0,
+ 10.0,
+ 8.0,
+ 7.0,
+ 6.0,
+ 5.0,
+ 4.0,
+ 2.0,
+ -1.0,
+ -2.0,
+ -7.0,
+ -12.0,
+ -17.0,
+ -21.0,
+ -23.0,
+ -26.0,
+ -29.0,
+ -30.0,
+ -36.0,
+ -39.0,
+ -44.0,
+ -47.0,
+ -49.0,
+ -51.0,
+ -53.0,
+ -54.0
+ ]
+ }
+ },
+ "BridgeConstant": {
+ "EU0": {
+ "Default": "true",
+ "Reference": "",
+ "Description": "Empty bridge constant",
+ "Value": 0
+ },
+ "EU1": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "min",
+ "Value": 0
+ },
+ "EU2": {
+ "Default": "true",
+ "Reference": "IMAGINE",
+ "Description": "max",
+ "Value": 9
+ },
+ "EU3": {
+ "Default": "true",
+ "Reference": "CNOSSOS",
+ "Description": "Predominantly concrete or masonry bridges with any trackform",
+ "Values": [
+ 85.2,
+ 87.1,
+ 91.0,
+ 94.0,
+ 94.4,
+ 96.0,
+ 92.5,
+ 96.7,
+ 97.4,
+ 99.4,
+ 100.7,
+ 102.5,
+ 107.1,
+ 109.8,
+ 112.0,
+ 107.2,
+ 106.8,
+ 107.3,
+ 99.3,
+ 91.4,
+ 86.9,
+ 79.7,
+ 75.1,
+ 70.8
+ ]
+ },
+ "EU4": {
+ "Default": "true",
+ "Reference": "CNOSSOS",
+ "Description": "Predominantly steel bridges with ballasted track ",
+ "Values": [
+ 90.1,
+ 92.1,
+ 96.0,
+ 99.5,
+ 99.9,
+ 101.5,
+ 99.6,
+ 103.8,
+ 104.5,
+ 106.5,
+ 107.8,
+ 109.6,
+ 116.1,
+ 118.8,
+ 120.9,
+ 109.5,
+ 109.1,
+ 109.6,
+ 102.0,
+ 94.1,
+ 89.6,
+ 83.6,
+ 79.0,
+ 74.7
+ ]
+ },
+ "SNCF1": {
+ "Description": "Tout-type-de-pont-hors-ponts-metalliques-avec-voies-non-ballastees",
+ "Reference": "SNCF Juin 2021",
+ "Values": 0
+ },
+ "SNCF2": {
+ "Description": "Ponts-metalliques-avec-voies-non-ballastees",
+ "Reference": "SNCF Juin 2021",
+ "Values": 5
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos.json
index 728885fd3..647d6102e 100644
--- a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos.json
+++ b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos.json
@@ -1,1621 +1,1621 @@
{
- "EU1": {
- "Description": "TestTrain EU",
- "Reference": "Test 11/2022",
- "Vmax": 300,
- "Length": 200.19,
- "TrailerBogieBraking": "Disques-garniture-frittee",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 10,
- "FirstSourcePosition": 10.0095,
- "SourceSpacing": 20.019,
- "NbAxlePerVeh": 2.6,
- "RefAerodynamic": 1,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 1
- },
- "SNCF1": {
- "Description": "TGV-00-100-TGV-SE",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 300,
- "Length": 200.19,
- "TrailerBogieBraking": "Disques-garniture-frittee",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 10,
- "FirstSourcePosition": 10.0095,
- "SourceSpacing": 20.019,
- "NbAxlePerVeh": 2.6,
- "RefAerodynamic": 1,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 1
- },
- "SNCF2": {
- "Description": "TGV200-600-700-TGV-Duplex-DASYE-TGV800-4700-TGV-2N2-TGV4300-TGV-Thalys-PBKATGV500-4500-TGV-R-TGV-Thalys-PBA-TGV4400-TGV-POS",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 320,
- "Length": 200.19,
- "TrailerBogieBraking": "Disques",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 910,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 10,
- "FirstSourcePosition": 10.0095,
- "SourceSpacing": 20.019,
- "NbAxlePerVeh": 2.6,
- "RefAerodynamic": 1,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 1
- },
- "SNCF3": {
- "Description": "TGV300-400-TGV-A",
- "Vmax": 300,
- "Length": 237.59,
- "TrailerBogieBraking": "Disques",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 12,
- "FirstSourcePosition": 9.8996,
- "SourceSpacing": 19.7992,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 1,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 1
- },
- "SNCF4": {
- "Description": "TGV3200-TGV-TMST-Eurostar",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 300,
- "Length": 393.72,
- "TrailerBogieBraking": "Disques",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 20,
- "FirstSourcePosition": 9.843,
- "SourceSpacing": 19.686,
- "NbAxlePerVeh": 2.4,
- "RefAerodynamic": 1,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 1
- },
- "SNCF5": {
- "Description": "A1A-A1A-68000",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 130,
- "Length": 18.01,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 950,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 9.005,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 6,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 14
- },
- "SNCF6": {
- "Description": "BB15000",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 17.48,
- "TrailerBogieBraking": "Semelle-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.74,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF7": {
- "Description": "BB16500-BB16600-BB16700",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 14.4,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1110,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.2,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF8": {
- "Description": "BB17000-BB17100",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 14.94,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1110,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.47,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF9": {
- "Description": "BB22200-BB22300-BB22400",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 17.48,
- "TrailerBogieBraking": "Semelle-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.74,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF10": {
- "Description": "BB25200",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 16.7,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.35,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF11": {
- "Description": "BB25500-BB25600",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 14.7,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1110,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.35,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF12": {
- "Description": "BB26000-BB26100-BB26200",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 17.71,
- "TrailerBogieBraking": "Semelle-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.855,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF13": {
- "Description": "BB27000-BB27100",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 19.72,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 9.86,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF14": {
- "Description": "BB36000-BB36300",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 220,
- "Length": 19.11,
- "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 9.555,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 3,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF15": {
- "Description": "BB37000",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 19.72,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 9.86,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF16": {
- "Description": "BB61000-Vossloh-G-1206-BB",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 100,
- "Length": 14.7,
- "TrailerBogieBraking": "Disque+semelles",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1000,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.35,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 3,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF17": {
- "Description": "BB63500-BB64000-BB64700",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 90,
- "Length": 14.68,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1050,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.34,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 14
- },
- "SNCF18": {
- "Description": "BB66000-BB66100-BB66200-BB66300",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 14.89,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1100,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.445,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF19": {
- "Description": "BB66400-BB66500",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 14.97,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1100,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.486,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF20": {
- "Description": "BB67000-BB67100-BB67300",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 17.09,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.545,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF21": {
- "Description": "BB67200",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 90,
- "Length": 17.09,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.545,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF22": {
- "Description": "BB67400-BB67500-BB67600",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 17.09,
- "TrailerBogieBraking": "Semelle-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.545,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF23": {
- "Description": "BB7200-BB7300-BB7400",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 17.48,
- "TrailerBogieBraking": "Semelle-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1250,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.74,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF24": {
- "Description": "BB75000-BB75100-BB75300-BB75400",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 20.28,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1150,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 10.14,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 15
- },
- "SNCF25": {
- "Description": "BB8500-BB8600",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 14.9,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1110,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 7.45,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF26": {
- "Description": "CC72000-CC72100",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 20.19,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1140,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 10.095,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 6,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF27": {
- "Description": "TBB64800",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 80,
- "Length": 11.39,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1050,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 5.695,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 1,
- "RefTraction": 14
- },
- "SNCF28": {
- "Description": "B81500-tricaisse-AGC-bimode",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 57.4,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 9.5667,
- "SourceSpacing": 19.1333,
- "NbAxlePerVeh": 2.7,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 6
- },
- "SNCF29": {
- "Description": "B81500-B82500-quadricaisse-AGC-bimode",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 72.8,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 9.1,
- "SourceSpacing": 18.2,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 6
- },
- "SNCF30": {
- "Description": "B83500-B84500-B85900-4-caisses-Regiolis",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 71.82,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 8.9775,
- "SourceSpacing": 17.955,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 6
- },
- "SNCF31": {
- "Description": "B83500-B84500-B85000-6-caisses-Regiolis",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 110,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 6,
- "FirstSourcePosition": 9.1667,
- "SourceSpacing": 18.3333,
- "NbAxlePerVeh": 2.7,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 6
- },
- "SNCF32": {
- "Description": "X4500-modernise",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 43.48,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 860,
- "TrailerWheelDiameter": 860,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 2,
- "FirstSourcePosition": 10.87,
- "SourceSpacing": 21.74,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 9
- },
- "SNCF33": {
- "Description": "X72500-X72600-X72700-bicaisse-X-TER",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 52.9,
- "TrailerBogieBraking": "Disque-+-semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 2,
- "FirstSourcePosition": 13.225,
- "SourceSpacing": 26.45,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 3,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 7
- },
- "SNCF34": {
- "Description": "X72500-X72600-X72700-tricaisse-X-TER",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 78.5,
- "TrailerBogieBraking": "Disque-+-semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 13.0833,
- "SourceSpacing": 26.1667,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 3,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 7
- },
- "SNCF35": {
- "Description": "X73500-X73600-X73700-X73800-73900-A-TER",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 28.9,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 14.45,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 8
- },
- "SNCF36": {
- "Description": "X76500-tricaisse-AGC",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 57.4,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 9.5667,
- "SourceSpacing": 19.1333,
- "NbAxlePerVeh": 2.7,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 6
- },
- "SNCF37": {
- "Description": "X76500-quadricaisse-AGC",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 72.8,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 9.1,
- "SourceSpacing": 18.2,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 6
- },
- "SNCF38": {
- "Description": "Z20500-Z20600-Z20700-Z20800-Z20900-Z21000-quadricaisse-Z2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 103.508,
- "TrailerBogieBraking": "Disque-+-semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 12.9385,
- "SourceSpacing": 25.877,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 3
- },
- "SNCF39": {
- "Description": "Z20500-Z20600-Z20700-Z20800-pentacaisse-Z2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 129.4,
- "TrailerBogieBraking": "Disque-+-semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 5,
- "FirstSourcePosition": 12.94,
- "SourceSpacing": 25.88,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 3
- },
- "SNCF40": {
- "Description": "Z21500-Z21600-Z21700-ZTER",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 79.2,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 13.2,
- "SourceSpacing": 26.4,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 4
- },
- "SNCF41": {
- "Description": "Z22500-Z22600-pentacaisse-MI2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 112,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "Semelle-composite",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 5,
- "FirstSourcePosition": 11.2,
- "SourceSpacing": 22.4,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF42": {
- "Description": "Z23500-TER-2N-PG",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 52.5,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 2,
- "FirstSourcePosition": 13.125,
- "SourceSpacing": 26.25,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF43": {
- "Description": "Z24500-Z24600-Z24700-Z24800-tricaisse-TER-2N-NG",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 81.1,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 13.5167,
- "SourceSpacing": 27.0333,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 4
- },
- "SNCF44": {
- "Description": "Z26500-Z26600-quadricaisse-TER-2N-NG",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 107.5,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 13.4375,
- "SourceSpacing": 26.875,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 4
- },
- "SNCF45": {
- "Description": "Z26500-Z26600-pentacaisse-TER-2N-NG",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 133.9,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 920,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 5,
- "FirstSourcePosition": 13.39,
- "SourceSpacing": 26.78,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 4
- },
- "SNCF46": {
- "Description": "Z27500-Z27600-Z27700-Z27800-Z27900-tricaisse-ZGC",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 57.4,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 3,
- "FirstSourcePosition": 9.5667,
- "SourceSpacing": 19.1333,
- "NbAxlePerVeh": 2.7,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 5
- },
- "SNCF47": {
- "Description": "Z27500-Z27600-Z27700-Z27800-Z27900-quadricaisse-ZGC",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 72.8,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 9.1,
- "SourceSpacing": 18.2,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 5
- },
- "SNCF48": {
- "Description": "Z50000-7-caisses-NAT",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 94.31,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 7,
- "FirstSourcePosition": 6.7364,
- "SourceSpacing": 13.4729,
- "NbAxlePerVeh": 2.3,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF49": {
- "Description": "Z50000-8-caisses-NAT",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 112.5,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 8,
- "FirstSourcePosition": 7.0313,
- "SourceSpacing": 14.0625,
- "NbAxlePerVeh": 2.25,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF50": {
- "Description": "Z51500-Z54500-Z54900-4-caisses-REGIOLIS",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 71.82,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 8.9775,
- "SourceSpacing": 17.955,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 5
- },
- "SNCF51": {
- "Description": "Z51500-6-caisses-REGIOLIS",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 110,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 6,
- "FirstSourcePosition": 9.1667,
- "SourceSpacing": 18.3333,
- "NbAxlePerVeh": 2.7,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 5
- },
- "SNCF52": {
- "Description": "Z31500-REGIOLIS-LEX",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 71.82,
- "TrailerBogieBraking": "Disque-garniture-frittee",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 8.9775,
- "SourceSpacing": 17.955,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 3,
- "RefTraction": 5
- },
- "SNCF53": {
- "Description": "Z55500-Z55600-Z55700-courte�Z56300-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 80.945,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 6,
- "FirstSourcePosition": 6.7454,
- "SourceSpacing": 13.4908,
- "NbAxlePerVeh": 2.3,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF54": {
- "Description": "Z55500-Z55600-Z55700-courte2-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 82.695,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 6,
- "FirstSourcePosition": 6.8913,
- "SourceSpacing": 13.7825,
- "NbAxlePerVeh": 2.3,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF55": {
- "Description": "Z55500-Z55600-Z55700-moyenne-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 94.975,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 7,
- "FirstSourcePosition": 6.7839,
- "SourceSpacing": 13.5679,
- "NbAxlePerVeh": 2.3,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF56": {
- "Description": "Z55500-Z55600-Z55700-Z57000-longue-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 109.91,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 8,
- "FirstSourcePosition": 6.8694,
- "SourceSpacing": 13.7388,
- "NbAxlePerVeh": 2.3,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF57": {
- "Description": "Z56500-Z56700-V200-intervilles-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 109.91,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 8,
- "FirstSourcePosition": 6.8694,
- "SourceSpacing": 13.7388,
- "NbAxlePerVeh": 2.25,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF58": {
- "Description": "Z55500-Z55600-Z55700-extra-longue-REGIO-2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 135.375,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 10,
- "FirstSourcePosition": 6.7688,
- "SourceSpacing": 13.5375,
- "NbAxlePerVeh": 2.2,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF59": {
- "Description": "Z56600-extra-longue-REGIO-2N-V200",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 135.375,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
- "MotorWheelDiameter": 840,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 10,
- "FirstSourcePosition": 6.7688,
- "SourceSpacing": 13.5375,
- "NbAxlePerVeh": 2.2,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 4
- },
- "SNCF60": {
- "Description": "Z5600-Z5700-quadricaisse",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 98.76,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "RefAerodynamic": 0,
- "NbCoach": 4,
- "FirstSourcePosition": 12.345,
- "SourceSpacing": 24.69,
- "NbAxlePerVeh": 4,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 3
- },
- "SNCF61": {
- "Description": "Z5600-Z5700-pentacaisse",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 123.04,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 5,
- "FirstSourcePosition": 12.304,
- "SourceSpacing": 24.608,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 3
- },
- "SNCF62": {
- "Description": "Z5600-Z5700-hexacaisse",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 147.32,
- "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 6,
- "FirstSourcePosition": 12.2767,
- "SourceSpacing": 24.5533,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 3
- },
- "SNCF63": {
- "Description": "Z6400-Z6500",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 92.43,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 800,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 11.5538,
- "SourceSpacing": 23.1075,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 3,
- "RefTraction": 2
- },
- "SNCF64": {
- "Description": "Z7300-Z7500-Z9500-Z9600-Z11500-Z2",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 50.2,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1000,
- "TrailerWheelDiameter": 890,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 2,
- "FirstSourcePosition": 12.55,
- "SourceSpacing": 25.1,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 9
- },
- "SNCF65": {
- "Description": "Z8100-Z8200-MI79",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 104.05,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 13.0063,
- "SourceSpacing": 26.0125,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 2
- },
- "SNCF66": {
- "Description": "Z8800-Z8900-Z2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 98.76,
- "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-composite",
- "MotorBogieBraking": "Semelle-frittee",
- "MotorWheelDiameter": 1020,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 12.345,
- "SourceSpacing": 24.69,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 3
- },
- "SNCF67": {
- "Description": "U25500-Tram-Train-Avento",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 100,
- "Length": 36.678,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 660,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 5,
- "FirstSourcePosition": 3.6678,
- "SourceSpacing": 7.3356,
- "NbAxlePerVeh": 1.6,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 2,
- "RefTransfer": 4,
- "RefTraction": 13
- },
- "SNCF68": {
- "Description": "U52500-U53500-U53600-U53700-Tram-Train-Citadis-Dualis-�-TTNG",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 100,
- "Length": 42,
- "TrailerBogieBraking": "Disque-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": 660,
- "TrailerWheelDiameter": null,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 5.25,
- "SourceSpacing": 10.5,
- "NbAxlePerVeh": 2.5,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 2,
- "RefTransfer": 4,
- "RefTraction": 13
- },
- "SNCF69": {
- "Description": "Voiture-V2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 160,
- "Length": 26.4,
- "TrailerBogieBraking": "Disque",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 13.2,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 4,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 11
- },
- "SNCF70": {
- "Description": "Voiture-VB2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 24.28,
- "TrailerBogieBraking": "Disque-+-Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 12.14,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 11
- },
- "SNCF71": {
- "Description": "Voiture-VO2N-Voiture-VR2N",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 24.78,
- "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 12.39,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 11
- },
- "SNCF72": {
- "Description": "Voiture-freinee-composite-LUNEA-VSOE",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 26.4,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 13.2,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 11
- },
- "SNCF73": {
- "Description": "Voiture-freinee-fonte-CORAIL-VU-VTU-VSE",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 26.4,
- "TrailerBogieBraking": "Semelle-fonte-disques-garniture-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 13.2,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 12
- },
- "SNCF74": {
- "Description": "Voiture-USI",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 25.094,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 12.547,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 11
- },
- "SNCF75": {
- "Description": "Rame-RIB-RIO-RRR",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 140,
- "Length": 99,
- "TrailerBogieBraking": "Disque-garniture-composite+-semelle-frittee-ou-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 840,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 4,
- "FirstSourcePosition": 12.375,
- "SourceSpacing": 24.75,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 5,
- "RefTransfer": 2,
- "RefTraction": 10
- },
- "SNCF76": {
- "Description": "Wagon-FRET-PLAT-freine-composite-sans-chargement",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 17,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 0,
- "NbCoach": 1,
- "FirstSourcePosition": 8.5,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 3,
- "RefTransfer": 2,
- "RefTraction": 10
- },
- "SNCF77": {
- "Description": "Wagon-FRET-PLAT-freine-composite-sans-chargement",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 17,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 0,
- "NbCoach": 1,
- "FirstSourcePosition": 8.5,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 10
- },
- "SNCF78": {
- "Description": "Wagon-FRET-freine-composite",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 17,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.5,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 10
- },
- "SNCF79": {
- "Description": "Wagon-FRET-freine-fonte",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 120,
- "Length": 17,
- "TrailerBogieBraking": "Semelle-fonte",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 8.5,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 1,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 10
- },
- "SNCF80": {
- "Description": "Fourgons-&-porte-autos-freine-composite",
- "Reference": "SNCF 25/02/2022",
- "Vmax": 200,
- "Length": 26.4,
- "TrailerBogieBraking": "Semelle-composite",
- "MotorBogieBraking": "-",
- "MotorWheelDiameter": null,
- "TrailerWheelDiameter": 920,
- "ReflectingBarrierEffect": 1,
- "NbCoach": 1,
- "FirstSourcePosition": 13.2,
- "SourceSpacing": 0,
- "NbAxlePerVeh": 4,
- "RefAerodynamic": 0,
- "RefRoughness": 2,
- "RefContact": 4,
- "RefTransfer": 2,
- "RefTraction": 10
- }
+ "EU1": {
+ "Description": "TestTrain EU",
+ "Reference": "Test 11/2022",
+ "Vmax": 300,
+ "Length": 200.19,
+ "TrailerBogieBraking": "Disques-garniture-frittee",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 10,
+ "FirstSourcePosition": 10.0095,
+ "SourceSpacing": 20.019,
+ "NbAxlePerVeh": 2.6,
+ "RefAerodynamic": "EU1",
+ "RefRoughness": "EU4",
+ "RefContact": "EU5",
+ "RefTransfer": "EU2",
+ "RefTraction": "EU1"
+ },
+ "SNCF1": {
+ "Description": "TGV-00-100-TGV-SE",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 300,
+ "Length": 200.19,
+ "TrailerBogieBraking": "Disques-garniture-frittee",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 10,
+ "FirstSourcePosition": 10.0095,
+ "SourceSpacing": 20.019,
+ "NbAxlePerVeh": 2.6,
+ "RefAerodynamic": "SNCF1",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF1"
+ },
+ "SNCF2": {
+ "Description": "TGV200-600-700-TGV-Duplex-DASYE-TGV800-4700-TGV-2N2-TGV4300-TGV-Thalys-PBKATGV500-4500-TGV-R-TGV-Thalys-PBA-TGV4400-TGV-POS",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 320,
+ "Length": 200.19,
+ "TrailerBogieBraking": "Disques",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 910,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 10,
+ "FirstSourcePosition": 10.0095,
+ "SourceSpacing": 20.019,
+ "NbAxlePerVeh": 2.6,
+ "RefAerodynamic": "SNCF1",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF1"
+ },
+ "SNCF3": {
+ "Description": "TGV300-400-TGV-A",
+ "Vmax": 300,
+ "Length": 237.59,
+ "TrailerBogieBraking": "Disques",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 12,
+ "FirstSourcePosition": 9.8996,
+ "SourceSpacing": 19.7992,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF1",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF1"
+ },
+ "SNCF4": {
+ "Description": "TGV3200-TGV-TMST-Eurostar",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 300,
+ "Length": 393.72,
+ "TrailerBogieBraking": "Disques",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 20,
+ "FirstSourcePosition": 9.843,
+ "SourceSpacing": 19.686,
+ "NbAxlePerVeh": 2.4,
+ "RefAerodynamic": "SNCF1",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF1"
+ },
+ "SNCF5": {
+ "Description": "A1A-A1A-68000",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 130,
+ "Length": 18.01,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 950,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 9.005,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 6,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF6": {
+ "Description": "BB15000",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 17.48,
+ "TrailerBogieBraking": "Semelle-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.74,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF7": {
+ "Description": "BB16500-BB16600-BB16700",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 14.4,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1110,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.2,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF8": {
+ "Description": "BB17000-BB17100",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 14.94,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1110,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.47,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF9": {
+ "Description": "BB22200-BB22300-BB22400",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 17.48,
+ "TrailerBogieBraking": "Semelle-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.74,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF10": {
+ "Description": "BB25200",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 16.7,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.35,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF11": {
+ "Description": "BB25500-BB25600",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 14.7,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1110,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.35,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF12": {
+ "Description": "BB26000-BB26100-BB26200",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 17.71,
+ "TrailerBogieBraking": "Semelle-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.855,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF13": {
+ "Description": "BB27000-BB27100",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 19.72,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 9.86,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF14": {
+ "Description": "BB36000-BB36300",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 220,
+ "Length": 19.11,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 9.555,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF3",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF15": {
+ "Description": "BB37000",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 19.72,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 9.86,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF16": {
+ "Description": "BB61000-Vossloh-G-1206-BB",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 100,
+ "Length": 14.7,
+ "TrailerBogieBraking": "Disque+semelles",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1000,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.35,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF3",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF17": {
+ "Description": "BB63500-BB64000-BB64700",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 90,
+ "Length": 14.68,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1050,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.34,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF18": {
+ "Description": "BB66000-BB66100-BB66200-BB66300",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 14.89,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1100,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.445,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF19": {
+ "Description": "BB66400-BB66500",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 14.97,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1100,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.486,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF20": {
+ "Description": "BB67000-BB67100-BB67300",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 17.09,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.545,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF21": {
+ "Description": "BB67200",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 90,
+ "Length": 17.09,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.545,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF22": {
+ "Description": "BB67400-BB67500-BB67600",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 17.09,
+ "TrailerBogieBraking": "Semelle-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.545,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF23": {
+ "Description": "BB7200-BB7300-BB7400",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 17.48,
+ "TrailerBogieBraking": "Semelle-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1250,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.74,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF24": {
+ "Description": "BB75000-BB75100-BB75300-BB75400",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 20.28,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1150,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 10.14,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF15"
+ },
+ "SNCF25": {
+ "Description": "BB8500-BB8600",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 14.9,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1110,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 7.45,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF26": {
+ "Description": "CC72000-CC72100",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 20.19,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1140,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 10.095,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 6,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF27": {
+ "Description": "TBB64800",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 80,
+ "Length": 11.39,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1050,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 5.695,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF1",
+ "RefTraction": "SNCF14"
+ },
+ "SNCF28": {
+ "Description": "B81500-tricaisse-AGC-bimode",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 57.4,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 9.5667,
+ "SourceSpacing": 19.1333,
+ "NbAxlePerVeh": 2.7,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF29": {
+ "Description": "B81500-B82500-quadricaisse-AGC-bimode",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 72.8,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 9.1,
+ "SourceSpacing": 18.2,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF30": {
+ "Description": "B83500-B84500-B85900-4-caisses-Regiolis",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 71.82,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 8.9775,
+ "SourceSpacing": 17.955,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF31": {
+ "Description": "B83500-B84500-B85000-6-caisses-Regiolis",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 110,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 6,
+ "FirstSourcePosition": 9.1667,
+ "SourceSpacing": 18.3333,
+ "NbAxlePerVeh": 2.7,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF32": {
+ "Description": "X4500-modernise",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 43.48,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 860,
+ "TrailerWheelDiameter": 860,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 2,
+ "FirstSourcePosition": 10.87,
+ "SourceSpacing": 21.74,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF9"
+ },
+ "SNCF33": {
+ "Description": "X72500-X72600-X72700-bicaisse-X-TER",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 52.9,
+ "TrailerBogieBraking": "Disque-+-semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 2,
+ "FirstSourcePosition": 13.225,
+ "SourceSpacing": 26.45,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF3",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF7"
+ },
+ "SNCF34": {
+ "Description": "X72500-X72600-X72700-tricaisse-X-TER",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 78.5,
+ "TrailerBogieBraking": "Disque-+-semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 13.0833,
+ "SourceSpacing": 26.1667,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF3",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF7"
+ },
+ "SNCF35": {
+ "Description": "X73500-X73600-X73700-X73800-73900-A-TER",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 28.9,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 14.45,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF8"
+ },
+ "SNCF36": {
+ "Description": "X76500-tricaisse-AGC",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 57.4,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 9.5667,
+ "SourceSpacing": 19.1333,
+ "NbAxlePerVeh": 2.7,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF37": {
+ "Description": "X76500-quadricaisse-AGC",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 72.8,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 9.1,
+ "SourceSpacing": 18.2,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF6"
+ },
+ "SNCF38": {
+ "Description": "Z20500-Z20600-Z20700-Z20800-Z20900-Z21000-quadricaisse-Z2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 103.508,
+ "TrailerBogieBraking": "Disque-+-semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 12.9385,
+ "SourceSpacing": 25.877,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF39": {
+ "Description": "Z20500-Z20600-Z20700-Z20800-pentacaisse-Z2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 129.4,
+ "TrailerBogieBraking": "Disque-+-semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 5,
+ "FirstSourcePosition": 12.94,
+ "SourceSpacing": 25.88,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF40": {
+ "Description": "Z21500-Z21600-Z21700-ZTER",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 79.2,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 13.2,
+ "SourceSpacing": 26.4,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF41": {
+ "Description": "Z22500-Z22600-pentacaisse-MI2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 112,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "Semelle-composite",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 5,
+ "FirstSourcePosition": 11.2,
+ "SourceSpacing": 22.4,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF42": {
+ "Description": "Z23500-TER-2N-PG",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 52.5,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 2,
+ "FirstSourcePosition": 13.125,
+ "SourceSpacing": 26.25,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF43": {
+ "Description": "Z24500-Z24600-Z24700-Z24800-tricaisse-TER-2N-NG",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 81.1,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 13.5167,
+ "SourceSpacing": 27.0333,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF44": {
+ "Description": "Z26500-Z26600-quadricaisse-TER-2N-NG",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 107.5,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 13.4375,
+ "SourceSpacing": 26.875,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF45": {
+ "Description": "Z26500-Z26600-pentacaisse-TER-2N-NG",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 133.9,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 920,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 5,
+ "FirstSourcePosition": 13.39,
+ "SourceSpacing": 26.78,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF46": {
+ "Description": "Z27500-Z27600-Z27700-Z27800-Z27900-tricaisse-ZGC",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 57.4,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 3,
+ "FirstSourcePosition": 9.5667,
+ "SourceSpacing": 19.1333,
+ "NbAxlePerVeh": 2.7,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF5"
+ },
+ "SNCF47": {
+ "Description": "Z27500-Z27600-Z27700-Z27800-Z27900-quadricaisse-ZGC",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 72.8,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 9.1,
+ "SourceSpacing": 18.2,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF5"
+ },
+ "SNCF48": {
+ "Description": "Z50000-7-caisses-NAT",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 94.31,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 7,
+ "FirstSourcePosition": 6.7364,
+ "SourceSpacing": 13.4729,
+ "NbAxlePerVeh": 2.3,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF49": {
+ "Description": "Z50000-8-caisses-NAT",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 112.5,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 8,
+ "FirstSourcePosition": 7.0313,
+ "SourceSpacing": 14.0625,
+ "NbAxlePerVeh": 2.25,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF50": {
+ "Description": "Z51500-Z54500-Z54900-4-caisses-REGIOLIS",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 71.82,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 8.9775,
+ "SourceSpacing": 17.955,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF5"
+ },
+ "SNCF51": {
+ "Description": "Z51500-6-caisses-REGIOLIS",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 110,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 6,
+ "FirstSourcePosition": 9.1667,
+ "SourceSpacing": 18.3333,
+ "NbAxlePerVeh": 2.7,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF5"
+ },
+ "SNCF52": {
+ "Description": "Z31500-REGIOLIS-LEX",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 71.82,
+ "TrailerBogieBraking": "Disque-garniture-frittee",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 8.9775,
+ "SourceSpacing": 17.955,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF5"
+ },
+ "SNCF53": {
+ "Description": "Z55500-Z55600-Z55700-courte�Z56300-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 80.945,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 6,
+ "FirstSourcePosition": 6.7454,
+ "SourceSpacing": 13.4908,
+ "NbAxlePerVeh": 2.3,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF54": {
+ "Description": "Z55500-Z55600-Z55700-courte2-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 82.695,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 6,
+ "FirstSourcePosition": 6.8913,
+ "SourceSpacing": 13.7825,
+ "NbAxlePerVeh": 2.3,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF55": {
+ "Description": "Z55500-Z55600-Z55700-moyenne-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 94.975,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 7,
+ "FirstSourcePosition": 6.7839,
+ "SourceSpacing": 13.5679,
+ "NbAxlePerVeh": 2.3,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF56": {
+ "Description": "Z55500-Z55600-Z55700-Z57000-longue-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 109.91,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 8,
+ "FirstSourcePosition": 6.8694,
+ "SourceSpacing": 13.7388,
+ "NbAxlePerVeh": 2.3,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF57": {
+ "Description": "Z56500-Z56700-V200-intervilles-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 109.91,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 8,
+ "FirstSourcePosition": 6.8694,
+ "SourceSpacing": 13.7388,
+ "NbAxlePerVeh": 2.25,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF58": {
+ "Description": "Z55500-Z55600-Z55700-extra-longue-REGIO-2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 135.375,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 10,
+ "FirstSourcePosition": 6.7688,
+ "SourceSpacing": 13.5375,
+ "NbAxlePerVeh": 2.2,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF59": {
+ "Description": "Z56600-extra-longue-REGIO-2N-V200",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 135.375,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "2-essieux-fontes-+-semelle-composite",
+ "MotorWheelDiameter": 840,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 10,
+ "FirstSourcePosition": 6.7688,
+ "SourceSpacing": 13.5375,
+ "NbAxlePerVeh": 2.2,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF4"
+ },
+ "SNCF60": {
+ "Description": "Z5600-Z5700-quadricaisse",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 98.76,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "RefAerodynamic": "SNCF0",
+ "NbCoach": 4,
+ "FirstSourcePosition": 12.345,
+ "SourceSpacing": 24.69,
+ "NbAxlePerVeh": 4,
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF61": {
+ "Description": "Z5600-Z5700-pentacaisse",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 123.04,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 5,
+ "FirstSourcePosition": 12.304,
+ "SourceSpacing": 24.608,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF62": {
+ "Description": "Z5600-Z5700-hexacaisse",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 147.32,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-Semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 6,
+ "FirstSourcePosition": 12.2767,
+ "SourceSpacing": 24.5533,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF63": {
+ "Description": "Z6400-Z6500",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 92.43,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 800,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 11.5538,
+ "SourceSpacing": 23.1075,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF3",
+ "RefTraction": "SNCF2"
+ },
+ "SNCF64": {
+ "Description": "Z7300-Z7500-Z9500-Z9600-Z11500-Z2",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 50.2,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1000,
+ "TrailerWheelDiameter": 890,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 2,
+ "FirstSourcePosition": 12.55,
+ "SourceSpacing": 25.1,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF9"
+ },
+ "SNCF65": {
+ "Description": "Z8100-Z8200-MI79",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 104.05,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 13.0063,
+ "SourceSpacing": 26.0125,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF2"
+ },
+ "SNCF66": {
+ "Description": "Z8800-Z8900-Z2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 98.76,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-composite",
+ "MotorBogieBraking": "Semelle-frittee",
+ "MotorWheelDiameter": 1020,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 12.345,
+ "SourceSpacing": 24.69,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF3"
+ },
+ "SNCF67": {
+ "Description": "U25500-Tram-Train-Avento",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 100,
+ "Length": 36.678,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 660,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 5,
+ "FirstSourcePosition": 3.6678,
+ "SourceSpacing": 7.3356,
+ "NbAxlePerVeh": 1.6,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF2",
+ "RefTransfer": "SNCF4",
+ "RefTraction": "SNCF13"
+ },
+ "SNCF68": {
+ "Description": "U52500-U53500-U53600-U53700-Tram-Train-Citadis-Dualis-�-TTNG",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 100,
+ "Length": 42,
+ "TrailerBogieBraking": "Disque-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": 660,
+ "TrailerWheelDiameter": null,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 5.25,
+ "SourceSpacing": 10.5,
+ "NbAxlePerVeh": 2.5,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF2",
+ "RefTransfer": "SNCF4",
+ "RefTraction": "SNCF13"
+ },
+ "SNCF69": {
+ "Description": "Voiture-V2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 160,
+ "Length": 26.4,
+ "TrailerBogieBraking": "Disque",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 13.2,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF4",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF11"
+ },
+ "SNCF70": {
+ "Description": "Voiture-VB2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 24.28,
+ "TrailerBogieBraking": "Disque-+-Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 12.14,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF11"
+ },
+ "SNCF71": {
+ "Description": "Voiture-VO2N-Voiture-VR2N",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 24.78,
+ "TrailerBogieBraking": "Disque-garniture-composite-+-semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 12.39,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF11"
+ },
+ "SNCF72": {
+ "Description": "Voiture-freinee-composite-LUNEA-VSOE",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 26.4,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 13.2,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF11"
+ },
+ "SNCF73": {
+ "Description": "Voiture-freinee-fonte-CORAIL-VU-VTU-VSE",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 26.4,
+ "TrailerBogieBraking": "Semelle-fonte-disques-garniture-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 13.2,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF12"
+ },
+ "SNCF74": {
+ "Description": "Voiture-USI",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 25.094,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 12.547,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF11"
+ },
+ "SNCF75": {
+ "Description": "Rame-RIB-RIO-RRR",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 140,
+ "Length": 99,
+ "TrailerBogieBraking": "Disque-garniture-composite+-semelle-frittee-ou-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 840,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 4,
+ "FirstSourcePosition": 12.375,
+ "SourceSpacing": 24.75,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF5",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ },
+ "SNCF76": {
+ "Description": "Wagon-FRET-PLAT-freine-composite-sans-chargement",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 17,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 0,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.5,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF3",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ },
+ "SNCF77": {
+ "Description": "Wagon-FRET-PLAT-freine-composite-sans-chargement",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 17,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 0,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.5,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ },
+ "SNCF78": {
+ "Description": "Wagon-FRET-freine-composite",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 17,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.5,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ },
+ "SNCF79": {
+ "Description": "Wagon-FRET-freine-fonte",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 120,
+ "Length": 17,
+ "TrailerBogieBraking": "Semelle-fonte",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 8.5,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF1",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ },
+ "SNCF80": {
+ "Description": "Fourgons-&-porte-autos-freine-composite",
+ "Reference": "SNCF 25/02/2022",
+ "Vmax": 200,
+ "Length": 26.4,
+ "TrailerBogieBraking": "Semelle-composite",
+ "MotorBogieBraking": "-",
+ "MotorWheelDiameter": null,
+ "TrailerWheelDiameter": 920,
+ "ReflectingBarrierEffect": 1,
+ "NbCoach": 1,
+ "FirstSourcePosition": 13.2,
+ "SourceSpacing": 0,
+ "NbAxlePerVeh": 4,
+ "RefAerodynamic": "SNCF0",
+ "RefRoughness": "SNCF2",
+ "RefContact": "SNCF4",
+ "RefTransfer": "SNCF2",
+ "RefTraction": "SNCF10"
+ }
}
\ No newline at end of file
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosEU_2020.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayCnossosEU_2020.json
similarity index 100%
rename from noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosEU_2020.json
rename to noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayCnossosEU_2020.json
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosSNCF_2021.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayCnossosSNCF_2021.json
similarity index 100%
rename from noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosSNCF_2021.json
rename to noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayCnossosSNCF_2021.json
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos_2015.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayVehiclesCnossos_2015.json
similarity index 100%
rename from noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesCnossos_2015.json
rename to noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayVehiclesCnossos_2015.json
diff --git a/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesNMPB.json b/noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayVehiclesNMPB.json
similarity index 100%
rename from noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/RailwayVehiclesNMPB.json
rename to noisemodelling-emission/src/main/resources/org/noise_planet/noisemodelling/emission/railway/outdated/RailwayVehiclesNMPB.json
diff --git a/noisemodelling-emission/src/test/java/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosTest.java b/noisemodelling-emission/src/test/java/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosTest.java
index f50efd462..1b1ef97dd 100644
--- a/noisemodelling-emission/src/test/java/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosTest.java
+++ b/noisemodelling-emission/src/test/java/org/noise_planet/noisemodelling/emission/railway/RailwayCnossosTest.java
@@ -33,9 +33,9 @@ public class RailwayCnossosTest {
@Test
public void testUnknownVehicle() throws IOException {
- railwayCnossos.setVehicleDataFile("RailwayVehiclesNMPB.json");
+ railwayCnossos.setVehicleDataFile("RailwayVehiclesCnossos.json");
railwayCnossos.setTrainSetDataFile("RailwayTrainsets.json");
- railwayCnossos.setRailwayDataFile("RailwayCnossosSNCF_2021.json");
+ railwayCnossos.setRailwayDataFile("RailwayEmissionCnossos.json");
String vehCat = "notsupported";
@@ -45,10 +45,10 @@ public void testUnknownVehicle() throws IOException {
double idlingTime = 0;
int nTracks = 2;
- int trackTransfer = 7;
- int railRoughness = 3;
- int impactNoise = 1;
- int bridgeTransfert = 0;
+ String trackTransfer = "7";
+ String railRoughness = "3";
+ String impactNoise = "1";
+ String bridgeTransfert = "0";
int curvature = 0;
boolean isTunnel = false;
@@ -69,11 +69,11 @@ public void testUnknownVehicle() throws IOException {
@Test
public void Test_Cnossos_Rail_emission_section_1() throws IOException {
- railwayCnossos.setVehicleDataFile("RailwayVehiclesCnossos_2015.json");
+ railwayCnossos.setVehicleDataFile("outdated/RailwayVehiclesCnossos_2015.json");
railwayCnossos.setTrainSetDataFile("RailwayTrainsets.json");
- railwayCnossos.setRailwayDataFile("RailwayCnossosEU_2020.json");
+ railwayCnossos.setRailwayDataFile("outdated/RailwayCnossosEU_2020.json");
- double[] expectedValuesLWRolling = new double[]{46.6292393707431, 47.5930887825746, 49.4885539543655, 50.8452515062722, 48.2904168235536, 47.5599118826212, 48.3659880536885, 53.6850278385126, 55.1794804329062, 56.4435891375742, 57.3811010252223, 58.0623572653650, 59.8295159717300, 59.4546602463242, 56.5427214224336, 52.1790459841615, 54.5278138699040, 53.2809012600130, 51.1582349752733, 48.8021326510878, 49.2065949846659, 48.6821233578964, 48.6765663190977, 50.2184119171958};
+ double[] expectedValuesLWRolling = new double[]{46.62923937074308, 47.5930887825746, 49.48855395436553, 50.84525150627218, 48.290416823553656, 47.55991120199147, 48.36598621415334, 53.685023895294904, 55.17947382327178, 56.44358069598965, 57.381090582936295, 58.06233085323542, 59.82942366487711, 59.45435581560096, 56.541564191934896, 52.173989709214666, 54.51129722944201, 53.2570463370446, 51.118153521942574, 48.73174211849708, 49.13517759611298, 48.60046539576308, 48.58746317902302, 50.14566621392022};
double[] expectedValuesLWTractionA = new double[]{46.8200805302231, 42.7200805302231, 40.5200805302231, 42.5200805302231, 40.7200805302231, 40.7200805302231, 40.9200805302231, 42.7200805302231, 42.5200805302231, 43.6200805302231, 43.5200805302231, 46.5200805302231, 43.1200805302231, 43.0200805302231, 43.0200805302231, 42.0200805302231, 42.0200805302231, 47.3200805302231, 40.4200805302231, 37.4200805302231, 34.9200805302231, 32.0200805302231, 29.4200805302231, 27.1200805302231};
double[] expectedValuesLWTractionB = new double[]{51.1200805302231, 47.9200805302231, 43.4200805302231, 41.9200805302231, 41.2200805302231, 41.5200805302231, 40.8200805302231, 40.6200805302231, 40.3200805302231, 40.7200805302231, 40.7200805302231, 44.7200805302231, 40.6200805302231, 40.9200805302231, 40.8200805302231, 41.0200805302231, 41.1200805302231, 46.2200805302231, 39.4200805302231, 36.6200805302231, 33.9200805302231, 31.3200805302231, 28.8200805302231, 26.6200805302231};
String[] typeNoise = new String[] {"ROLLING", "TRACTIONA", "TRACTIONB"};
@@ -88,10 +88,10 @@ public void Test_Cnossos_Rail_emission_section_1() throws IOException {
// Initiate Section Train
int nTracks = 2;
- int trackTransfer = 7;
- int railRoughness = 3;
- int impactNoise = 1;
- int bridgeTransfert = 0;
+ String trackTransfer = "7";
+ String railRoughness = "3";
+ String impactNoise = "1";
+ String bridgeTransfert = "0";
int curvature = 0;
boolean isTunnel = false;
double vMaxInfra = 160;
@@ -114,6 +114,7 @@ public void Test_Cnossos_Rail_emission_section_1() throws IOException {
RailWayParameters lWRailWay = railwayCnossos.evaluate(vehicleParameters, trackParameters);
+
for(int i=0;i RoadCnossos.evaluate(rsParameters));
}
}
+
+
+
+ @Test
+ public void TGlobalBP() throws IOException {
+ double lv_speed = 50;
+ int lv_per_hour = 3747;
+ double mv_speed = 50;
+ int mv_per_hour = 0;
+ double hgv_speed = 50;
+ int hgv_per_hour = 479;
+ double wav_speed = 50;
+ int wav_per_hour = 0;
+ double wbv_speed = 50;
+ int wbv_per_hour = 0;
+
+ double Temperature = 20;
+ String RoadSurface = "FR_R2";
+ double Pm_stud = 0;
+ double Ts_stud = 0;
+ double Junc_dist = 0;
+ int Junc_type = 0;
+ double globalLw = 0;
+ for(int i = 0; i < FREQUENCIES.length; i++){
+ RoadCnossosParameters rsParameters = new RoadCnossosParameters(lv_speed, mv_speed, hgv_speed, wav_speed, wbv_speed, lv_per_hour, mv_per_hour, hgv_per_hour, wav_per_hour, wbv_per_hour, FREQUENCIES[i], Temperature, RoadSurface, Ts_stud, Pm_stud, Junc_dist, Junc_type);
+ rsParameters.setFileVersion(1);
+ rsParameters.setWay(3);
+ double lw = RoadCnossos.evaluate(rsParameters);
+ globalLw += Utils.dbToW(A_WEIGHTING[i]+lw);
+ }
+ assertEquals(89.6, Utils.wToDba(globalLw), 0.1);
+ }
}
\ No newline at end of file
diff --git a/noisemodelling-jdbc/pom.xml b/noisemodelling-jdbc/pom.xml
index f02d84752..b4f740da2 100644
--- a/noisemodelling-jdbc/pom.xml
+++ b/noisemodelling-jdbc/pom.xml
@@ -10,7 +10,7 @@
org.noise-planet
noisemodelling-parent
- 5.0.2-SNAPSHOT
+ 6.0.1-SNAPSHOT
../pom.xml
Compute sound propagation rays.
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/DelaunayReceiversMaker.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/DelaunayReceiversMaker.java
index cf28ddf19..ab14de75e 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/DelaunayReceiversMaker.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/DelaunayReceiversMaker.java
@@ -10,7 +10,7 @@
package org.noise_planet.noisemodelling.jdbc;
-import org.h2gis.api.EmptyProgressVisitor;
+import org.h2gis.api.ProgressVisitor;
import org.h2gis.utilities.*;
import org.h2gis.utilities.dbtypes.DBTypes;
import org.h2gis.utilities.dbtypes.DBUtils;
@@ -29,13 +29,11 @@
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.Wall;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.tinfour.common.Vertex;
import java.io.IOException;
import java.sql.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.h2gis.utilities.GeometryTableUtilities.getGeometryColumnNames;
@@ -48,10 +46,10 @@
*/
public class DelaunayReceiversMaker extends GridMapMaker {
private static final int BATCH_MAX_SIZE = 100;
- private Logger logger = LoggerFactory.getLogger(DelaunayReceiversMaker.class);
+ private final Logger logger = LoggerFactory.getLogger(DelaunayReceiversMaker.class);
private double roadWidth = 2;
private double maximumArea = 75;
- private long nbreceivers = 0;
+ private long receiversCount = 0;
private double receiverHeight = 1.6;
private double buildingBuffer = 2;
private String exceptionDumpFolder = "";
@@ -59,6 +57,13 @@ public class DelaunayReceiversMaker extends GridMapMaker {
private double epsilon = 1e-6;
private double geometrySimplificationDistance = 1;
private boolean isoSurfaceInBuildings = false;
+ private boolean exportTrianglesGeometries = false;
+
+ /**
+ * Do not evaluate a computation cell if there is no source geometries at least at x meters from the cell envelope
+ * It is ignored if it is NaN or if source table name is not provided
+ */
+ private double minimalSourceGeometriesDistanceToComputeCell = Double.NaN;
/**
* Create constructor DelaunayReceiversMaker
@@ -69,6 +74,20 @@ public DelaunayReceiversMaker(String buildingsTableName, String sourcesTableName
super(buildingsTableName, sourcesTableName);
}
+ /**
+ * @return True if triangle geometries are exported
+ */
+ public boolean isExportTrianglesGeometries() {
+ return exportTrianglesGeometries;
+ }
+
+ /**
+ * @param exportTrianglesGeometries Set true in order to export triangle geometries
+ */
+ public void setExportTrianglesGeometries(boolean exportTrianglesGeometries) {
+ this.exportTrianglesGeometries = exportTrianglesGeometries;
+ }
+
/**
* @return True if isosurface will be placed into buildings
*/
@@ -92,15 +111,38 @@ public void setIsoSurfaceInBuildings(boolean isoSurfaceInBuildings) {
* @param triangleTableName The name of the database table where the triangles will be stored.
* @throws SQLException Thrown if a database access error or other SQL-related error occurs.
*/
- public void run(Connection connection, String verticesTableName, String triangleTableName) throws SQLException {
- initialize(connection, new EmptyProgressVisitor());
-
+ public void run(Connection connection, String verticesTableName, String triangleTableName, ProgressVisitor progressVisitor) throws SQLException {
+ initialize(connection);
AtomicInteger pk = new AtomicInteger(0);
- for(int i=0; i < getGridDim(); i++) {
- for(int j=0; j < getGridDim(); j++) {
+ ProgressVisitor progressVisitorNM = progressVisitor.subProcess(getGridDim() * getGridDim());
+ for(int i=0; i < getGridDim() && !progressVisitorNM.isCanceled(); i++) {
+ for(int j=0; j < getGridDim() && !progressVisitorNM.isCanceled(); j++) {
+
+ if(!Double.isNaN(minimalSourceGeometriesDistanceToComputeCell) && !sourcesTableName.isEmpty()) {
+ // Check if there is a source near fence
+ Envelope cellEnvelope = getCellEnv(mainEnvelope, i,
+ j, getCellWidth(), getCellHeight());
+ if(!hasSourcesNearEnvelope(connection, cellEnvelope, minimalSourceGeometriesDistanceToComputeCell)) {
+ if (verbose) {
+ int ij = i * gridDim + j + 1;
+ logger.info("Skip processing of cell {} / {} no source near cell", ij, gridDim * gridDim);
+ }
+ progressVisitorNM.endStep();
+ continue;
+ }
+ }
try {
+ if (verbose) {
+ int ij = i * gridDim + j + 1;
+ logger.info("Processing of cell {} / {}", ij, gridDim * gridDim);
+ }
generateReceivers(connection, i, j, verticesTableName,
triangleTableName, pk);
+ if(verbose) {
+ int ij = i * gridDim + j + 1;
+ logger.info("End processing of cell {} / {}", ij, gridDim * gridDim);
+ }
+ progressVisitorNM.endStep();
} catch (IOException | LayerDelaunayError ex) {
throw new SQLException(ex);
}
@@ -261,7 +303,7 @@ private void feedDelaunay(List buildings, LayerDelaunay delaunayTool,
* @param maxSrcDist Maximum propagation distance
* @param minRecDist Minimal distance receiver-source
* @param maximumArea Maximum area of triangles
- * @throws LayerDelaunayError
+ * @throws LayerDelaunayError if an error occurs during the Delaunay triangulation process.
*/
public void computeDelaunay(LayerDelaunay cellMesh,
Envelope mainEnvelope, int cellI, int cellJ, double maxSrcDist, Collection sources,
@@ -307,7 +349,6 @@ public void computeDelaunay(LayerDelaunay cellMesh,
minRecDist, buildingBuffer);
// Process delaunay
- logger.info("Begin delaunay");
cellMesh.setRetrieveNeighbors(false);
// Add cell envelope
if (maximumArea > 1) {
@@ -325,17 +366,16 @@ public void computeDelaunay(LayerDelaunay cellMesh,
}
}
cellMesh.processDelaunay();
- logger.info("End delaunay");
}
/**
* Retrieves the computation envelope based on data stored in the database tables.
* @param connection the database connection.
* @return the computation envelope containing the bounding box of the data stored in the specified tables.
- * @throws SQLException
+ * @throws SQLException if a database access error occurs.
*/
@Override
- protected Envelope getComputationEnvelope(Connection connection) throws SQLException {
+ public Envelope getComputationEnvelope(Connection connection) throws SQLException {
Envelope computationEnvelope = new Envelope();
DBTypes dbTypes = DBUtils.getDBType(connection);
if(!sourcesTableName.isEmpty() && JDBCUtilities.getRowCount(connection, sourcesTableName) > 0) {
@@ -363,15 +403,20 @@ public void setGeometrySimplificationDistance(double geometrySimplificationDista
public static void generateResultTable(Connection connection, String receiverTableName, String trianglesTableName,
AtomicInteger receiverPK, List vertices,
GeometryFactory geometryFactory, List triangles, int cellI,
- int cellJ, int gridDim) throws SQLException {
+ int cellJ, int gridDim, boolean exportTrianglesGeometries) throws SQLException {
+ int srid = geometryFactory.getSRID();
if(!JDBCUtilities.tableExists(connection, receiverTableName)) {
Statement st = connection.createStatement();
- st.execute("CREATE TABLE "+TableLocation.parse(receiverTableName)+"(pk serial NOT NULL, the_geom geometry not null, PRIMARY KEY (PK))");
+ st.execute("CREATE TABLE "+TableLocation.parse(receiverTableName)+"(pk serial NOT NULL, the_geom geometry(POINTZ, "+srid+") not null, PRIMARY KEY (PK))");
}
if(!JDBCUtilities.tableExists(connection, trianglesTableName)) {
Statement st = connection.createStatement();
- st.execute("CREATE TABLE "+TableLocation.parse(trianglesTableName)+"(pk serial NOT NULL, the_geom geometry , PK_1 integer not null, PK_2 integer not null, PK_3 integer not null, cell_id integer not null, PRIMARY KEY (PK))");
+ if(exportTrianglesGeometries) {
+ st.execute("CREATE TABLE " + TableLocation.parse(trianglesTableName) + "(pk serial NOT NULL, the_geom geometry(POLYGONZ, "+srid+") , PK_1 integer not null, PK_2 integer not null, PK_3 integer not null, cell_id integer not null, PRIMARY KEY (PK))");
+ } else {
+ st.execute("CREATE TABLE " + TableLocation.parse(trianglesTableName) + "(pk serial NOT NULL, PK_1 integer not null, PK_2 integer not null, PK_3 integer not null, cell_id integer not null, PRIMARY KEY (PK))");
+ }
}
int receiverPkOffset = receiverPK.get();
// Add vertices to receivers
@@ -392,15 +437,22 @@ public static void generateResultTable(Connection connection, String receiverTab
ps.executeBatch();
}
// Add triangles
- ps = connection.prepareStatement("INSERT INTO "+TableLocation.parse(trianglesTableName)+"(the_geom, PK_1, PK_2, PK_3, CELL_ID) VALUES (?, ?, ?, ?, ?);");
+ if(exportTrianglesGeometries) {
+ ps = connection.prepareStatement("INSERT INTO " + TableLocation.parse(trianglesTableName) + "(the_geom, PK_1, PK_2, PK_3, CELL_ID) VALUES (?, ?, ?, ?, ?);");
+ } else {
+ ps = connection.prepareStatement("INSERT INTO " + TableLocation.parse(trianglesTableName) + "(PK_1, PK_2, PK_3, CELL_ID) VALUES (?, ?, ?, ?);");
+ }
batchSize = 0;
for(Triangle t : triangles) {
- ps.setObject(1, geometryFactory.createPolygon(new Coordinate[]{vertices.get(t.getA()),
- vertices.get(t.getB()), vertices.get(t.getC()), vertices.get(t.getA())}));
- ps.setInt(2, t.getA() + receiverPkOffset);
- ps.setInt(3, t.getC() + receiverPkOffset);
- ps.setInt(4, t.getB() + receiverPkOffset);
- ps.setInt(5, cellI * gridDim + cellJ);
+ int rowIndex = 1;
+ if(exportTrianglesGeometries) {
+ ps.setObject(rowIndex++, geometryFactory.createPolygon(new Coordinate[]{vertices.get(t.getA()),
+ vertices.get(t.getB()), vertices.get(t.getC()), vertices.get(t.getA())}));
+ }
+ ps.setInt(rowIndex++, t.getA() + receiverPkOffset);
+ ps.setInt(rowIndex++, t.getC() + receiverPkOffset);
+ ps.setInt(rowIndex++, t.getB() + receiverPkOffset);
+ ps.setInt(rowIndex, cellI * gridDim + cellJ);
ps.addBatch();
batchSize++;
if (batchSize >= BATCH_MAX_SIZE) {
@@ -429,7 +481,7 @@ public void setEpsilon(double epsilon) {
* @param fetchEnvelope Fetch envelope
* @param doIntersection Truncate geometris
* @param sourceGeometries List to feed
- * @throws SQLException
+ * @throws SQLException if a database access error occurs
*/
public void fetchCellSource(Connection connection, Envelope fetchEnvelope, boolean doIntersection, List sourceGeometries)
throws SQLException {
@@ -477,12 +529,9 @@ public void fetchCellSource(Connection connection, Envelope fetchEnvelope, boole
}
}
- 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);
- }
+ public void generateReceivers(Connection connection, int cellI, int cellJ, String receiverTableName,
+ String trianglesTableName, AtomicInteger receiverPK)
+ throws SQLException, LayerDelaunayError, IOException {
// Compute the first pass delaunay mesh
// The first pass doesn't take account of additional
// vertices of neighbor cells at the borders
@@ -494,7 +543,7 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin
List sourceDelaunayGeometries = new LinkedList<>();
- if(!sourcesTableName.isEmpty()) {
+ if(!sourcesTableName.isEmpty() && roadWidth > 0) {
fetchCellSource(connection, cellEnvelope, true, sourceDelaunayGeometries);
}
@@ -506,6 +555,7 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin
geometryFactory);
LayerTinfour cellMesh = new LayerTinfour();
+ cellMesh.setVerbose(verbose);
cellMesh.setEpsilon(epsilon);
cellMesh.setDumpFolder(exceptionDumpFolder);
cellMesh.setMaxArea(maximumArea > 1 ? maximumArea : 0);
@@ -538,27 +588,124 @@ public void generateReceivers(Connection connection, int cellI, int cellJ, Strin
triangles = new ArrayList<>(cellMesh.getTriangles().size());
for (Triangle triangle : cellMesh.getTriangles()) {
if (triangle.getAttribute() == 0) {
- // place only triangles not associated to a building
+ // Keep only triangles that aren't associated with a building
triangles.add(triangle);
}
}
+ // Keep only referenced vertices
+ Map verticesIndexCorrespondence = new HashMap<>(); // Tinfour vertex index to our vertex index
+ List filteredVertices = new ArrayList<>(vertices.size());
+ for(Triangle triangle : triangles) {
+ for(int i = 0; i < 3; i++) {
+ updateTriangle(triangle, verticesIndexCorrespondence, i, vertices, filteredVertices);
+ }
+ }
+ vertices = filteredVertices;
} else {
triangles = cellMesh.getTriangles();
}
- nbreceivers += vertices.size();
+ receiversCount += vertices.size();
generateResultTable(connection, receiverTableName, trianglesTableName, receiverPK, vertices, geometryFactory,
- triangles, cellI, cellJ, gridDim);
+ triangles, cellI, cellJ, gridDim, exportTrianglesGeometries);
+ }
+
+ /**
+ * Insert into the new vertice list only vertices referenced in the triangles. If the vertex already exists
+ * in the mapping, its corresponding index is used; otherwise, the vertex is added to
+ * the vertices list and mapped to its new index.
+ *
+ * @param triangle The triangle to be updated, where vertex indices are modified as needed.
+ * @param tinFourIndexToListIndex A mapping of original vertex indices to their corresponding
+ * indices in the updated vertices list.
+ * @param cornerIndex The corner index of the triangle to update (0, 1, or 2).
+ * @param oldVerticesList The list of original vertices, from which new vertices are sourced if needed.
+ * @param vertices The list of updated vertices, where new vertices are added if they do not exist.
+ */
+ private static void updateTriangle(Triangle triangle, Map tinFourIndexToListIndex,
+ int cornerIndex, List oldVerticesList, List vertices) {
+ // get the original vertex index
+ int tinFourVertexIndex = triangle.get(cornerIndex);
+ // find if this vertex is already in our vertices list
+ if(tinFourIndexToListIndex.containsKey(tinFourVertexIndex)) {
+ // use the vertex index inserted by a previous triangle
+ triangle.set(cornerIndex, tinFourIndexToListIndex.get(tinFourVertexIndex));
+ } else {
+ // Not found, create a new vertex
+ vertices.add(oldVerticesList.get(tinFourVertexIndex));
+ triangle.set(cornerIndex, vertices.size() - 1);
+ }
}
public double getRoadWidth() {
return roadWidth;
}
+ /**
+ * Set the buffer around the roads where no receivers will be placed.
+ * If this value is equal to 0m, the roads will not be integrated into the triangulation
+ * So you can exclude cells with {@link #setMinimalSourceGeometriesDistanceToComputeCell(double)} and not use the
+ * road buffer.
+ * @param roadWidth Road width in meters
+ */
public void setRoadWidth(double roadWidth) {
this.roadWidth = roadWidth;
}
+ /**
+ * @return Do not evaluate a computation cell if there is no source geometries at least at x meters from the cell envelope
+ */
+ public double getMinimalSourceGeometriesDistanceToComputeCell() {
+ return minimalSourceGeometriesDistanceToComputeCell;
+ }
+
+ /**
+ * Do not evaluate a computation cell if there is no source geometries at least at x meters from the cell envelope.
+ * Evaluated on {@link #run(Connection, String, String, ProgressVisitor)}
+ * It is ignored if it is NaN or if source table name is not provided or does not exists
+ * @param minimalSourceGeometriesDistanceToComputeCell Distance in meters
+ */
+ public void setMinimalSourceGeometriesDistanceToComputeCell(double minimalSourceGeometriesDistanceToComputeCell) {
+ this.minimalSourceGeometriesDistanceToComputeCell = minimalSourceGeometriesDistanceToComputeCell;
+ }
+
+
+
+ /**
+ * Check if there is at least one source geometry near the envelope
+ * @param connection Active connection
+ * @param fetchEnvelope Fetch envelope
+ * @param minimalDistance Minimal distance from envelope
+ * @return True if there is at least one source geometry near the envelope
+ * @throws SQLException if a database access error occurs
+ */
+ public boolean hasSourcesNearEnvelope(Connection connection, Envelope fetchEnvelope, double minimalDistance)
+ throws SQLException {
+ DBTypes dbType = DBUtils.getDBType(connection.unwrap(Connection.class));
+ TableLocation sourceTableIdentifier = TableLocation.parse(sourcesTableName, dbType);
+ List geomFields = getGeometryColumnNames(connection, sourceTableIdentifier);
+ if (geomFields.isEmpty()) {
+ throw new SQLException(String.format("The table %s does not exists or does not contain a geometry field", sourceTableIdentifier));
+ }
+ String sourceGeomName = TableLocation.quoteIdentifier(geomFields.get(0));
+ boolean hasSource = false;
+ try (PreparedStatement st = connection.prepareStatement(
+ "SELECT 1 FROM " + sourcesTableName + " WHERE "
+ + sourceGeomName + " && ST_EXPAND(?::geometry,?) AND ST_DISTANCE(" + sourceGeomName + ", ?::geometry) <= ? LIMIT 1")) {
+ st.setObject(1, geometryFactory.toGeometry(fetchEnvelope));
+ st.setDouble(2, minimalDistance);
+ st.setObject(3, geometryFactory.toGeometry(fetchEnvelope));
+ st.setDouble(4, minimalDistance);
+ try (ResultSet rs = st.executeQuery()) {
+ if (rs.next()) {
+ hasSource = true;
+ }
+ }
+ }
+ return hasSource;
+ }
+
+
public double getMaximumArea() {
return maximumArea;
}
@@ -575,7 +722,7 @@ public void setReceiverHeight(double receiverHeight) {
this.receiverHeight = receiverHeight;
}
- public long getNbreceivers() {
- return nbreceivers;
+ public long getReceiversCount() {
+ return receiversCount;
}
}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/EmissionTableGenerator.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/EmissionTableGenerator.java
index b61aafcaf..82c02147f 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/EmissionTableGenerator.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/EmissionTableGenerator.java
@@ -47,7 +47,7 @@ public enum STANDARD_PERIOD {DAY, EVENING, NIGHT}
* Cache table fields in upper case in Map
* @param sourceFieldsCache map
* @param rs table to load
- * @throws SQLException If error
+ * @throws SQLException If error occurred
*/
public static void cacheFields(Map sourceFieldsCache, ResultSet rs) throws SQLException {
if (sourceFieldsCache.isEmpty()) {
@@ -244,13 +244,17 @@ public static double getSlope(Geometry g) {
/**
* Generate Train emission from train geometry tracks and train traffic
- * @param connection
- * @param railSectionTableName
- * @param railTrafficTableName
- * @param outputTable
- * @throws SQLException
+ * @param connection Database connection
+ * @param railSectionTableName Table name of rail sections
+ * @param railTrafficTableName Table name of rail traffic
+ * @param outputTable Output table name
+ * @param frequencyPrepend Prepend to frequency columns (e.g. "HZ_")
+ * @param vehicleDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for vehicle data configuration.
+ * @param trainSetDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for train set data configuration.
+ * @param railwayEmissionDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for railway metadata configuration.
+ * @throws SQLException If error occurred
*/
- public static void makeTrainLWTable(Connection connection, String railSectionTableName, String railTrafficTableName, String outputTable, String frequencyPrepend) throws SQLException {
+ public static void makeTrainLWTable(Connection connection, String railSectionTableName, String railTrafficTableName, String outputTable, String frequencyPrepend, String vehicleDataFile, String trainSetDataFile, String railwayEmissionDataFile) throws SQLException {
// drop table LW_RAILWAY if exists and the create and prepare the table
connection.createStatement().execute("drop table if exists " + outputTable);
@@ -293,7 +297,12 @@ public static void makeTrainLWTable(Connection connection, String railSectionTab
connection.createStatement().execute(createTableQuery.toString());
// Get Class to compute HZ
- RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,railSectionTableName, railTrafficTableName);
+ RailWayLWIterator railWayLWIterator;
+ try {
+ railWayLWIterator = new RailWayLWIterator(connection,railSectionTableName, railTrafficTableName, vehicleDataFile, trainSetDataFile, railwayEmissionDataFile);
+ } catch (IOException ex) {
+ throw new SQLException(ex);
+ }
while (railWayLWIterator.hasNext()) {
RailWayLWGeom railWayLWGeom = railWayLWIterator.next();
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/GridMapMaker.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/GridMapMaker.java
index cabdb3169..6ee2941da 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/GridMapMaker.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/GridMapMaker.java
@@ -10,15 +10,12 @@
package org.noise_planet.noisemodelling.jdbc;
-import org.h2gis.api.ProgressVisitor;
import org.h2gis.utilities.TableLocation;
import org.h2gis.utilities.dbtypes.DBTypes;
import org.h2gis.utilities.dbtypes.DBUtils;
import org.locationtech.jts.geom.*;
import org.noise_planet.noisemodelling.jdbc.input.DefaultTableLoader;
import org.noise_planet.noisemodelling.jdbc.utils.CellIndex;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import java.sql.*;
@@ -38,11 +35,17 @@ public abstract class GridMapMaker {
// Digital elevation model table. (Contains points or triangles)
protected String demTable = "";
protected String sound_lvl_field = "DB_M";
- // True if Z of sound source and receivers are relative to the ground
- protected boolean receiverHasAbsoluteZCoordinates = false;
- protected boolean sourceHasAbsoluteZCoordinates = false;
+ /** True if Z of receivers geometry is the altitude (sea level) or false if Z is relative to the ground (relative to digital elevation model)
+ * When the propagation area will be prepared. All coordinates will be converted into altitude if necessary.
+ */
+ protected boolean receiverHasSeaLevelZCoordinates = false;
+ /** True if Z of sources geometry is the altitude (sea level) or false if Z is relative to the ground (relative to digital elevation model)
+ * When the propagation area will be prepared. All coordinates will be converted into altitude if necessary.
+ */
+ protected boolean sourceHasSeaLevelZCoordinates = false;
protected double maximumPropagationDistance = 750;
protected double maximumReflectionDistance = 100;
+ protected double closeReceiverReflectionWallDistance = 0;
protected double gs = 0;
// Soil areas are split by the provided size in order to reduce the propagation time
protected double groundSurfaceSplitSideLength = 200;
@@ -52,7 +55,6 @@ public abstract class GridMapMaker {
public boolean verbose = true;
protected boolean computeHorizontalDiffraction = true;
protected boolean computeVerticalDiffraction = true;
-
protected GeometryFactory geometryFactory;
// Initialised attributes
@@ -134,14 +136,14 @@ public double getCellHeight() {
return mainEnvelope.getHeight() / gridDim;
}
- abstract protected Envelope getComputationEnvelope(Connection connection) throws SQLException;
+ abstract public Envelope getComputationEnvelope(Connection connection) throws SQLException;
/**
* Fetch scene attributes, compute best computation cell size.
* @param connection Active connection
- * @throws java.sql.SQLException
+ * @throws java.sql.SQLException If some table are not found or parameters are invalid
*/
- public void initialize(Connection connection, ProgressVisitor progression) throws SQLException {
+ public void initialize(Connection connection) throws SQLException {
if(soundReflectionOrder > 0 && maximumPropagationDistance < maximumReflectionDistance) {
throw new SQLException(new IllegalArgumentException(
"Maximum wall seeking distance cannot be superior than maximum propagation distance"));
@@ -228,7 +230,7 @@ public String getSoilTableName() {
* @return True if provided Z value are sea level (false for relative to ground level)
*/
public boolean isReceiverHasAbsoluteZCoordinates() {
- return receiverHasAbsoluteZCoordinates;
+ return receiverHasSeaLevelZCoordinates;
}
/**
@@ -236,21 +238,21 @@ public boolean isReceiverHasAbsoluteZCoordinates() {
* @param receiverHasAbsoluteZCoordinates True if provided Z value are sea level (false for relative to ground level)
*/
public void setReceiverHasAbsoluteZCoordinates(boolean receiverHasAbsoluteZCoordinates) {
- this.receiverHasAbsoluteZCoordinates = receiverHasAbsoluteZCoordinates;
+ this.receiverHasSeaLevelZCoordinates = receiverHasAbsoluteZCoordinates;
}
/**
* @return True if provided Z value are sea level (false for relative to ground level)
*/
public boolean isSourceHasAbsoluteZCoordinates() {
- return sourceHasAbsoluteZCoordinates;
+ return sourceHasSeaLevelZCoordinates;
}
/**
* @param sourceHasAbsoluteZCoordinates True if provided Z value are sea level (false for relative to ground level)
*/
public void setSourceHasAbsoluteZCoordinates(boolean sourceHasAbsoluteZCoordinates) {
- this.sourceHasAbsoluteZCoordinates = sourceHasAbsoluteZCoordinates;
+ this.sourceHasSeaLevelZCoordinates = sourceHasAbsoluteZCoordinates;
}
public boolean iszBuildings() {
@@ -354,6 +356,22 @@ public void setMaximumReflectionDistance(double maximumReflectionDistance) {
this.maximumReflectionDistance = maximumReflectionDistance;
}
+ /**
+ * @return Maximum receiver-to-wall distance in meters below which reflection cut profiles can be ignored.
+ * A value of 0 means the optional filter is disabled.
+ */
+ public double getCloseReceiverReflectionWallDistance() {
+ return closeReceiverReflectionWallDistance;
+ }
+
+ /**
+ * @param closeReceiverReflectionWallDistance Maximum receiver-to-wall distance in meters below which
+ * reflection cut profiles can be ignored. A value of 0 disables the filter.
+ */
+ public void setCloseReceiverReflectionWallDistance(double closeReceiverReflectionWallDistance) {
+ this.closeReceiverReflectionWallDistance = closeReceiverReflectionWallDistance;
+ }
+
/**
* @return Sound reflection order. 0 order mean 0 reflection depth.
* 2 means propagation of rays up to 2 collision with walls.
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMaker.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMaker.java
index 23fac5926..8f2d442b3 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMaker.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMaker.java
@@ -156,12 +156,22 @@ public void setProfilerThread(ProfilerThread profilerThread) {
this.profilerThread = profilerThread;
}
+ /**
+ * @param computeRaysOutFactory Factory to create output data handler for each cell
+ */
public void setComputeRaysOutFactory(IComputeRaysOutFactory computeRaysOutFactory) {
this.computeRaysOutFactory = computeRaysOutFactory;
}
/**
- * Do not call this method after {@link #initialize(Connection, ProgressVisitor)} has been called
+ * @return Factory to create an output data handler for each cell the default is {@link DefaultCutPlaneProcessing}
+ */
+ public IComputeRaysOutFactory getComputeRaysOutFactory() {
+ return computeRaysOutFactory;
+ }
+
+ /**
+ * Do not call this method after {@link #initialize(Connection)} has been called
* @param tableLoader Object that generate scene for each sub-cell using database data
*/
public void setPropagationProcessDataFactory(TableLoader tableLoader) {
@@ -175,10 +185,16 @@ public TableLoader getPropagationProcessDataFactory() {
return tableLoader;
}
+ /**
+ * @return Number of threads used for ray propagation, 0 means automatic detection of number of CPU cores
+ */
public int getThreadCount() {
return threadCount;
}
+ /**
+ * @param threadCount Number of threads used for ray propagation, 0 means automatic detection of number of CPU cores
+ */
public void setThreadCount(int threadCount) {
this.threadCount = threadCount;
}
@@ -189,7 +205,8 @@ public void setThreadCount(int threadCount) {
* @param cellIndex Computation area index
* @param skipReceivers Do not process the receivers primary keys in this set and once included add the new receivers primary in it
* @return Data input for cell evaluation
- * @throws SQLException
+ * @throws SQLException SQL exception instance
+ * @throws IOException IO exception instance
*/
public SceneWithEmission prepareCell(Connection connection, CellIndex cellIndex,
Set skipReceivers) throws SQLException, IOException {
@@ -211,10 +228,10 @@ public SceneWithEmission prepareCell(Connection connection, CellIndex cellIndex,
* Retrieves the computation envelope based on data stored in the database tables.
* @param connection the database connection.
* @return the computation envelope containing the bounding box of the data stored in the specified tables.
- * @throws SQLException
+ * @throws SQLException if an SQL exception occurs while retrieving the envelope.
*/
@Override
- protected Envelope getComputationEnvelope(Connection connection) throws SQLException {
+ public Envelope getComputationEnvelope(Connection connection) throws SQLException {
DBTypes dbTypes = DBUtils.getDBType(connection);
Envelope envelopeInternal = GeometryTableUtilities.getEnvelope(connection, TableLocation.parse(receiverTableName, dbTypes)).getEnvelopeInternal();
envelopeInternal.expandBy(maximumPropagationDistance);
@@ -223,9 +240,9 @@ protected Envelope getComputationEnvelope(Connection connection) throws SQLExcep
/**
* Fetch all receivers and compute cells that contains receivers
- * @param connection
+ * @param connection JDBC Connection
* @return Cell index with number of receivers
- * @throws SQLException
+ * @throws SQLException SQL exception instance
*/
public Map searchPopulatedCells(Connection connection) throws SQLException {
if(mainEnvelope == null) {
@@ -299,14 +316,6 @@ public CutPlaneVisitorFactory evaluateCell(Connection connection, CellIndex cell
computeRays.setThreadCount(threadCount);
}
- if(!receiverHasAbsoluteZCoordinates) {
- computeRays.makeReceiverRelativeZToAbsolute();
- }
-
- if(!sourceHasAbsoluteZCoordinates) {
- computeRays.makeSourceRelativeZToAbsolute();
- }
-
computeRays.run(computeRaysOut);
return computeRaysOut;
@@ -322,12 +331,11 @@ public TableLoader getTableLoader() {
/**
* Initializes the noise map computation process.
* @param connection Active connection
- * @param progression
- * @throws SQLException
+ * @throws SQLException if an SQL exception occurs during initialization.
*/
@Override
- public void initialize(Connection connection, ProgressVisitor progression) throws SQLException {
- super.initialize(connection, progression);
+ public void initialize(Connection connection) throws SQLException {
+ super.initialize(connection);
tableLoader.initialize(connection, this);
computeRaysOutFactory.initialize(connection, this);
}
@@ -336,7 +344,7 @@ public void initialize(Connection connection, ProgressVisitor progression) throw
* Run NoiseModelling with provided parameters, return when computation is done
*/
public void run(Connection connection, ProgressVisitor progressLogger) throws SQLException {
- initialize(connection, progressLogger);
+ initialize(connection);
// Set of already processed receivers
Set receivers = new HashSet<>();
@@ -351,6 +359,10 @@ public void run(Connection connection, ProgressVisitor progressLogger) throws SQ
// Run ray propagation
try {
evaluateCell(connection, cellIndex, progressVisitor, receivers);
+ if(progressLogger.isCanceled()) {
+ // Computation has been canceled, exit the loop
+ break;
+ }
} catch (IOException ex) {
throw new SQLException(ex);
}
@@ -400,7 +412,7 @@ public interface IComputeRaysOutFactory {
/**
* Called before the first sub cell is being computed
* @param progressLogger Main progression information, this method will not update the progression
- * @throws SQLException
+ * @throws SQLException If an SQL exception occurs
*/
void start(ProgressVisitor progressLogger) throws SQLException;
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapDatabaseParameters.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapDatabaseParameters.java
index 26db6284c..56e0ae60e 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapDatabaseParameters.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/NoiseMapDatabaseParameters.java
@@ -45,10 +45,6 @@ public NoiseMapDatabaseParameters() {
public enum ExportRaysMethods {TO_RAYS_TABLE, NONE}
public ExportRaysMethods exportRaysMethod = ExportRaysMethods.NONE;
- /** Cnossos revisions have multiple coefficients for road emission formulae
- * this parameter will be removed when the final version of Cnossos will be published
- */
- public int coefficientVersion = 2;
// Output config
@@ -115,17 +111,23 @@ public ExportRaysMethods getExportRaysMethod() {
/**
* Export rays in table (beware this could take a lot of storage space) or keep on memory or do not keep
- * @param exportRaysMethod
+ * @param exportRaysMethod Export rays method
*/
public void setExportRaysMethod(ExportRaysMethods exportRaysMethod) {
this.exportRaysMethod = exportRaysMethod;
}
+ /**
+ * @param exportCnossosPathWithAttenuation With attenuation export also the json of the related cnossos path, for debugging purpose
+ */
public void setExportCnossosPathWithAttenuation(boolean exportCnossosPathWithAttenuation) {
this.exportCnossosPathWithAttenuation = exportCnossosPathWithAttenuation;
}
+ /**
+ * @return With attenuation export also the json of the related cnossos path, for debugging purpose
+ */
public boolean isKeepAbsorption() {
return keepAbsorption;
}
@@ -138,18 +140,6 @@ public void setExportAttenuationMatrix(boolean exportAttenuationMatrix) {
this.exportAttenuationMatrix = exportAttenuationMatrix;
}
- /**
- * @param coefficientVersion Cnossos revisions have multiple coefficients for road emission formulae this parameter
- * will be removed when the final version of Cnossos will be published
- */
- public void setCoefficientVersion(int coefficientVersion) {
- this.coefficientVersion = coefficientVersion;
- }
-
- public int getCoefficientVersion() {
- return coefficientVersion;
- }
-
/**
* Maximum result stack to be inserted in database
* if the stack is full, the computation core is waiting
@@ -174,6 +164,10 @@ public void setMaximumError(double maximumError) {
this.maximumError = maximumError;
}
+ /**
+ * @param mergeSources If true all sources contributions are merged into a single noise level per receiver.
+ * The source identifier is loosed in the output tables.
+ */
public void setMergeSources(boolean mergeSources) {
this.mergeSources = mergeSources;
}
@@ -186,11 +180,16 @@ public String getRaysTable() {
}
/**
+ * @param raysTable Table name that contains rays dump (profile)
*/
public void setRaysTable(String raysTable) {
this.raysTable = raysTable;
}
+ /**
+ * @return If true all sources contributions are merged into a single noise level per receiver.
+ * The source identifier is loosed in the output tables.
+ */
public boolean isMergeSources() {
return mergeSources;
}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/DefaultTableLoader.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/DefaultTableLoader.java
index e431cd73e..3a60299ee 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/DefaultTableLoader.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/DefaultTableLoader.java
@@ -34,6 +34,7 @@
import java.sql.*;
import java.util.*;
+import java.util.stream.Collectors;
import static org.h2gis.utilities.GeometryTableUtilities.getGeometryColumnNames;
@@ -79,24 +80,42 @@ public void insertTrainDirectivity() {
* Initializes the NoiseMap parameters and attenuation data based on the input mode specified in the NoiseMap parameters.
* @param connection the database connection to be used for initialization.
* @param noiseMapByReceiverMaker the noise map by receiver maker object associated with the computation process.
- * @throws SQLException
+ * @throws SQLException if a database access error occurs during initialization.
*/
@Override
public void initialize(Connection connection, NoiseMapByReceiverMaker noiseMapByReceiverMaker) throws SQLException {
this.noiseMapByReceiverMaker = noiseMapByReceiverMaker;
+ DBTypes dbType = DBUtils.getDBType(connection);
SceneDatabaseInputSettings inputSettings = noiseMapByReceiverMaker.getSceneInputSettings();
if(inputSettings.inputMode == SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_GUESS) {
// Check fields to find appropriate expected data
- inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_ATTENUATION;
if(!inputSettings.sourcesEmissionTableName.isEmpty()) {
List sourceFields = JDBCUtilities.getColumnNames(connection, noiseMapByReceiverMaker.getSourcesEmissionTableName());
+ List periods = JDBCUtilities.getUniqueFieldValues(connection, inputSettings.sourcesEmissionTableName, TableLocation.capsIdentifier("period", dbType)).stream().map(String::toUpperCase).collect(Collectors.toList());
if(sourceFields.contains("LV_SPD")) {
- inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_TRAFFIC_FLOW;
+ if(periods.contains("D") && periods.contains("E") && periods.contains("N")) {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_TRAFFIC_FLOW_DEN;
+ } else {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_TRAFFIC_FLOW;
+ }
} else {
- inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW;
+ if(periods.contains("D") && periods.contains("E") && periods.contains("N")) {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW_DEN;
+ } else {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW;
+ }
}
} else {
- List sourceFields = JDBCUtilities.getColumnNames(connection, noiseMapByReceiverMaker.getSourcesTableName());
+ List sourceFields = JDBCUtilities.getColumnNames(connection, noiseMapByReceiverMaker.getSourcesTableName()).stream().map(String::toUpperCase).collect(Collectors.toList());
+ // Look for Emission/Traffic columns without periods
+ List frequencyValuesWithoutPeriod = readFrequenciesFromLwTable(
+ noiseMapByReceiverMaker.getFrequencyFieldPrepend(), sourceFields);
+ if(!frequencyValuesWithoutPeriod.isEmpty()) {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW;
+ } else if (sourceFields.contains("LV_SPD")) {
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_TRAFFIC_FLOW;
+ }
+ // Look for Emission/Traffic columns for each period
for (EmissionTableGenerator.STANDARD_PERIOD period : EmissionTableGenerator.STANDARD_PERIOD.values()) {
String periodFieldName = EmissionTableGenerator.STANDARD_PERIOD_VALUE[period.ordinal()];
List frequencyValues = readFrequenciesFromLwTable(
@@ -114,11 +133,17 @@ public void initialize(Connection connection, NoiseMapByReceiverMaker noiseMapBy
}
}
}
-
+ if(inputSettings.inputMode == SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_GUESS) {
+ // By default, we compute the attenuation
+ inputSettings.inputMode = SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_ATTENUATION;
+ }
if(inputSettings.inputMode == SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW) {
// Load expected frequencies used for computation
// Fetch source fields
- List sourceField = JDBCUtilities.getColumnNames(connection, noiseMapByReceiverMaker.getSourcesEmissionTableName());
+ List sourceField = JDBCUtilities.getColumnNames(connection,
+ noiseMapByReceiverMaker.getSourcesEmissionTableName().isEmpty() ?
+ noiseMapByReceiverMaker.getSourcesTableName() :
+ noiseMapByReceiverMaker.getSourcesEmissionTableName());
List frequencyValues = readFrequenciesFromLwTable(noiseMapByReceiverMaker.getFrequencyFieldPrepend(), sourceField);
if(frequencyValues.isEmpty()) {
throw new SQLException("Source emission table "+ noiseMapByReceiverMaker.getSourcesTableName()+" does not contains any frequency bands");
@@ -278,6 +303,8 @@ public SceneWithEmission create(Connection connection, CellIndex cellIndex,
scene.setBodyBarrier(noiseMapByReceiverMaker.isBodyBarrier());
scene.maxRefDist = maximumReflectionDistance;
scene.maxSrcDist = maximumPropagationDistance;
+ scene.setCloseReceiverReflectionWallDistance(noiseMapByReceiverMaker.getCloseReceiverReflectionWallDistance());
+ scene.lineSourceSpacingRatio = noiseMapByReceiverMaker.getSceneInputSettings().getLineSourceSpacingRatio();
scene.setComputeVerticalDiffraction(noiseMapByReceiverMaker.isComputeVerticalDiffraction());
scene.setComputeHorizontalDiffraction(noiseMapByReceiverMaker.isComputeHorizontalDiffraction());
@@ -316,6 +343,9 @@ public SceneWithEmission create(Connection connection, CellIndex cellIndex,
" contain at least one receiver without Z ordinate." +
" You must specify X,Y,Z for each receiver");
}
+ if(!noiseMapByReceiverMaker.isReceiverHasAbsoluteZCoordinates()) {
+ pt = scene.profileBuilder.makeGeometryRelativeZToAbsolute(pt, true);
+ }
scene.addReceiver(receiverPk, pt.getCoordinate(), rs);
}
}
@@ -335,7 +365,7 @@ public SceneWithEmission create(Connection connection, CellIndex cellIndex,
* @param tableName Table name
* @param defaultInterpolation Interpolation if applicable
* @param frequencyFieldPrepend Frequency field name ex. HZ for HZ1000
- * @return
+ * @return Map of directivity spheres
*/
public static Map fetchDirectivity(Connection connection, String tableName, int defaultInterpolation, String frequencyFieldPrepend) throws SQLException {
Map directionAttributes = new HashMap<>();
@@ -449,7 +479,7 @@ public static void fetchCellBuildings(Connection connection,
String additionalQuery = "";
DBTypes dbType = DBUtils.getDBType(connection.unwrap(Connection.class));
if(!buildingTableParameters.heightField.isEmpty()) {
- additionalQuery += ", " + TableLocation.quoteIdentifier(buildingTableParameters.heightField, dbType);
+ additionalQuery += ", " + TableLocation.capsIdentifier(buildingTableParameters.heightField, dbType);
}
if(fetchAlpha) {
additionalQuery += ", " + buildingTableParameters.alphaFieldName;
@@ -701,6 +731,8 @@ protected void fetchCellSoilAreas(Connection connection, Envelope fetchEnvelope,
*/
public void fetchCellSource(Connection connection, Envelope fetchEnvelope, SceneWithEmission scene, boolean doIntersection)
throws SQLException {
+ Map sourceEmissionFieldsCache = new HashMap<>();
+ Map sourceFieldNames = new HashMap<>();
String sourcesTableName = noiseMapByReceiverMaker.getSourcesTableName();
GeometryFactory geometryFactory = noiseMapByReceiverMaker.getGeometryFactory();
DBTypes dbType = DBUtils.getDBType(connection.unwrap(Connection.class));
@@ -727,6 +759,7 @@ public void fetchCellSource(Connection connection, Envelope fetchEnvelope, Scene
}
st.setFetchDirection(ResultSet.FETCH_FORWARD);
try (SpatialResultSet rs = st.executeQuery().unwrap(SpatialResultSet.class)) {
+ EmissionTableGenerator.cacheFields(sourceFieldNames, rs);
while (rs.next()) {
Geometry geo = rs.getGeometry();
if (geo != null) {
@@ -737,13 +770,20 @@ public void fetchCellSource(Connection connection, Envelope fetchEnvelope, Scene
Coordinate[] coordinates = geo.getCoordinates();
for (Coordinate coordinate : coordinates) {
// check z value
- if (coordinate.getZ() == Coordinate.NULL_ORDINATE) {
+ if (Double.isNaN(coordinate.getZ())) {
throw new IllegalArgumentException("The table " + sourcesTableName +
" contain at least one source without Z ordinate." +
" You must specify X,Y,Z for each source");
}
}
- scene.addSource(rs.getLong(pkIndex), geo, rs);
+ if(!noiseMapByReceiverMaker.isSourceHasAbsoluteZCoordinates()) {
+ if(scene.profileBuilder.hasDem()) {
+ // Coordinates are supposed to be relative to the digital elevation model
+ // So we must compute the altitude values
+ geo = scene.profileBuilder.makeGeometryRelativeZToAbsolute(geo, true);
+ }
+ }
+ scene.addSource(rs.getLong(pkIndex), geo, rs, sourceFieldNames);
}
}
}
@@ -768,8 +808,9 @@ public void fetchCellSource(Connection connection, Envelope fetchEnvelope, Scene
}
st.setFetchDirection(ResultSet.FETCH_FORWARD);
try (ResultSet rs = st.executeQuery()) {
+ EmissionTableGenerator.cacheFields(sourceEmissionFieldsCache, rs);
while (rs.next()) {
- scene.addSourceEmission(rs.getLong(scene.sceneDatabaseInputSettings.sourceEmissionPrimaryKeyField), rs);
+ scene.addSourceEmission(rs.getLong(scene.sceneDatabaseInputSettings.sourceEmissionPrimaryKeyField), rs, sourceEmissionFieldsCache);
}
} finally {
if (autoCommit) {
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneDatabaseInputSettings.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneDatabaseInputSettings.java
index de1264aef..83e15afe3 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneDatabaseInputSettings.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneDatabaseInputSettings.java
@@ -45,6 +45,12 @@ public enum INPUT_MODE {
String periodAtmosphericSettingsTableName = "";
/** Cnossos coefficient version (1 = 2015, 2 = 2020) */
int coefficientVersion = 2;
+ /**
+ * dictates the density of source points created from a line sound source;
+ * a higher value means more points and finer discretization
+ * sourcePointDistance = DistanceSourceReceiver / lineSourceSpacingRatio
+ */
+ public double lineSourceSpacingRatio = 2;
public String frequencyFieldPrepend = "HZ";
public SceneDatabaseInputSettings() {
@@ -132,4 +138,21 @@ public String getFrequencyFieldPrepend() {
public void setFrequencyFieldPrepend(String frequencyFieldPrepend) {
this.frequencyFieldPrepend = frequencyFieldPrepend;
}
+
+ /**
+ * @return dictates the density of source points created from a line sound source;
+ * a higher value means more points and finer discretization
+ * sourcePointDistance = DistanceSourceReceiver / lineSourceSpacingRatio
+ */
+ public double getLineSourceSpacingRatio() {
+ return lineSourceSpacingRatio;
+ }
+
+ /**
+ * dictates the density of source points created from a line sound source;
+ * @param lineSourceSpacingRatio a higher value means more points and finer discretization
+ */
+ public void setLineSourceSpacingRatio(double lineSourceSpacingRatio) {
+ this.lineSourceSpacingRatio = lineSourceSpacingRatio;
+ }
}
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneWithEmission.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneWithEmission.java
index 8645ca756..51cd697f8 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneWithEmission.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/input/SceneWithEmission.java
@@ -26,9 +26,6 @@
* This is input data, not thread safe, never update anything here during propagation
*/
public class SceneWithEmission extends SceneWithAttenuation {
- /** Old style DEN columns traffic period */
- Map sourceEmissionFieldsCache = new HashMap<>();
-
// For each source primary key give the map between period and source power spectrum values
public Map> wjSources = new HashMap<>();
@@ -46,7 +43,7 @@ public SceneWithEmission(ProfileBuilder profileBuilder) {
public SceneWithEmission() {
}
- public void processTrafficFlowDEN(Long pk, SpatialResultSet rs) throws SQLException {
+ public void processTrafficFlowDEN(Long pk, SpatialResultSet rs, Map sourceFieldNames) throws SQLException {
// Source table PK, GEOM, LV_D, LV_E, LV_N ...
double[][] lw = EmissionTableGenerator.computeLw(rs, sceneDatabaseInputSettings.coefficientVersion, sourceFieldNames);
// Will generate D E N emission
@@ -60,11 +57,11 @@ public void processTrafficFlowDEN(Long pk, SpatialResultSet rs) throws SQLExcept
* @param rs Emission source table IDSOURCE, PERIOD, LV, HV ..
* @throws SQLException
*/
- public void processTrafficFlow(Long pk, ResultSet rs) throws SQLException {
+ public void processTrafficFlow(Long pk, ResultSet rs, Map sourceFieldNames) throws SQLException {
String period = rs.getString("PERIOD");
// Use geometry as default slope (if field slope is not provided
double defaultSlope = 0;
- if(!sourceEmissionFieldsCache.containsKey("SLOPE")) {
+ if(!sourceFieldNames.containsKey("SLOPE")) {
int sourceIndex = sourcesPk.indexOf(pk);
if(sourceIndex >= 0) {
defaultSlope = EmissionTableGenerator.getSlope(sourceGeometries.get(sourceIndex));
@@ -73,7 +70,7 @@ public void processTrafficFlow(Long pk, ResultSet rs) throws SQLException {
double[] lw = AcousticIndicatorsFunctions.dBToW(
EmissionTableGenerator.getEmissionFromTrafficTable(rs, "",
defaultSlope,
- sceneDatabaseInputSettings.coefficientVersion, sourceEmissionFieldsCache));
+ sceneDatabaseInputSettings.coefficientVersion, sourceFieldNames));
addSourceEmission(pk, period, lw);
}
@@ -82,31 +79,45 @@ public void processTrafficFlow(Long pk, ResultSet rs) throws SQLException {
* @param rs Emission source table IDSOURCE, PERIOD, LV, HV ..
* @throws SQLException
*/
- public void processEmission(Long pk, ResultSet rs) throws SQLException {
+ public void processEmission(Long pk, ResultSet rs, Map sourceFieldNames) throws SQLException {
double[] lw = new double[profileBuilder.frequencyArray.size()];
List frequencyArray = profileBuilder.frequencyArray;
for (int i = 0, frequencyArraySize = frequencyArray.size(); i < frequencyArraySize; i++) {
Integer frequency = frequencyArray.get(i);
- lw[i] = AcousticIndicatorsFunctions.dBToW(rs.getDouble(sceneDatabaseInputSettings.frequencyFieldPrepend +frequency));
+ String fieldName = sceneDatabaseInputSettings.frequencyFieldPrepend+frequency;
+ int fieldIndex = sourceFieldNames.getOrDefault(fieldName, 0);
+ if(fieldIndex <= 0) {
+ // No emission value here for this frequency, skip the extraction of noise source level
+ // It could be provided later from another table
+ return;
+ }
+ lw[i] = AcousticIndicatorsFunctions.dBToW(rs.getDouble(fieldIndex));
+ }
+ // Check if period field exists use empty string otherwise
+ if(!sourceFieldNames.containsKey("PERIOD")) {
+ addSourceEmission(pk, "", lw);
+ } else {
+ addSourceEmission(pk, Objects.toString(rs.getString("PERIOD"), ""), lw);
}
- String period = rs.getString("PERIOD");
- addSourceEmission(pk, period, lw);
}
@Override
- public void addSource(Long pk, Geometry geom, SpatialResultSet rs) throws SQLException {
- super.addSource(pk, geom, rs);
+ public void addSource(Long pk, Geometry geom, SpatialResultSet rs, Map sourceFieldNames) throws SQLException {
+ super.addSource(pk, geom, rs, sourceFieldNames);
switch (Objects.requireNonNull(sceneDatabaseInputSettings.inputMode)) {
case INPUT_MODE_TRAFFIC_FLOW_DEN:
- processTrafficFlowDEN(pk, rs);
+ processTrafficFlowDEN(pk, rs, sourceFieldNames);
break;
case INPUT_MODE_LW_DEN:
- processEmissionDEN(pk, rs);
+ processEmissionDEN(pk, rs, sourceFieldNames);
+ break;
+ case INPUT_MODE_LW:
+ processEmission(pk, rs, sourceFieldNames);
break;
}
}
- private void processEmissionDEN(Long pk, SpatialResultSet rs) throws SQLException {
+ private void processEmissionDEN(Long pk, SpatialResultSet rs, Map sourceFieldNames) throws SQLException {
List frequencyArray = profileBuilder.frequencyArray;
for (EmissionTableGenerator.STANDARD_PERIOD period : EmissionTableGenerator.STANDARD_PERIOD.values()) {
double[] lw = new double[profileBuilder.frequencyArray.size()];
@@ -129,13 +140,15 @@ private void processEmissionDEN(Long pk, SpatialResultSet rs) throws SQLExceptio
}
}
- public void addSourceEmission(Long pk, ResultSet rs) throws SQLException {
+ public void addSourceEmission(Long pk, ResultSet rs, Map sourceFieldNames) throws SQLException {
switch (sceneDatabaseInputSettings.inputMode) {
+ case INPUT_MODE_TRAFFIC_FLOW_DEN:
case INPUT_MODE_TRAFFIC_FLOW:
- processTrafficFlow(pk, rs);
+ processTrafficFlow(pk, rs, sourceFieldNames);
break;
+ case INPUT_MODE_LW_DEN:
case INPUT_MODE_LW:
- processEmission(pk, rs);
+ processEmission(pk, rs, sourceFieldNames);
break;
}
}
@@ -163,7 +176,6 @@ public void addSourceEmission(Long sourcePrimaryKey, String period, double[] wj)
@Override
public void clearSources() {
super.clearSources();
- sourceEmissionFieldsCache.clear();
wjSources.clear();
}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/AttenuationOutputSingleThread.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/AttenuationOutputSingleThread.java
index ce80dba22..3268c03fa 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/AttenuationOutputSingleThread.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/AttenuationOutputSingleThread.java
@@ -22,15 +22,12 @@
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutProfile;
import org.noise_planet.noisemodelling.propagation.AttenuationParameters;
import org.noise_planet.noisemodelling.propagation.ReceiverNoiseLevel;
-import org.noise_planet.noisemodelling.propagation.cnossos.AttenuationCnossos;
-import org.noise_planet.noisemodelling.propagation.cnossos.CnossosPath;
+import org.noise_planet.noisemodelling.propagation.cnossos.*;
import org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions;
-import org.noise_planet.noisemodelling.propagation.cnossos.CnossosPathBuilder;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.DoubleStream;
import static org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions.*;
import static org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions.wToDb;
@@ -60,10 +57,10 @@ public class AttenuationOutputSingleThread implements CutPlaneVisitor {
/**
* MaxError DB Processing variable
* Favourable Free Field cumulated global power at receiver, only used to stop looking for far sources
- * Key source index
+ * Key unique source point identifier
* Value maximum expected noise level in w
*/
- Map> maximumWjExpectedSplAtReceiver = new HashMap<>();
+ Map> maximumWjExpectedSplAtReceiver = new HashMap<>();
public AtomicInteger cutProfileCount = new AtomicInteger(0);
@@ -81,22 +78,13 @@ public AttenuationOutputSingleThread(AttenuationOutputMultiThread multiThreadPar
}
/**
- *
- * @param sourceInfo
- * @param receiverInfo
- * @param cnossosParameters
- * @return Attenuation in dB
+ * The maximumError shortcut stops the path finder before all farther sources are visited.
+ * When rays are explicitly exported, users expect the rays table to describe the complete
+ * propagation search, so the shortcut must stay disabled for that diagnostic output.
*/
- private static double[] computeFastAttenuation(PathFinder.SourcePointInfo sourceInfo,
- PathFinder.ReceiverPointInfo receiverInfo, AttenuationParameters cnossosParameters) {
- // For the quick attenuation evaluation
- // only take account of geometric dispersion and atmospheric attenuation
- double distance = Math.max(1.0, sourceInfo.position.distance3D(receiverInfo.position));
- // 3 dB gain as we consider source G path is equal to 0
- double attenuationDivGeom = AttenuationCnossos.getADiv(distance) - 3;
- return AcousticIndicatorsFunctions.multiplicationArray(AcousticIndicatorsFunctions.sumArray(
- AttenuationCnossos.aAtm(cnossosParameters.getAlpha_atmo(), distance),
- attenuationDivGeom), -1);
+ private boolean isMaximumErrorPruningEnabled() {
+ return dbSettings.maximumError > 0 &&
+ dbSettings.getExportRaysMethod() == NoiseMapDatabaseParameters.ExportRaysMethods.NONE;
}
private double[] processAndStoreAttenuation(AttenuationParameters data, CnossosPath proPathParameters, String period) {
@@ -133,6 +121,10 @@ public PathSearchStrategy onNewCutPlane(CutProfile cutProfile) {
cutProfileCount.addAndGet(1);
PathSearchStrategy strategy = PathSearchStrategy.CONTINUE;
final SceneWithEmission scene = multiThread.sceneWithEmission;
+ if(scene.getCloseReceiverReflectionWallDistance() > 0
+ && cutProfile.hasCloseReflectionBeforeReceiver(scene.getCloseReceiverReflectionWallDistance())) {
+ return strategy;
+ }
List cnossosPaths = CnossosPathBuilder.computeCnossosPathsFromCutProfile(cutProfile, scene.isBodyBarrier(),
scene.profileBuilder.exactFrequencyArray, scene.defaultGroundAttenuation);
for (CnossosPath cnossosPath : cnossosPaths) {
@@ -195,24 +187,27 @@ public PathSearchStrategy onNewCutPlane(CutProfile cutProfile) {
new ReceiverNoiseLevel(new PathFinder.SourcePointInfo(source),
new PathFinder.ReceiverPointInfo(receiver), period, levels);
processNoiseLevel(receiverNoiseLevel);
- if(dbSettings.maximumError > 0) {
+ if(isMaximumErrorPruningEnabled()) {
double powerSum = sumArray(levels);
wjAtReceiver.merge(period, powerSum, Double::sum);
}
}
}
}
- if(dbSettings.maximumError > 0 && scene.wjSources.containsKey(sourcePk)) {
+ // To reduce the computation time, we evaluate the potential remaining power
+ // at the receiver and stop processing further sources if we are already close enough to
+ // the expected final level at the receiver (if maximumError is defined in dbSettings)
+ if(isMaximumErrorPruningEnabled() && scene.wjSources.containsKey(sourcePk)) {
boolean keepRunning = false;
- // update remaining expected max power for each source periods
+ // Update remaining expected max power for each source period.
+ // We remove the currently processed source point from the precomputed budget.
ArrayList emissions = scene.wjSources.get(sourcePk);
+ SourcePointKey sourcePointKey = new SourcePointKey(source);
for (SceneWithEmission.PeriodEmission periodEmission : emissions) {
final String period = periodEmission.period;
- // replace unknown value (evaluated on startReceiver) of expected power for this source point
if (maximumWjExpectedSplAtReceiver.containsKey(period)) {
- maximumWjExpectedSplAtReceiver.get(period).remove(source.coordinate);
+ maximumWjExpectedSplAtReceiver.get(period).remove(sourcePointKey);
if (maximumWjExpectedSplAtReceiver.get(period).isEmpty()) {
- // no remaining power at this period
maximumWjExpectedSplAtReceiver.remove(period);
}
}
@@ -222,19 +217,18 @@ public PathSearchStrategy onNewCutPlane(CutProfile cutProfile) {
final double levelAtReceiver = entry.getValue();
if(!maximumWjExpectedSplAtReceiver.containsKey(period)) {
- // nothing to evaluate here, as there is no expected further power for this period
+ // Nothing to evaluate here, as there is no expected further power for this period.
continue;
}
// Evaluate the current noise level at receiver compared to the final
- // expected noise level at the receiver
+ // expected noise level at the receiver.
double nonProcessedPower = maximumWjExpectedSplAtReceiver.get(period).values().stream()
.reduce(Double::sum).orElse(0.0);
- double maximumExpectedLevelInDb = AcousticIndicatorsFunctions.wToDb(levelAtReceiver
- + nonProcessedPower);
+ double maximumExpectedLevelInDb = AcousticIndicatorsFunctions.wToDb(levelAtReceiver + nonProcessedPower);
double dBDiff = maximumExpectedLevelInDb - wToDb(levelAtReceiver);
if (dBDiff > dbSettings.maximumError) {
- // For this period we expect to see some significant sources further away
+ // For this period we still expect to see some significant sources further away.
keepRunning = true;
break;
}
@@ -252,7 +246,7 @@ public void startReceiver(PathFinder.ReceiverPointInfo receiver, Collection 0 && !multiThread.sceneWithEmission.wjSources.isEmpty()) {
+ if(isMaximumErrorPruningEnabled() && !multiThread.sceneWithEmission.wjSources.isEmpty()) {
wjAtReceiver = new HashMap<>(multiThread.sceneWithEmission.periodSet.size());
for (String period : multiThread.sceneWithEmission.periodSet) {
wjAtReceiver.put(period, 0.0);
@@ -261,20 +255,45 @@ public void startReceiver(PathFinder.ReceiverPointInfo receiver, Collection());
+ List pts2D = cutProfile.computePts2D();
+ cnossosPath.setSRSegment(CnossosPathBuilder.computeSegment(pts2D.get(0), pts2D.get(1), new double[] {0, 0}));
+ cnossosPath.getPointList().add(new PointPath(pts2D.get(0), 0, PointPath.POINT_TYPE.SRCE));
+ cnossosPath.getPointList().add(new PointPath(pts2D.get(1), 0, PointPath.POINT_TYPE.RECV));
+ double[] attenuation = dBToW(AttenuationCnossos.computeCnossosAttenuation(scene.defaultCnossosParameters, cnossosPath, scene, false));
+ // For line source apply a gain on the attenuation
+ if(sourcePointInfo.li > 1) {
+ attenuation = multiplicationArray(attenuation, sourcePointInfo.li);
+ }
if(scene.wjSources.containsKey(sourcePointInfo.sourcePk)) {
ArrayList emissions = scene.wjSources.get(sourcePointInfo.sourcePk);
for (SceneWithEmission.PeriodEmission periodEmission : emissions) {
- double[] wjAtReceiver = multiplicationArray(attenuation, periodEmission.emission);
+ // Use period-specific attenuation settings when available so the remaining-power
+ // budget matches the actual period being evaluated by the maxError algorithm.
+ AttenuationParameters parameters = scene.cnossosParametersPerPeriod.getOrDefault(
+ periodEmission.period, scene.defaultCnossosParameters);
+ double[] attenuationPerPeriod = attenuation;
+ if(parameters != scene.defaultCnossosParameters) {
+ attenuationPerPeriod = dBToW(AttenuationCnossos.computeCnossosAttenuation(parameters,
+ cnossosPath, scene, false));
+ if(sourcePointInfo.li > 1) {
+ attenuationPerPeriod = multiplicationArray(attenuationPerPeriod, sourcePointInfo.li);
+ }
+ }
+ double[] wjAtReceiver = multiplicationArray(attenuationPerPeriod, periodEmission.emission);
double sumPower = sumArray(wjAtReceiver);
- HashMap sourceLevel;
+ HashMap sourceLevel;
if(!maximumWjExpectedSplAtReceiver.containsKey(periodEmission.period)) {
sourceLevel = new HashMap<>();
maximumWjExpectedSplAtReceiver.put(periodEmission.period, sourceLevel);
} else {
sourceLevel = maximumWjExpectedSplAtReceiver.get(periodEmission.period);
}
- sourceLevel.merge(sourcePointInfo.getCoord(), sumPower, Double::sum);
+ sourceLevel.merge(new SourcePointKey(sourcePointInfo), sumPower, Double::sum);
}
}
}
@@ -397,6 +416,7 @@ public void finalizeReceiver(PathFinder.ReceiverPointInfo receiver) {
}
}
if (dbSettings.isMergeSources()) {
+ // If Merge source is activated, the following code will push empty values (-99dB) when no rays have reached the receiver
Set difference = new HashSet<>(multiThread.sceneWithEmission.periodSet);
if(computeLden) {
difference.add(EmissionTableGenerator.DEN_PERIOD);
@@ -462,4 +482,5 @@ public TimePeriodParameters update(TimePeriodParameters other) {
return this;
}
}
+
}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/SourcePointKey.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/SourcePointKey.java
new file mode 100644
index 000000000..61b400fdc
--- /dev/null
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/output/SourcePointKey.java
@@ -0,0 +1,59 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+
+package org.noise_planet.noisemodelling.jdbc.output;
+
+import org.noise_planet.noisemodelling.pathfinder.PathFinder;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointSource;
+
+import java.util.Objects;
+
+/**
+ * Unique identifier for one discretized source point in the maxError bookkeeping.
+ * We cannot use the coordinate alone because distinct source points may legitimately
+ * share the same position while belonging to different source identifiers.
+ * A line source will create multiple source points with the same primary key and index but with different coordinates.
+ */
+final class SourcePointKey {
+ final long sourcePk;
+ final int sourceIndex;
+ final long x;
+ final long y;
+ final long z;
+
+ SourcePointKey(PathFinder.SourcePointInfo sourcePointInfo) {
+ this.sourcePk = sourcePointInfo.sourcePk;
+ this.sourceIndex = sourcePointInfo.sourceIndex;
+ this.x = Double.doubleToLongBits(sourcePointInfo.position.x);
+ this.y = Double.doubleToLongBits(sourcePointInfo.position.y);
+ this.z = Double.doubleToLongBits(sourcePointInfo.position.z);
+ }
+
+ SourcePointKey(CutPointSource sourcePoint) {
+ this.sourcePk = sourcePoint.sourcePk;
+ this.sourceIndex = sourcePoint.id;
+ this.x = Double.doubleToLongBits(sourcePoint.coordinate.x);
+ this.y = Double.doubleToLongBits(sourcePoint.coordinate.y);
+ this.z = Double.doubleToLongBits(sourcePoint.coordinate.z);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SourcePointKey)) return false;
+ SourcePointKey that = (SourcePointKey) o;
+ return sourcePk == that.sourcePk && sourceIndex == that.sourceIndex &&
+ x == that.x && y == that.y && z == that.z;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(sourcePk, sourceIndex, x, y, z);
+ }
+}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/railway/RailWayLWIterator.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/railway/RailWayLWIterator.java
index 52e3011ad..16ba435c8 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/railway/RailWayLWIterator.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/railway/RailWayLWIterator.java
@@ -30,6 +30,9 @@
public class RailWayLWIterator implements Iterator {
+ public static final String RAILWAY_VEHICLES_CNOSSOS_JSON = "RailwayVehiclesCnossos.json";
+ public static final String RAILWAY_TRAINSETS_JSON = "RailwayTrainsets.json";
+ public static final String RAILWAY_EMISSION_CNOSSOS_JSON = "RailwayEmissionCnossos.json";
private RailwayCnossos railway = new RailwayCnossos();
private Connection connection;
private RailWayLWGeom railWayLWComplete = null;
@@ -46,27 +49,33 @@ public class RailWayLWIterator implements Iterator {
* @param tableTrackGeometry Track geometry and metadata
* @param tableTrainTraffic Train traffic associated with tracks
*/
- public RailWayLWIterator(Connection connection, String tableTrackGeometry, String tableTrainTraffic) {
- this.railway.setVehicleDataFile("RailwayVehiclesCnossos.json");
- this.railway.setTrainSetDataFile("RailwayTrainsets.json");
- this.railway.setRailwayDataFile("RailwayCnossosSNCF_2021.json");
+ public RailWayLWIterator(Connection connection, String tableTrackGeometry, String tableTrainTraffic) throws IOException {
+ this.railway.setVehicleDataFile(RAILWAY_VEHICLES_CNOSSOS_JSON);
+ this.railway.setTrainSetDataFile(RAILWAY_TRAINSETS_JSON);
+ this.railway.setRailwayDataFile(RAILWAY_EMISSION_CNOSSOS_JSON);
this.connection = connection;
this.tableTrackGeometry = tableTrackGeometry;
this.tableTrainTraffic = tableTrainTraffic;
railWayLWComplete = fetchNext(railWayLWIncomplete);
}
-
/**
* Generate sound source for train (with train source directivity) from traffic and geometry tracks tables
- * @param connection
- * @param tableTrackGeometry Track geometry and metadata
- * @param tableTrainTraffic Train traffic associated with tracks
+ * Constructs a RailWayLWIterator instance to process railway data by reading from a database connection
+ * and associated data files. Initializes the railway metadata and fetches the first incomplete railway geometry.
+ *
+ * @param connection Database connection used to fetch railway and traffic data.
+ * @param tableTrackGeometry Table name containing track geometry and metadata.
+ * @param tableTrainTraffic Table name containing train traffic details associated with tracks.
+ * @param vehicleDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for vehicle data configuration.
+ * @param trainSetDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for train set data configuration.
+ * @param railwayEmissionDataFile File path Url or resource filename (from org.noise_planet.noisemodelling.emission.railway package) for railway metadata configuration.
+ * @throws IOException If an error occurs during file reading or parsing the specified data files.
*/
- public RailWayLWIterator(Connection connection, String tableTrackGeometry, String tableTrainTraffic, String vehicleDataFile, String trainSetDataFile, String railwayDataFile) {
+ public RailWayLWIterator(Connection connection, String tableTrackGeometry, String tableTrainTraffic, String vehicleDataFile, String trainSetDataFile, String railwayEmissionDataFile) throws IOException {
this.railway.setVehicleDataFile(vehicleDataFile);
this.railway.setTrainSetDataFile(trainSetDataFile);
- this.railway.setRailwayDataFile(railwayDataFile);
+ this.railway.setRailwayDataFile(railwayEmissionDataFile);
this.connection = connection;
this.tableTrackGeometry = tableTrackGeometry;
this.tableTrainTraffic = tableTrainTraffic;
@@ -227,11 +236,11 @@ public RailWayCnossosParameters getRailwayEmissionFromResultSet(ResultSet rs, St
double vehiclePerHour = 1;
int rollingCondition = 0;
double idlingTime = 0;
- int trackTransfer = 4;
- int impactNoise = 0;
- int bridgeTransfert = 0;
+ String trackTransferStr = "SNCF4";
+ String impactNoiseStr = "";
+ String bridgeTransfertStr = "";
int curvature = 0;
- int railRoughness = 1;
+ String railRoughnessStr = "SNCF1";
int nbTrack = 2;
double vMaxInfra = 160;
double commercialSpeed = 160;
@@ -252,17 +261,19 @@ public RailWayCnossosParameters getRailwayEmissionFromResultSet(ResultSet rs, St
idlingTime = rs.getDouble("IDLINGTIME");
}
if (sourceFields.containsKey("TRANSFER")) {
- trackTransfer = rs.getInt("TRANSFER");
+ trackTransferStr = rs.getString("TRANSFER");
}
if (sourceFields.containsKey("ROUGHNESS")) {
- railRoughness = rs.getInt("ROUGHNESS");
+ railRoughnessStr = rs.getString("ROUGHNESS");
}
if (sourceFields.containsKey("IMPACT")) {
- impactNoise = rs.getInt("IMPACT");
+ String val = rs.getString("IMPACT");
+ impactNoiseStr = val != null ? val : "";
}
if (sourceFields.containsKey("BRIDGE")) {
- bridgeTransfert = rs.getInt("BRIDGE");
+ String val = rs.getString("BRIDGE");
+ bridgeTransfertStr = val != null ? val : "";
}
if (sourceFields.containsKey("CURVATURE")) {
curvature = rs.getInt("CURVATURE");
@@ -299,8 +310,8 @@ public RailWayCnossosParameters getRailwayEmissionFromResultSet(ResultSet rs, St
RailWayCnossosParameters lWRailWay = new RailWayCnossosParameters();
- RailwayTrackCnossosParameters trackParameters = new RailwayTrackCnossosParameters(vMaxInfra, trackTransfer, railRoughness,
- impactNoise, bridgeTransfert, curvature, commercialSpeed, isTunnel, nbTrack);
+ RailwayTrackCnossosParameters trackParameters = new RailwayTrackCnossosParameters(vMaxInfra, trackTransferStr, railRoughnessStr,
+ impactNoiseStr, bridgeTransfertStr, curvature, commercialSpeed, isTunnel, nbTrack);
Map vehicles = railway.getVehicleFromTrainset(train);
// double vehiclePerHouri=vehiclePerHour;
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/AscReaderDriver.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/AscReaderDriver.java
index 9aac4ef2b..ad0133f74 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/AscReaderDriver.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/AscReaderDriver.java
@@ -230,8 +230,9 @@ public String[] read(Connection connection, File fileName, ProgressVisitor progr
stmt.execute("DROP TABLE IF EXISTS " + outputTableName);
stmt.close();
}
- FileInputStream fis = new FileInputStream(fileName);
- outputTableName = readAsc(connection, new GZIPInputStream(fis), progress, outputTableName, srid);
+ try (FileInputStream fis = new FileInputStream(fileName)) {
+ outputTableName = readAsc(connection, new GZIPInputStream(fis), progress, outputTableName, srid);
+ }
return new String[]{outputTableName};
} else {
throw new SQLException("The asc read driver supports only asc or gz extensions");
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/DataBaseUtilities.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/DataBaseUtilities.java
new file mode 100644
index 000000000..c4330235f
--- /dev/null
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/DataBaseUtilities.java
@@ -0,0 +1,59 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+package org.noise_planet.noisemodelling.jdbc.utils;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+public class DataBaseUtilities {
+ /**
+ * Checks if the given SRID uses meters as its unit by querying the SPATIAL_REF_SYS table.
+ *
+ * @param connection A live JDBC connection
+ * @param srid The SRID to check
+ * @return true if the units are metric (meters)
+ */
+ public static boolean isSridMetric(Connection connection, int srid) throws SQLException {
+ String sql = "SELECT PROJ4TEXT FROM SPATIAL_REF_SYS WHERE SRID = ?";
+
+ try (PreparedStatement ps = connection.prepareStatement(sql)) {
+ ps.setInt(1, srid);
+
+ try (ResultSet rs = ps.executeQuery()) {
+ if (rs.next()) {
+ String proj = rs.getString("PROJ4TEXT");
+ if (proj == null || proj.isEmpty()) {
+ return false;
+ }
+
+ // Normalize to lowercase for reliable matching
+ proj = proj.toLowerCase();
+
+ /*
+ * Logic for Proj4 metric detection:
+ * 1. Check for "+units=m".
+ * We ensure it's not "+units=mm" (millimeters).
+ * 2. Check for "+to_meter=1" or "+to_meter=1.0".
+ */
+ boolean hasMeterUnit = proj.contains("+units=m") && !proj.contains("+units=mm");
+ boolean hasMeterFactor = proj.contains("+to_meter=1") || proj.contains("+to_meter=1.0");
+
+ return hasMeterUnit || hasMeterFactor;
+ }
+ }
+ } catch (SQLException e) {
+ // Log the exception according to your project's logging policy
+ System.err.println("Error querying SPATIAL_REF_SYS: " + e.getMessage());
+ }
+
+ return false;
+ }
+}
diff --git a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/IsoSurface.java b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/IsoSurface.java
index 556fd33f8..35159cf27 100644
--- a/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/IsoSurface.java
+++ b/noisemodelling-jdbc/src/main/java/org/noise_planet/noisemodelling/jdbc/utils/IsoSurface.java
@@ -9,11 +9,12 @@
package org.noise_planet.noisemodelling.jdbc.utils;
+import org.h2gis.api.EmptyProgressVisitor;
+import org.h2gis.api.ProgressVisitor;
import org.h2gis.functions.spatial.convert.ST_Force2D;
import org.h2gis.functions.spatial.convert.ST_Force3D;
import org.h2gis.utilities.JDBCUtilities;
import org.h2gis.utilities.TableLocation;
-import org.h2gis.utilities.TableUtilities;
import org.h2gis.utilities.dbtypes.DBTypes;
import org.h2gis.utilities.dbtypes.DBUtils;
import org.h2gis.utilities.jts_utils.Contouring;
@@ -57,6 +58,7 @@ public class IsoSurface {
public static final List NF31_133_ISO = Collections.unmodifiableList(Arrays.asList(35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0));
private int exportDimension = 2;
+ private ProgressVisitor progressVisitor = new EmptyProgressVisitor();
/**
* @param isoLevels Iso levels in dB. First range start with -Infinity then first level excluded.
@@ -521,6 +523,39 @@ public void createTable(Connection connection) throws SQLException {
createTable(connection, pkField);
}
+ /**
+ * Retrieve all unique time periods in receivers level table
+ * @param connection SQL connection
+ * @return List of unique periods
+ * @throws SQLException Error during SQL query
+ */
+ public List getUniquePeriods(Connection connection) throws SQLException {
+ List fieldValues = new ArrayList<>();
+ try(Statement statement = connection.createStatement()) {
+ try (ResultSet result =
+ statement.executeQuery(String.format("SELECT DISTINCT period FROM %s ORDER BY period", pointTable))) {
+ while (result.next()) {
+ fieldValues.add(result.getString(1));
+ }
+ }
+ }
+ return fieldValues;
+ }
+
+ /**
+ * @return instance of progress information
+ */
+ public ProgressVisitor getProgressVisitor() {
+ return progressVisitor;
+ }
+
+ /**
+ * @param progressVisitor Progress informations
+ */
+ public void setProgressVisitor(ProgressVisitor progressVisitor) {
+ this.progressVisitor = progressVisitor;
+ }
+
/**
* @param connection
* @param pkField Field name in point table to join with Triangle table and point table
@@ -571,12 +606,33 @@ public void createTable(Connection connection, String pkField) throws SQLExcepti
if(!aggregateByPeriod) {
periods.add("");
} else {
- periods.addAll(JDBCUtilities.getUniqueFieldValues(connection, pointTable, periodField));
+ periods.addAll(getUniquePeriods(connection));
}
+ ProgressVisitor periodProgress = progressVisitor.subProcess(periods.size());
for (String period : periods) {
if(aggregateByPeriod) {
statement.setString(1, period);
}
+
+ // Evaluate the number of triangles with this period in order to have a relevant progress information
+ int numRows = 0;
+ StringBuilder selectCountQuery = new StringBuilder();
+ selectCountQuery.append("SELECT COUNT(*) FROM ").append(triangleTable);
+ if(aggregateByPeriod) {
+ selectCountQuery.append(" t, ").append(pointTable).append(" p1 WHERE t.PK_1 = p1.").append(pkField);
+ selectCountQuery.append(" AND p1.PERIOD = ?");
+ }
+ try(PreparedStatement countStatement = connection.prepareStatement(selectCountQuery.toString())) {
+ if(aggregateByPeriod) {
+ countStatement.setString(1, period);
+ }
+ try (ResultSet countResult = countStatement.executeQuery()) {
+ if (countResult.next()) {
+ numRows = countResult.getInt(1);
+ }
+ }
+ }
+ ProgressVisitor cellProgress = periodProgress.subProcess(numRows);
// Cache iso for the current processing cell
Map> polyMap = new HashMap<>();
try (ResultSet rs = statement.executeQuery()) {
@@ -659,6 +715,7 @@ public void createTable(Connection connection, String pkField) throws SQLExcepti
polygonsArray.add(poly);
}
}
+ cellProgress.endStep();
}
}
if (!polyMap.isEmpty()) {
diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/IsoSurfaceJDBCTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/IsoSurfaceJDBCTest.java
index 5116a3346..8e7a32b19 100644
--- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/IsoSurfaceJDBCTest.java
+++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/IsoSurfaceJDBCTest.java
@@ -9,9 +9,12 @@
package org.noise_planet.noisemodelling.jdbc;
+import org.h2.value.ValueBoolean;
import org.h2gis.api.EmptyProgressVisitor;
import org.h2gis.functions.factory.H2GISDBFactory;
import org.h2gis.functions.io.geojson.GeoJsonRead;
+import org.h2gis.functions.io.shp.SHPWrite;
+import org.h2gis.utilities.GeometryTableUtilities;
import org.h2gis.utilities.JDBCUtilities;
import org.h2gis.utilities.SpatialResultSet;
import org.junit.jupiter.api.AfterEach;
@@ -93,8 +96,9 @@ public void testIsoSurface() throws SQLException, IOException {
@Test
public void testContouring3D() throws SQLException, IOException, LayerDelaunayError {
// Will create elevation iso from DEM table
+ // Take the resource from the other project in the source tree (we are not dependent on it)
GeoJsonRead.importTable(connection, Paths.get(Paths.get(System.getProperty("user.dir")).getParent().toString(),
- "wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/dem.geojson").toString());
+ "noisemodelling-scripts/src/test/resources/org/noise_planet/noisemodelling/scripts/dem.geojson").toString());
LayerTinfour delaunayTool = new LayerTinfour();
try (PreparedStatement st = connection.prepareStatement(
"SELECT the_geom FROM DEM")) {
@@ -110,7 +114,7 @@ public void testContouring3D() throws SQLException, IOException, LayerDelaunayEr
delaunayTool.processDelaunay();
DelaunayReceiversMaker.generateResultTable(connection, "RECEIVERS", "TRIANGLES",
new AtomicInteger(), delaunayTool.getVertices(), new GeometryFactory(), delaunayTool.getTriangles(),
- 0, 0, 1);
+ 0, 0, 1, false);
try(Statement st = connection.createStatement()) {
st.execute("ALTER TABLE RECEIVERS ADD COLUMN HEIGHT FLOAT");
st.execute("UPDATE RECEIVERS SET HEIGHT = ST_Z(THE_GEOM)");
@@ -140,8 +144,9 @@ public void testContouring3D() throws SQLException, IOException, LayerDelaunayEr
@Test
public void testContouring3DMerge() throws SQLException, IOException, LayerDelaunayError {
// Will create elevation iso from DEM table
+ // Take the resource from the other project in the source tree (we are not dependent on it)
GeoJsonRead.importTable(connection, Paths.get(Paths.get(System.getProperty("user.dir")).getParent().toString(),
- "wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/dem.geojson").toString());
+ "noisemodelling-scripts/src/test/resources/org/noise_planet/noisemodelling/scripts/dem.geojson").toString());
LayerTinfour delaunayTool = new LayerTinfour();
try (PreparedStatement st = connection.prepareStatement(
"SELECT the_geom FROM DEM")) {
@@ -157,7 +162,7 @@ public void testContouring3DMerge() throws SQLException, IOException, LayerDelau
delaunayTool.processDelaunay();
DelaunayReceiversMaker.generateResultTable(connection, "RECEIVERS", "TRIANGLES",
new AtomicInteger(), delaunayTool.getVertices(), new GeometryFactory(), delaunayTool.getTriangles(),
- 0, 0, 1);
+ 0, 0, 1, false);
try(Statement st = connection.createStatement()) {
st.execute("ALTER TABLE RECEIVERS ADD COLUMN HEIGHT FLOAT");
st.execute("UPDATE RECEIVERS SET HEIGHT = ST_Z(THE_GEOM)");
@@ -192,7 +197,7 @@ public void testNoiseMapBuilding() throws Exception {
noisemap.setReceiverHasAbsoluteZCoordinates(false);
noisemap.setSourceHasAbsoluteZCoordinates(false);
noisemap.setHeightField("HEIGHT");
- noisemap.initialize(connection, new EmptyProgressVisitor());
+ noisemap.initialize(connection);
AtomicInteger pk = new AtomicInteger(0);
for(int i=0; i < noisemap.getGridDim(); i++) {
@@ -214,9 +219,9 @@ public void testGenerateReceiversAndPeriodIsoCountours() throws SQLException {
IsoSurface isoSurface = new IsoSurface(IsoSurface.NF31_133_ISO, srid);
// Generate delaunay triangulation
DelaunayReceiversMaker delaunayReceiversMaker = new DelaunayReceiversMaker("BUILDINGS", "ROADS_TRAFF");
- delaunayReceiversMaker.setMaximumArea(800);
+ delaunayReceiversMaker.setMaximumArea(0);
delaunayReceiversMaker.setGridDim(1);
- delaunayReceiversMaker.run(connection, "RECEIVERS" , isoSurface.getTriangleTable());
+ delaunayReceiversMaker.run(connection, "RECEIVERS" , isoSurface.getTriangleTable(), new EmptyProgressVisitor());
// Create noise map for 4 periods
NoiseMapByReceiverMaker noiseMapByReceiverMaker = new NoiseMapByReceiverMaker("BUILDINGS",
@@ -227,7 +232,6 @@ public void testGenerateReceiversAndPeriodIsoCountours() throws SQLException {
noiseMapByReceiverMaker.setComputeHorizontalDiffraction(false);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportReceiverPosition = true;
noiseMapByReceiverMaker.setGridDim(1);
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setMaximumError(3);
noiseMapByReceiverMaker.run(connection, new RootProgressVisitor(1, true, 5));
@@ -245,6 +249,7 @@ public void testGenerateReceiversAndPeriodIsoCountours() throws SQLException {
isoSurface.setPointTableField("LAEQ");
isoSurface.setSmooth(false); // faster
isoSurface.setMergeTriangles(false); // faster
+ isoSurface.setProgressVisitor(new RootProgressVisitor(1, true, 5));
isoSurface.createTable(connection, "IDRECEIVER");
List columnNames = JDBCUtilities.getColumnNames(connection, isoSurface.getOutputTable());
@@ -259,4 +264,81 @@ public void testGenerateReceiversAndPeriodIsoCountours() throws SQLException {
}
}
+
+ @Test
+ public void testDelaunayReceiverSkipCells() throws SQLException, IOException {
+ GeoJsonRead.importTable(connection, IsoSurfaceJDBCTest.class.getResource("SPARSE_BUILDINGS.geojson").getFile());
+ GeoJsonRead.importTable(connection, IsoSurfaceJDBCTest.class.getResource("SPARSE_ROADS.geojson").getFile());
+ try(Statement st = connection.createStatement()) {
+ st.execute("ALTER TABLE SPARSE_BUILDINGS ALTER COLUMN PK INTEGER NOT NULL");
+ st.execute("ALTER TABLE SPARSE_BUILDINGS ADD PRIMARY KEY (PK)");
+ st.execute("ALTER TABLE SPARSE_ROADS ALTER COLUMN PK INTEGER NOT NULL");
+ st.execute("ALTER TABLE SPARSE_ROADS ADD PRIMARY KEY (PK)");
+ }
+
+ DelaunayReceiversMaker delaunayReceiversMaker = new DelaunayReceiversMaker("SPARSE_BUILDINGS", "SPARSE_ROADS");
+
+ delaunayReceiversMaker.setMinimalSourceGeometriesDistanceToComputeCell(1000);
+ delaunayReceiversMaker.setMaximumPropagationDistance(500);
+ delaunayReceiversMaker.setMaximumArea(2000);
+ delaunayReceiversMaker.setVerbose(true);
+ delaunayReceiversMaker.setExportTrianglesGeometries(false);
+ delaunayReceiversMaker.run(connection, "RECEIVERS", "TRIANGLES", new EmptyProgressVisitor());
+
+ // Count the number of unique cell_id values
+ int rowCount = 0;
+ try(Statement st = connection.createStatement()) {
+ try (ResultSet rs = st.executeQuery("SELECT COUNT(DISTINCT CELL_ID) AS CNT FROM TRIANGLES")) {
+ if (rs.next()) {
+ rowCount = rs.getInt("CNT");
+ }
+ }
+ }
+ // 16 instead of 4096
+ assertEquals(16, rowCount);
+
+ }
+
+
+ @Test
+ public void testDelaunayNoReceiverInBuildings() throws SQLException, IOException {
+ GeoJsonRead.importTable(connection, IsoSurfaceJDBCTest.class.getResource("delaunaytest/buildings.geojson.gz").getFile(), "BUILDINGS", ValueBoolean.TRUE);
+ GeoJsonRead.importTable(connection, IsoSurfaceJDBCTest.class.getResource("delaunaytest/sources.geojson.gz").getFile(), "SOURCES", ValueBoolean.TRUE);
+ try(Statement st = connection.createStatement()) {
+ // Add primary keys
+ st.execute("ALTER TABLE buildings ALTER COLUMN PK INTEGER NOT NULL");
+ st.execute("ALTER TABLE buildings ADD PRIMARY KEY (PK)");
+ st.execute("ALTER TABLE sources ALTER COLUMN PK INTEGER NOT NULL");
+ st.execute("ALTER TABLE sources ADD PRIMARY KEY (PK)");
+ // Create spatial indexes
+ st.execute("CREATE SPATIAL INDEX ON sources(THE_GEOM)");
+ st.execute("CREATE SPATIAL INDEX ON buildings(THE_GEOM)");
+ }
+
+
+
+ DelaunayReceiversMaker delaunayReceiversMaker = new DelaunayReceiversMaker("BUILDINGS", "SOURCES");
+
+ delaunayReceiversMaker.setMaximumPropagationDistance(5000);
+ delaunayReceiversMaker.setMaximumArea(500);
+ delaunayReceiversMaker.run(connection, "RECEIVERS", "TRIANGLES", new EmptyProgressVisitor());
+
+ // Check if there is no receiver in buildings
+ try(Statement st = connection.createStatement()) {
+ try (ResultSet rs = st.executeQuery("SELECT COUNT(*) AS CNT FROM RECEIVERS R, BUILDINGS B WHERE R.THE_GEOM && B.THE_GEOM AND ST_CONTAINS(B.THE_GEOM, R.THE_GEOM)")) {
+ if (rs.next()) {
+ assertEquals(0, rs.getInt("CNT"));
+ }
+ }
+ }
+ // Check the minimal distance of receivers to buildings
+ try(Statement st = connection.createStatement()) {
+ try (ResultSet rs = st.executeQuery("SELECT MIN(ST_DISTANCE(RECEIVERS.THE_GEOM, BUILDINGS.THE_GEOM)) AS MINDIST FROM RECEIVERS, BUILDINGS WHERE ST_EXPAND(RECEIVERS.THE_GEOM, 10) && BUILDINGS.THE_GEOM")) {
+ if (rs.next()) {
+ assertTrue(rs.getDouble("MINDIST") > 1, "Minimal distance between receivers and buildings should be greater than 1m");
+ }
+ }
+ }
+
+ }
}
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMakerTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMakerTest.java
index 3ec5d1689..af55fa7dc 100644
--- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMakerTest.java
+++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/NoiseMapByReceiverMakerTest.java
@@ -31,6 +31,7 @@
import org.noise_planet.noisemodelling.propagation.cnossos.CnossosPath;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.GroundAbsorption;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.Orientation;
+import org.noise_planet.noisemodelling.propagation.cnossos.PointPath;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -70,7 +71,7 @@ public void testGroundSurface() throws Exception {
noiseMapByReceiverMaker.setHeightField("HEIGHT");
noiseMapByReceiverMaker.setSoilTableName("LAND_G");
noiseMapByReceiverMaker.setFrequencyFieldPrepend("DB_M");
- noiseMapByReceiverMaker.initialize(connection, new EmptyProgressVisitor());
+ noiseMapByReceiverMaker.initialize(connection);
Set processedReceivers = new HashSet<>();
Map populatedCells = noiseMapByReceiverMaker.searchPopulatedCells(connection);
@@ -140,7 +141,6 @@ public void testPointDirectivity() throws Exception {
noiseMapByReceiverMaker.setMaximumPropagationDistance(1000);
noiseMapByReceiverMaker.setHeightField("HEIGHT");
noiseMapByReceiverMaker.setInputMode(SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW_DEN);
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setCoefficientVersion(1);
// Use train directivity functions instead of discrete directivity
noiseMapByReceiverMaker.getSceneInputSettings().setUseTrainDirectivity(true);
@@ -180,7 +180,6 @@ public void testLineDirectivity() throws Exception {
noiseMapByReceiverMaker.setMaximumPropagationDistance(1000);
noiseMapByReceiverMaker.setHeightField("HEIGHT");
noiseMapByReceiverMaker.setInputMode(SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW_DEN);
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setCoefficientVersion(1);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportRaysMethod = NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportCnossosPathWithAttenuation = true;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportAttenuationMatrix = true;
@@ -258,7 +257,6 @@ public void testPointRayDirectivity() throws Exception {
noiseMapByReceiverMaker.setMaximumPropagationDistance(1000);
noiseMapByReceiverMaker.setHeightField("HEIGHT");
noiseMapByReceiverMaker.setInputMode(SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW_DEN);
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setCoefficientVersion(1);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportRaysMethod = NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportCnossosPathWithAttenuation = true;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportAttenuationMatrix = true;
@@ -328,9 +326,9 @@ public void testEmissionTrafficTable() throws SQLException {
IsoSurface isoSurface = new IsoSurface(IsoSurface.NF31_133_ISO, srid);
// Generate delaunay triangulation
DelaunayReceiversMaker delaunayReceiversMaker = new DelaunayReceiversMaker("BUILDINGS", "ROADS_TRAFF");
- delaunayReceiversMaker.setMaximumArea(800);
+ delaunayReceiversMaker.setMaximumArea(0);
delaunayReceiversMaker.setGridDim(1);
- delaunayReceiversMaker.run(connection, "RECEIVERS", isoSurface.getTriangleTable());
+ delaunayReceiversMaker.run(connection, "RECEIVERS", isoSurface.getTriangleTable(), new EmptyProgressVisitor());
// Create noise map for 4 periods
NoiseMapByReceiverMaker noiseMapByReceiverMaker = new NoiseMapByReceiverMaker("BUILDINGS",
@@ -338,6 +336,7 @@ public void testEmissionTrafficTable() throws SQLException {
noiseMapByReceiverMaker.setMaximumPropagationDistance(100);
noiseMapByReceiverMaker.setSoundReflectionOrder(0);
+ noiseMapByReceiverMaker.setComputeVerticalDiffraction(false);
noiseMapByReceiverMaker.setComputeHorizontalDiffraction(false);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportReceiverPosition = true;
noiseMapByReceiverMaker.setGridDim(1);
@@ -351,8 +350,8 @@ public void testEmissionTrafficTable() throws SQLException {
int resultRowCount = JDBCUtilities.getRowCount(connection,
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().receiversLevelTable);
- // D E N, should be 3 more rows than receivers
- assertEquals(receiversRowCount * 3, resultRowCount);
+ // D E N and DEN, should be 4 more rows than receivers
+ assertEquals(receiversRowCount * 4, resultRowCount);
}
}
@@ -373,9 +372,9 @@ public void testEmissionLwTable() throws SQLException {
IsoSurface isoSurface = new IsoSurface(IsoSurface.NF31_133_ISO, srid);
// Generate delaunay triangulation
DelaunayReceiversMaker delaunayReceiversMaker = new DelaunayReceiversMaker("BUILDINGS", "SOURCES_GEOM");
- delaunayReceiversMaker.setMaximumArea(800);
+ delaunayReceiversMaker.setMaximumArea(0);
delaunayReceiversMaker.setGridDim(1);
- delaunayReceiversMaker.run(connection, "RECEIVERS", isoSurface.getTriangleTable());
+ delaunayReceiversMaker.run(connection, "RECEIVERS", isoSurface.getTriangleTable(), new EmptyProgressVisitor());
// Create noise map for 4 periods
NoiseMapByReceiverMaker noiseMapByReceiverMaker = new NoiseMapByReceiverMaker("BUILDINGS",
@@ -397,8 +396,8 @@ public void testEmissionLwTable() throws SQLException {
int resultRowCount = JDBCUtilities.getRowCount(connection,
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().receiversLevelTable);
- // D E N, should be 3 more rows than receivers
- assertEquals(receiversRowCount * 3, resultRowCount);
+ // D E N and DEN, should be 4 more rows than receivers
+ assertEquals(receiversRowCount * 4, resultRowCount);
}
}
@@ -433,7 +432,6 @@ public void testPointDem() throws Exception {
noiseMapByReceiverMaker.setSoundReflectionOrder(0);
noiseMapByReceiverMaker.setMaximumPropagationDistance(1000);
noiseMapByReceiverMaker.setHeightField("HEIGHT");
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setCoefficientVersion(1);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportRaysMethod = NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().raysTable = "RAYS";
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportCnossosPathWithAttenuation = true;
@@ -469,11 +467,11 @@ public void testPointDem() throws Exception {
/**
- * Place a point source into a building to check if the source is ignored
+ * Place a point source into a building to check if the source is still computed but a warning will be logged
* @throws Exception
*/
@Test
- public void testIgnoredSourceInBuilding() throws Exception {
+ public void testSourceInBuilding() throws Exception {
try (Statement st = connection.createStatement()) {
// Import shape file
// org/noise_planet/noisemodelling/jdbc/PointSource/DEM_Fence.shp
@@ -500,7 +498,6 @@ public void testIgnoredSourceInBuilding() throws Exception {
noiseMapByReceiverMaker.setSoundReflectionOrder(0);
noiseMapByReceiverMaker.setMaximumPropagationDistance(1000);
noiseMapByReceiverMaker.setHeightField("HEIGHT");
- noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().setCoefficientVersion(1);
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportRaysMethod = NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE;
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().raysTable = "RAYS";
noiseMapByReceiverMaker.getNoiseMapDatabaseParameters().exportCnossosPathWithAttenuation = true;
@@ -520,7 +517,12 @@ public void testIgnoredSourceInBuilding() throws Exception {
pathsParameters.add(cnossosPath);
}
}
- assertEquals(0 , pathsParameters.size());
+ // Diffraction over the walls of the building, but no direct path
+ assertEquals(2 , pathsParameters.size());
+ // Homogenous path with diffraction over the building wall
+ assertEquals(PointPath.POINT_TYPE.DIFH, pathsParameters.get(0).getPointList().get(1).type);
+ // Favorable path with diffraction over the building wall
+ assertEquals(PointPath.POINT_TYPE.DIFH, pathsParameters.get(1).getPointList().get(1).type);
}
}
}
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/SceneWithEmissionTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/SceneWithEmissionTest.java
index 51d4bc2d3..196edcf25 100644
--- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/SceneWithEmissionTest.java
+++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/SceneWithEmissionTest.java
@@ -11,21 +11,32 @@
import org.h2gis.api.EmptyProgressVisitor;
import org.h2gis.functions.factory.H2GISDBFactory;
+import org.h2gis.utilities.GeometryTableUtilities;
import org.h2gis.utilities.JDBCUtilities;
+import org.h2gis.utilities.TableLocation;
+import org.h2gis.utilities.dbtypes.DBTypes;
+import org.h2gis.utilities.dbtypes.DBUtils;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.noise_planet.noisemodelling.jdbc.input.SceneDatabaseInputSettings;
+import org.noise_planet.noisemodelling.jdbc.input.DefaultTableLoader;
import org.noise_planet.noisemodelling.jdbc.input.SceneWithEmission;
import org.noise_planet.noisemodelling.jdbc.output.AttenuationOutputMultiThread;
+import org.noise_planet.noisemodelling.jdbc.utils.CellIndex;
import org.noise_planet.noisemodelling.pathfinder.PathFinder;
import org.noise_planet.noisemodelling.pathfinder.delaunay.LayerDelaunayError;
+import org.noise_planet.noisemodelling.pathfinder.path.Scene;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutProfile;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.ProfileBuilder;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.WallAbsorption;
import org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions;
import org.noise_planet.noisemodelling.propagation.AttenuationParameters;
import org.noise_planet.noisemodelling.propagation.ReceiverNoiseLevel;
+import org.noise_planet.noisemodelling.propagation.cnossos.CnossosPath;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.sql.Connection;
@@ -41,8 +52,11 @@
* Test class evaluation and testing attenuation values.
*/
public class SceneWithEmissionTest {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SceneWithEmissionTest.class);
private static final double HUMIDITY = 70;
private static final double TEMPERATURE = 10;
+ public boolean verbose = false;
+
private List testIgnoreNonSignificantSourcesParam(Connection connection, double maxError) throws SQLException, IOException {
return testIgnoreNonSignificantSourcesParam(connection, maxError, "BUILDINGS",
"LW_ROADS", "RECEIVERS", "");
@@ -127,6 +141,160 @@ private static Map fetchReceiverLevel(Connection connection) thr
return allSourcesReceiverLevel;
}
+ private static double[] createFlatSpectrum(ProfileBuilder profileBuilder, double levelDb) {
+ double[] spectrum = new double[profileBuilder.frequencyArray.size()];
+ Arrays.fill(spectrum, AcousticIndicatorsFunctions.dBToW(levelDb));
+ return spectrum;
+ }
+
+ private static AttenuationParameters createPeriodParameters(SceneWithEmission scene) {
+ AttenuationParameters parameters = new AttenuationParameters(scene.defaultCnossosParameters);
+ parameters.setHumidity(HUMIDITY);
+ parameters.setTemperature(TEMPERATURE);
+ return parameters;
+ }
+
+ private static AttenuationOutputMultiThread runSceneWithMaximumError(SceneWithEmission scene, double maxError) {
+ AttenuationOutputMultiThread output = new AttenuationOutputMultiThread(scene);
+ output.noiseMapDatabaseParameters.setMaximumError(maxError);
+ output.noiseMapDatabaseParameters.setMergeSources(false);
+ output.noiseMapDatabaseParameters.setExportRaysMethod(NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE);
+ output.noiseMapDatabaseParameters.setExportAttenuationMatrix(true);
+ PathFinder computeRays = new PathFinder(scene);
+ computeRays.setThreadCount(1);
+ computeRays.run(output);
+ return output;
+ }
+
+ private static Map aggregateGlobalLevelsByPeriod(Collection receiverLevels) {
+ Map levelsByPeriod = new HashMap<>();
+ for (ReceiverNoiseLevel receiverNoiseLevel : receiverLevels) {
+ levelsByPeriod.merge(receiverNoiseLevel.period,
+ sumArray(AcousticIndicatorsFunctions.dBToW(receiverNoiseLevel.levels)), Double::sum);
+ }
+ return levelsByPeriod;
+ }
+
+ private static Map countSourcePeriodReceiverLevels(Collection receiverLevels) {
+ Map counts = new HashMap<>();
+ for (ReceiverNoiseLevel receiverNoiseLevel : receiverLevels) {
+ String key = receiverNoiseLevel.period + "#" + receiverNoiseLevel.source.sourcePk;
+ counts.merge(key, 1, Integer::sum);
+ }
+ return counts;
+ }
+
+ private static Map countSourcePeriodRays(Collection cnossosPaths) {
+ Map counts = new HashMap<>();
+ for (CnossosPath cnossosPath : cnossosPaths) {
+ String key = cnossosPath.getTimePeriod() + "#" + cnossosPath.getCutProfile().getSource().sourcePk;
+ counts.merge(key, 1, Integer::sum);
+ }
+ return counts;
+ }
+
+ private static void assertOutputsEquivalentWithinMaximumError(AttenuationOutputMultiThread baseline,
+ AttenuationOutputMultiThread optimized,
+ double maxError) {
+ Map baselineReceiverKeys = countSourcePeriodReceiverLevels(baseline.resultsCache.receiverLevels);
+ Map optimizedReceiverKeys = countSourcePeriodReceiverLevels(optimized.resultsCache.receiverLevels);
+ assertEquals(baselineReceiverKeys, optimizedReceiverKeys,
+ "Optimized run should keep the same source-period receiver contributions as baseline");
+
+ Map baselineRayKeys = countSourcePeriodRays(baseline.resultsCache.cnossosPaths);
+ Map optimizedRayKeys = countSourcePeriodRays(optimized.resultsCache.cnossosPaths);
+ assertEquals(baselineRayKeys, optimizedRayKeys,
+ "Optimized run should keep the same source-period ray contributions as baseline");
+
+ Map baselineLevels = aggregateGlobalLevelsByPeriod(baseline.resultsCache.receiverLevels);
+ Map optimizedLevels = aggregateGlobalLevelsByPeriod(optimized.resultsCache.receiverLevels);
+ assertEquals(baselineLevels.keySet(), optimizedLevels.keySet(),
+ "Optimized run should keep the same period coverage as baseline");
+ for (Map.Entry entry : baselineLevels.entrySet()) {
+ String period = entry.getKey();
+ double baselineDb = wToDb(entry.getValue());
+ double optimizedDb = wToDb(optimizedLevels.get(period));
+ assertTrue(Math.abs(baselineDb - optimizedDb) <= maxError,
+ String.format(Locale.ROOT,
+ "Period %s differs by %.2f dB, expected <= %.2f dB", period,
+ Math.abs(baselineDb - optimizedDb), maxError));
+ }
+ }
+
+ private static long runDynamicConfMaxErrorFixture(double maxError) throws Exception {
+ String dbName = "dynamicConfMaxError_" + Long.toUnsignedString(Double.doubleToLongBits(maxError));
+ try (Connection connection =
+ JDBCUtilities.wrapConnection(H2GISDBFactory.createSpatialDataBase(dbName, true, ""))) {
+ try (Statement st = connection.createStatement()) {
+ st.execute(String.format("CALL SHPREAD('%s', 'BUILDINGS')",
+ SceneWithEmissionTest.class.getResource("dynamicConfMaxErrorTest/buldings_test.shp").getFile()));
+ st.execute(String.format("CALL SHPREAD('%s', 'SOURCES')",
+ SceneWithEmissionTest.class.getResource("dynamicConfMaxErrorTest/sources_test.shp").getFile()));
+ st.execute(String.format("CALL SHPREAD('%s', 'RECEIVERS')",
+ SceneWithEmissionTest.class.getResource("dynamicConfMaxErrorTest/receivers_test.shp").getFile()));
+ }
+
+ splitDynamicSourcesPeriod(connection, "SOURCES", "PK", "PERIOD",
+ "SOURCES_GEOM", "SOURCES_EMISSION");
+
+ NoiseMapByReceiverMaker noiseMap = new NoiseMapByReceiverMaker("BUILDINGS", "SOURCES_GEOM", "RECEIVERS");
+ noiseMap.setGridDim(1);
+ noiseMap.setThreadCount(1);
+ noiseMap.setHeightField("HEIGHT");
+ noiseMap.setSourcesEmissionTableName("SOURCES_EMISSION");
+ noiseMap.setMaximumPropagationDistance(100);
+ noiseMap.setMaximumReflectionDistance(50);
+ noiseMap.setSoundReflectionOrder(0);
+ noiseMap.setComputeHorizontalDiffraction(true);
+ noiseMap.setComputeVerticalDiffraction(true);
+ noiseMap.getNoiseMapDatabaseParameters().setMergeSources(true);
+ noiseMap.getNoiseMapDatabaseParameters().setMaximumError(maxError);
+ noiseMap.getNoiseMapDatabaseParameters().setExportRaysMethod(NoiseMapDatabaseParameters.ExportRaysMethods.TO_RAYS_TABLE);
+ noiseMap.getNoiseMapDatabaseParameters().setRaysTable("RAYS");
+ noiseMap.getNoiseMapDatabaseParameters().setExportAttenuationMatrix(true);
+ noiseMap.getNoiseMapDatabaseParameters().setExportCnossosPathWithAttenuation(true);
+ noiseMap.getNoiseMapDatabaseParameters().keepAbsorption = true;
+
+ DefaultTableLoader defaultTableLoader = (DefaultTableLoader) noiseMap.getPropagationProcessDataFactory();
+ defaultTableLoader.defaultParameters.setWindRose(new double[AttenuationParameters.DEFAULT_WIND_ROSE.length]);
+
+ noiseMap.run(connection, new EmptyProgressVisitor());
+
+ return JDBCUtilities.getRowCount(connection, noiseMap.getNoiseMapDatabaseParameters().getRaysTable());
+ }
+ }
+
+ private static void splitDynamicSourcesPeriod(Connection connection, String tableSourceDynamic,
+ String sourceIndexFieldName, String sourcePeriodFieldName,
+ String sourceGeomTableName, String sourceEmissionTableName)
+ throws SQLException {
+ DBTypes dbType = DBUtils.getDBType(connection.unwrap(Connection.class));
+ String sourceIndexField = TableLocation.capsIdentifier(sourceIndexFieldName, dbType);
+ String sourcePeriodField = TableLocation.capsIdentifier(sourcePeriodFieldName, dbType);
+
+ try (Statement st = connection.createStatement()) {
+ st.execute("DROP TABLE IF EXISTS " + sourceGeomTableName);
+ st.execute("DROP TABLE IF EXISTS " + sourceEmissionTableName);
+ }
+
+ List columnNames = new ArrayList<>(JDBCUtilities.getColumnNames(connection, tableSourceDynamic));
+ columnNames.remove(TableLocation.capsIdentifier("THE_GEOM", dbType));
+ columnNames.remove(sourcePeriodField);
+ columnNames.remove(sourceIndexField);
+ String additionalColumns = String.join(", ", columnNames);
+ int sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(tableSourceDynamic, dbType));
+
+ try (Statement st = connection.createStatement()) {
+ st.execute("CREATE TABLE " + sourceGeomTableName + "(IDSOURCE INT PRIMARY KEY, THE_GEOM GEOMETRY) " +
+ "AS SELECT " + sourceIndexField + " IDSOURCE, ANY_VALUE(THE_GEOM) THE_GEOM FROM " +
+ tableSourceDynamic + " GROUP BY IDSOURCE");
+ st.execute("CREATE TABLE " + sourceEmissionTableName + " AS SELECT " + sourceIndexField + " IDSOURCE, " +
+ sourcePeriodField + " PERIOD, " + additionalColumns + " FROM " + tableSourceDynamic);
+ st.execute("CREATE INDEX ON " + sourceEmissionTableName + " (IDSOURCE, PERIOD)");
+ st.execute("SELECT UpdateGeometrySRID('" + sourceGeomTableName + "','the_geom', " + sridSources + ")");
+ }
+ }
+
/**
* Test optimisation feature {@link NoiseMapDatabaseParameters#setMaximumError(double)}
* This feature is disabled and all sound sources are computed
@@ -187,18 +355,15 @@ public void testSourceLines() throws ParseException {
double[] roadLvl = AcousticIndicatorsFunctions.dBToW(new double[]{25.65, 38.15, 54.35, 60.35, 74.65, 66.75, 59.25, 53.95});
SceneWithEmission scene = new SceneWithEmission(builder);
- scene.addReceiver(new Coordinate(50, 50, 0.05));
- scene.addReceiver(new Coordinate(48, 50, 4));
- scene.addReceiver(new Coordinate(44, 50, 4));
- scene.addReceiver(new Coordinate(40, 50, 4));
- scene.addReceiver(new Coordinate(20, 50, 4));
- scene.addReceiver(new Coordinate(0, 50, 4));
+ scene.addReceiver(scene.profileBuilder.offsetCoordinatesToAltitudeUsingDigitalElevationModel(
+ new Coordinate[]{new Coordinate(50, 50, 0.05), new Coordinate(48, 50, 4), new Coordinate(44, 50, 4),
+ new Coordinate(40, 50, 4), new Coordinate(20, 50, 4), new Coordinate(0, 50, 4)}, false));
List srcPtsRef = new ArrayList<>();
PathFinder.splitLineStringIntoPoints(geomSource, 1.0, srcPtsRef);
for (long i = 0; i < srcPtsRef.size(); i++) {
Coordinate srcPtRef = srcPtsRef.get((int) i);
- scene.addSource(i, factory.createPoint(srcPtRef));
+ scene.addSource(i, scene.profileBuilder.makeGeometryRelativeZToAbsolute(factory.createPoint(srcPtRef), false));
scene.addSourceEmission(i, "", roadLvl);
}
@@ -213,7 +378,6 @@ public void testSourceLines() throws ParseException {
AttenuationOutputMultiThread propDataOut = new AttenuationOutputMultiThread(scene);
PathFinder computeRays = new PathFinder(scene);
- computeRays.makeRelativeZToAbsolute();
computeRays.setThreadCount(1);
computeRays.run(propDataOut);
@@ -360,5 +524,233 @@ public void testReceiverOverBuilding() throws LayerDelaunayError, ParseException
0.1);
}
+ @Test
+ public void testMaximumErrorMultiPeriodOverlappingSourcesFreeField() {
+ final double maxError = 0.1;
+ GeometryFactory factory = new GeometryFactory();
+
+ ProfileBuilder builder = new ProfileBuilder();
+ builder.finishFeeding();
+
+ SceneWithEmission scene = new SceneWithEmission(builder);
+ scene.addReceiver(new Coordinate(0, 0, 4.0));
+ scene.setComputeHorizontalDiffraction(false);
+ scene.setComputeVerticalDiffraction(false);
+ scene.setReflexionOrder(0);
+ scene.maxSrcDist = 500;
+ scene.defaultCnossosParameters.setHumidity(HUMIDITY);
+ scene.defaultCnossosParameters.setTemperature(TEMPERATURE);
+ scene.cnossosParametersPerPeriod.put("T0", createPeriodParameters(scene));
+ scene.cnossosParametersPerPeriod.put("T1", createPeriodParameters(scene));
+
+ scene.addSource(1L, factory.createPoint(new Coordinate(5, 0, 0.05)));
+ scene.addSourceEmission(1L, "T0", createFlatSpectrum(builder, 120.0));
+
+ Coordinate duplicatedSource = new Coordinate(60, 0, 0.05);
+ scene.addSource(2L, factory.createPoint(duplicatedSource));
+ scene.addSourceEmission(2L, "T1", createFlatSpectrum(builder, 112.0));
+ scene.addSource(3L, factory.createPoint(new Coordinate(duplicatedSource)));
+ scene.addSourceEmission(3L, "T1", createFlatSpectrum(builder, 112.0));
+
+ AttenuationOutputMultiThread baseline = runSceneWithMaximumError(scene, 0.0);
+ AttenuationOutputMultiThread optimized = runSceneWithMaximumError(scene, maxError);
+
+ assertOutputsEquivalentWithinMaximumError(baseline, optimized, maxError);
+ }
+
+ @Test
+ public void testMaximumErrorMultiPeriodOverlappingSourcesWithReflection() {
+ final double maxError = 0.1;
+ GeometryFactory factory = new GeometryFactory();
+
+ List alphaWallFrequencies = Arrays.asList(AcousticIndicatorsFunctions.asOctaveBands(
+ ProfileBuilder.DEFAULT_FREQUENCIES_THIRD_OCTAVE));
+ List alphaWall = new ArrayList<>(alphaWallFrequencies.size());
+ for(int frequency : alphaWallFrequencies) {
+ alphaWall.add(WallAbsorption.getWallAlpha(100000, frequency));
+ }
+
+ ProfileBuilder profileBuilder = new ProfileBuilder()
+ .addWall(new Coordinate[]{
+ new Coordinate(6, 0, 4),
+ new Coordinate(-5, 12, 4),
+ }, 8, alphaWall, 0)
+ .addWall(new Coordinate[]{
+ new Coordinate(14, 4, 4),
+ new Coordinate(3, 16, 4),
+ }, 8, alphaWall, 1);
+ profileBuilder.setzBuildings(true);
+ profileBuilder.finishFeeding();
+
+ SceneWithEmission scene = new SceneWithEmission(profileBuilder);
+ scene.addReceiver(new Coordinate(4.5, 8, 1.6));
+ scene.setDefaultGroundAttenuation(0.5);
+ scene.setComputeHorizontalDiffraction(false);
+ scene.setComputeVerticalDiffraction(false);
+ scene.setReflexionOrder(1);
+ scene.maxSrcDist = 500;
+ scene.maxRefDist = 500;
+ scene.defaultCnossosParameters.setHumidity(HUMIDITY);
+ scene.defaultCnossosParameters.setTemperature(TEMPERATURE);
+ scene.cnossosParametersPerPeriod.put("T0", createPeriodParameters(scene));
+ scene.cnossosParametersPerPeriod.put("T1", createPeriodParameters(scene));
+
+ scene.addSource(1L, factory.createPoint(new Coordinate(2.5, 8, 0.1)));
+ scene.addSourceEmission(1L, "T0", createFlatSpectrum(profileBuilder, 120.0));
+
+ Coordinate duplicatedSource = new Coordinate(8, 5.5, 0.1);
+ scene.addSource(2L, factory.createPoint(duplicatedSource));
+ scene.addSourceEmission(2L, "T1", createFlatSpectrum(profileBuilder, 112.0));
+ scene.addSource(3L, factory.createPoint(new Coordinate(duplicatedSource)));
+ scene.addSourceEmission(3L, "T1", createFlatSpectrum(profileBuilder, 112.0));
+
+ AttenuationOutputMultiThread baseline = runSceneWithMaximumError(scene, 0.0);
+ assertTrue(baseline.resultsCache.cnossosPaths.stream().anyMatch(cnossosPath ->
+ cnossosPath.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.REFLECTION),
+ "Baseline scene should include at least one reflection path");
+
+ AttenuationOutputMultiThread optimized = runSceneWithMaximumError(scene, maxError);
+
+ assertOutputsEquivalentWithinMaximumError(baseline, optimized, maxError);
+ }
+
+ @Test
+ public void testDynamicConfMaxErrorFixtureKeepsSameRayCount() throws Exception {
+ long rayCountWithoutPruning = runDynamicConfMaxErrorFixture(0.0);
+ long rayCountWithPruning = runDynamicConfMaxErrorFixture(0.1);
+
+ assertTrue(rayCountWithoutPruning > 0, "Fixture should generate at least one ray");
+ assertEquals(rayCountWithoutPruning, rayCountWithPruning,
+ String.format(Locale.ROOT,
+ "Expected the same number of rays with confMaxError=0.0 and 0.1, but got %d and %d",
+ rayCountWithoutPruning, rayCountWithPruning));
+ }
+
+ /**
+ * Comparison: line source (500 m) and 0.1 dB error simplification vs discretized point sources (1 m spacing) without error.
+ * Free-field, no buildings, no ground effects (fully reflective), flat spectrum.
+ * The difference at each receiver should be less than the maximum error parameter per frequency band.
+ */
+ @Test
+ public void testLongLineSourceVsDiscretePointsMaximumError() throws ParseException {
+ double maxError = 0.1;
+ GeometryFactory factory = new GeometryFactory();
+
+ // 500 m horizontal line source at height 0.05 m
+ double cx = 250.0;
+ double cy = 0.0;
+ double halfLen = 250.0;
+ double x1 = cx - halfLen;
+ double x2 = cx + halfLen;
+
+ WKTReader wktReader = new WKTReader(factory);
+ LineString lineSource = (LineString) wktReader.read(
+ "LINESTRING (" + x1 + " " + cy + " 0.05, " + x2 + " " + cy + " 0.05)");
+
+ // Free-field: no buildings, no ground effect
+ ProfileBuilder builder = new ProfileBuilder();
+ builder.finishFeeding();
+
+ double lwOctDb = 73.037;
+ double[] roadLvl = new double[8];
+ for (int i = 0; i < 8; i++) {
+ roadLvl[i] = AcousticIndicatorsFunctions.dBToW(lwOctDb);
+ }
+
+ // Receivers at various distances and angles from mid-point of the line
+ // Angles relative to the line direction: 0° = along line, 45° = diagonal, 90° = perpendicular
+ double[] receiverDistances = {10, 50, 100, 200, 400};
+ double[] receiverAngles = {0, 45, 90}; // degrees
+
+ // === RUN 1: Discretized point sources at 1 m spacing ===
+ SceneWithEmission scene = new SceneWithEmission(builder);
+ List receiverLabels = new ArrayList<>();
+ for (double angleDeg : receiverAngles) {
+ double angleRad = Math.toRadians(angleDeg);
+ for (double dist : receiverDistances) {
+ double rx = cx + dist * Math.cos(angleRad);
+ double ry = cy + dist * Math.sin(angleRad);
+ scene.addReceiver(new Coordinate(rx, ry, 4.0));
+ receiverLabels.add(String.format("d=%dm, angle=%d°", (int) dist, (int) angleDeg));
+ }
+ }
+
+ List srcPts = new ArrayList<>();
+ PathFinder.splitLineStringIntoPoints(lineSource, 1.0, srcPts);
+ for (int i = 0; i < srcPts.size(); i++) {
+ scene.addSource((long) i, factory.createPoint(srcPts.get(i)));
+ scene.addSourceEmission((long) i, "", roadLvl);
+ }
+
+ scene.setComputeHorizontalDiffraction(false);
+ scene.setComputeVerticalDiffraction(false);
+ scene.setReflexionOrder(0);
+ scene.maxSrcDist = 800;
+
+ scene.defaultCnossosParameters.setHumidity(70);
+ scene.defaultCnossosParameters.setTemperature(15);
+
+ AttenuationOutputMultiThread outPoints = new AttenuationOutputMultiThread(scene);
+ outPoints.noiseMapDatabaseParameters.setMaximumError(0);
+ PathFinder computeRays = new PathFinder(scene);
+ computeRays.setThreadCount(1);
+ computeRays.run(outPoints);
+
+ // === RUN 2: Single line source ===
+ scene.clearSources();
+ scene.addSource(1L, lineSource);
+ scene.addSourceEmission(1L, "", roadLvl);
+ AttenuationOutputMultiThread outLine = new AttenuationOutputMultiThread(scene);
+ outLine.noiseMapDatabaseParameters.setMaximumError(maxError);
+ computeRays.run(outLine);
+
+ // === Compare results ===
+ List pointLevels = new ArrayList<>(outPoints.resultsCache.receiverLevels);
+ List lineLevels = new ArrayList<>(outLine.resultsCache.receiverLevels);
+
+ int expectedCount = receiverDistances.length * receiverAngles.length;
+ assertEquals(expectedCount, pointLevels.size(),
+ "Expected one result per receiver (points)");
+ assertEquals(expectedCount, lineLevels.size(),
+ "Expected one result per receiver (line)");
+
+ for (int i = 0; i < pointLevels.size(); i++) {
+ double globalPoints = AcousticIndicatorsFunctions.sumDbArray(pointLevels.get(i).levels);
+ double globalLine = AcousticIndicatorsFunctions.sumDbArray(lineLevels.get(i).levels);
+ double diff = Math.abs(globalPoints - globalLine);
+ if(verbose) {
+ LOGGER.info("Receiver {} ({}): points={} dB, line={} dB, diff={} dB", i, receiverLabels.get(i),
+ String.format("%.2f", globalPoints), String.format("%.2f", globalLine), String.format("%.2f",
+ diff));
+ }
+ assertTrue(diff < maxError,
+ "Difference between line and point sources at receiver " + i +
+ " (" + receiverLabels.get(i) + ") is " + String.format("%.2f dB, expected < %.2f dB", diff, maxError));
+ }
+ }
+
+ /**
+ * Test if HZ fields in Source geometry table is recognized (no time periods)
+ * @throws Exception
+ */
+ @Test
+ public void testSceneInputStructureGuessLwInGeometryTable() throws Exception {
+ try(Connection connection = JDBCUtilities.wrapConnection(H2GISDBFactory.createSpatialDataBase(
+ "testSceneInputStructureGuessLwInGeometryTable", true, ""))) {
+ // Insert dummy data
+ connection.createStatement().execute(Utils.getRunScriptRes("testGeometryWithLWFields.sql"));
+ NoiseMapByReceiverMaker maker = new NoiseMapByReceiverMaker("BUILDINGS", "SOURCES", "RECEIVERS");
+ maker.setGridDim(1);
+ maker.initialize(connection);
+ assertEquals(SceneDatabaseInputSettings.INPUT_MODE.INPUT_MODE_LW, maker.getSceneInputSettings().getInputMode(),
+ "Scene input structure should be correctly identified as having LW in geometry table");
+ SceneWithEmission sceneWithEmission = maker.getTableLoader().create(connection, new CellIndex(0, 0), new HashSet<>());
+ assertEquals(1, sceneWithEmission.sourceGeometries.size());
+ assertTrue(sceneWithEmission.wjSources.containsKey(1L), "Source with PK=1 should be present in wjSources");
+ assertEquals(24, sceneWithEmission.wjSources.get(1L).get(0).emission.length);
+ assertEquals(dBToW(100.0), sceneWithEmission.wjSources.get(1L).get(0).emission[0], 1e-6,
+ "First frequency band should have correct LW value converted to W");
+ }
+ }
}
diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/TableLoaderTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/TableLoaderTest.java
index 237b7d9a1..d76194ef3 100644
--- a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/TableLoaderTest.java
+++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/TableLoaderTest.java
@@ -13,6 +13,7 @@
import org.h2gis.functions.io.dbf.DBFRead;
import org.h2gis.functions.io.shp.SHPRead;
import org.h2gis.utilities.JDBCUtilities;
+import org.h2gis.utilities.SpatialResultSet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -24,6 +25,7 @@
import org.noise_planet.noisemodelling.jdbc.railway.RailWayLWGeom;
import org.noise_planet.noisemodelling.jdbc.railway.RailWayLWIterator;
import org.noise_planet.noisemodelling.jdbc.utils.CellIndex;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.ProfileBuilder;
import org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions;
import java.io.IOException;
@@ -38,12 +40,14 @@
import java.util.stream.Collectors;
import static org.junit.jupiter.api.Assertions.*;
-import static org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions.sumArray;
-import static org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions.sumDbArray;
+import static org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions.*;
public class TableLoaderTest {
private Connection connection;
+ public static double[] aWeightingArray = Arrays.stream(
+ asOctaveBands(ProfileBuilder.DEFAULT_FREQUENCIES_A_WEIGHTING_THIRD_OCTAVE)).
+ mapToDouble(value -> value).toArray();
@BeforeEach
public void tearUp() throws Exception {
@@ -66,7 +70,7 @@ public void testNoiseEmissionRailWay() throws SQLException, IOException {
assertTrue(rs.next());
expectedNumberOfRows = rs.getInt(1);
}
- RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAILTRACK", "RAILTRAIN","RailwayVehiclesCnossos.json","RailwayTrainsets.json", "RailwayCnossosSNCF_2021.json");
+ RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAILTRACK", "RAILTRAIN","RailwayVehiclesCnossos.json","RailwayTrainsets.json", "RailwayEmissionCnossos.json");
int numberOfRows = 0;
while (railWayLWIterator.hasNext()) {
@@ -154,6 +158,12 @@ public void testNoiseEmissionRailWay_OC5() throws SQLException, IOException {
SHPRead.importTable(connection, TableLoaderTest.class.getResource("Test/OC/RailTrack.shp").getFile());
DBFRead.importTable(connection, TableLoaderTest.class.getResource("Test/OC/RailTrain.dbf").getFile());
+ // Update using new string-based identifiers
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ALTER COLUMN ROUGHNESS VARCHAR");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ALTER COLUMN TRANSFER VARCHAR");
+ connection.createStatement().execute("UPDATE RAILTRACK SET ROUGHNESS = CONCAT('SNCF', ROUGHNESS)");
+ connection.createStatement().execute("UPDATE RAILTRACK SET TRANSFER = CONCAT('SNCF', TRANSFER)");
+
RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAILTRACK", "RAILTRAIN");
RailWayLWGeom v = railWayLWIterator.next();
assertNotNull(v);
@@ -166,6 +176,47 @@ public void testNoiseEmissionRailWay_OC5() throws SQLException, IOException {
}
+ /**
+ * Test with SNCF-prefixed string track parameter keys (VARCHAR columns)
+ * to validate that the merged RailwayEmissionCnossos.json works with properly prefixed keys
+ * read directly from the database (e.g. "SNCF7", "SNCF1").
+ */
+ @Test
+ public void testNoiseEmissionRailWay_OC5_SNCFPrefixed() throws SQLException, IOException {
+ // Import geometry from existing OC test data
+ SHPRead.importTable(connection, TableLoaderTest.class.getResource("Test/OC/RailTrack.shp").getFile());
+
+ // Re-create RAILTRACK with VARCHAR columns for TRANSFER, ROUGHNESS, IMPACT, BRIDGE
+ // using SNCF-prefixed values matching the merged RailwayEmissionCnossos.json keys
+ connection.createStatement().execute("ALTER TABLE RAILTRACK DROP COLUMN TRANSFER");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK DROP COLUMN ROUGHNESS");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK DROP COLUMN IMPACT");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK DROP COLUMN BRIDGE");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ADD COLUMN TRANSFER VARCHAR(20)");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ADD COLUMN ROUGHNESS VARCHAR(20)");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ADD COLUMN IMPACT VARCHAR(20)");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ADD COLUMN BRIDGE VARCHAR(20)");
+ connection.createStatement().execute("UPDATE RAILTRACK SET TRANSFER='SNCF7', ROUGHNESS='SNCF1', IMPACT='', BRIDGE=''");
+
+ // Import train traffic data from existing OC test
+ DBFRead.importTable(connection, TableLoaderTest.class.getResource("Test/OC/RailTrain.dbf").getFile());
+
+ // Use the merged RailwayEmissionCnossos.json with SNCF-prefixed keys
+ RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection, "RAILTRACK", "RAILTRAIN",
+ "RailwayVehiclesCnossos.json", "RailwayTrainsets.json", "RailwayEmissionCnossos.json");
+ RailWayLWGeom v = railWayLWIterator.next();
+ assertNotNull(v);
+ v.setNbTrack(2);
+ RailWayParameters railWayLW = v.getRailWayLW();
+ assertNotNull(railWayLW);
+ assertFalse(railWayLW.getRailwaySourceList().isEmpty(), "Railway source list should not be empty with SNCF-prefixed keys");
+ List geometries = v.getRailWayLWGeometry();
+ assertNotNull(geometries);
+
+ v = railWayLWIterator.next();
+ assertFalse(railWayLWIterator.hasNext());
+ }
+
@Test
public void testNoiseEmissionRailWay_BM() throws SQLException, IOException {
double[] dBA = new double[]{-30,-26.2,-22.5,-19.1,-16.1,-13.4,-10.9,-8.6,-6.6,-4.8,-3.2,-1.9,-0.8,0,0.6,1,1.2,1.3,1.2,1,0.5,-0.1,-1.1,-2.5};
@@ -173,9 +224,15 @@ public void testNoiseEmissionRailWay_BM() throws SQLException, IOException {
SHPRead.importTable(connection, TableLoaderTest.class.getResource("Test/BM/RailTrack.shp").getFile());
DBFRead.importTable(connection, TableLoaderTest.class.getResource("Test/BM/RailTrain.dbf").getFile());
+ // Update using new string-based identifiers
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ALTER COLUMN ROUGHNESS VARCHAR");
+ connection.createStatement().execute("ALTER TABLE RAILTRACK ALTER COLUMN TRANSFER VARCHAR");
+ connection.createStatement().execute("UPDATE RAILTRACK SET ROUGHNESS = CONCAT('SNCF', ROUGHNESS)");
+ connection.createStatement().execute("UPDATE RAILTRACK SET TRANSFER = CONCAT('SNCF', TRANSFER)");
+
HashMap Resultats = new HashMap<>();
- RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAILTRACK", "RAILTRAIN","RailwayVehiclesCnossos.json","RailwayTrainsets.json", "RailwayCnossosSNCF_2021.json");
+ RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAILTRACK", "RAILTRAIN","RailwayVehiclesCnossos.json","RailwayTrainsets.json", "RailwayEmissionCnossos.json");
double resD,resE,resN;
while (railWayLWIterator.hasNext()) {
@@ -231,7 +288,13 @@ public void testNoiseEmissionRailWay_Section556() throws SQLException, IOExcepti
SHPRead.importTable(connection, TableLoaderTest.class.getResource("Test/556/RAIL_SECTIONS.shp").getFile());
DBFRead.importTable(connection, TableLoaderTest.class.getResource("Test/556/RAIL_TRAFIC.dbf").getFile());
- HashMap Resultats = new HashMap<>();
+ // Update using new string-based identifiers
+ connection.createStatement().execute("ALTER TABLE RAIL_SECTIONS ALTER COLUMN ROUGHNESS VARCHAR");
+ connection.createStatement().execute("ALTER TABLE RAIL_SECTIONS ALTER COLUMN TRANSFER VARCHAR");
+ connection.createStatement().execute("UPDATE RAIL_SECTIONS SET ROUGHNESS = CONCAT('SNCF', ROUGHNESS)");
+ connection.createStatement().execute("UPDATE RAIL_SECTIONS SET TRANSFER = CONCAT('SNCF', TRANSFER)");
+
+ HashMap results = new HashMap<>();
RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"RAIL_SECTIONS", "RAIL_TRAFIC");
@@ -281,9 +344,13 @@ public void testNoiseEmissionRailWay_Section556() throws SQLException, IOExcepti
String idSection = v.getIdSection();
- Resultats.put(idSection,new double[]{resD, resE, resN});
+ results.put(idSection,new double[]{resD, resE, resN});
}
+
+ // check results
+
+
}
@@ -291,9 +358,14 @@ public void testNoiseEmissionRailWay_Section556() throws SQLException, IOExcepti
public void testNoiseEmissionRailWayForPropa() throws SQLException, IOException {
SHPRead.importTable(connection, TableLoaderTest.class.getResource("PropaRail/Rail_Section2.shp").getFile());
DBFRead.importTable(connection, TableLoaderTest.class.getResource("PropaRail/Rail_Traffic.dbf").getFile());
+ // Update using new string-based identifiers
+ connection.createStatement().execute("ALTER TABLE RAIL_SECTION2 ALTER COLUMN ROUGHNESS VARCHAR");
+ connection.createStatement().execute("ALTER TABLE RAIL_SECTION2 ALTER COLUMN TRANSFER VARCHAR");
+ connection.createStatement().execute("UPDATE RAIL_SECTION2 SET ROUGHNESS = CONCAT('SNCF', ROUGHNESS)");
+ connection.createStatement().execute("UPDATE RAIL_SECTION2 SET TRANSFER = CONCAT('SNCF', TRANSFER)");
EmissionTableGenerator.makeTrainLWTable(connection, "Rail_Section2", "Rail_Traffic",
- "LW_RAILWAY", "HZ");
+ "LW_RAILWAY", "HZ", RailWayLWIterator.RAILWAY_VEHICLES_CNOSSOS_JSON, RailWayLWIterator.RAILWAY_TRAINSETS_JSON, RailWayLWIterator.RAILWAY_EMISSION_CNOSSOS_JSON);
// Get Class to compute LW
RailWayLWIterator railWayLWIterator = new RailWayLWIterator(connection,"Rail_Section2", "Rail_Traffic");
@@ -392,7 +464,7 @@ public void testReadFrequencies() throws SQLException, IOException {
noiseMap.setFrequencyFieldPrepend("LW");
- noiseMap.initialize(connection, new EmptyProgressVisitor());
+ noiseMap.initialize(connection);
DefaultTableLoader tableLoader = (DefaultTableLoader)noiseMap.getTableLoader();
@@ -421,7 +493,7 @@ public void testRegression1() throws SQLException, IOException {
"LW_ROADS_FENCE",
"RECEIVERS");
- noiseMapByReceiverMaker.initialize(connection, new EmptyProgressVisitor());
+ noiseMapByReceiverMaker.initialize(connection);
Map populatedCells = noiseMapByReceiverMaker.searchPopulatedCells(connection);
@@ -429,4 +501,22 @@ public void testRegression1() throws SQLException, IOException {
assertEquals(nbReceivers, populatedCells.values().stream().reduce(Integer::sum).orElse(0));
}
+ @Test
+ public void testRoadNoiseEmission() throws SQLException, IOException {
+ try(Statement statement = connection.createStatement()) {
+ statement.execute("CREATE TABLE IF NOT EXISTS TRAFFIC_TEST (PK SERIAL, LV_D REAL, LV_SPD_D REAL, HGV_D REAL, HGV_SPD_D REAL, PVMT VARCHAR);\n"
+ + "INSERT INTO TRAFFIC_TEST(LV_D, LV_SPD_D, HGV_D, HGV_SPD_D, PVMT) VALUES (3747.23, 50, 479.02, 50, 'FR_R2');");
+ }
+ Map sourceEmissionFieldsCache = new HashMap<>();
+ double globalLevel = 0;
+ try(SpatialResultSet rs = connection.createStatement().executeQuery("SELECT * FROM TRAFFIC_TEST").unwrap(SpatialResultSet.class)) {
+ assertTrue(rs.next());
+ // new double[][] {ld, le, ln}
+ double[][] lw = EmissionTableGenerator.computeLw(rs, 1, sourceEmissionFieldsCache);
+ double[] dbLw = AcousticIndicatorsFunctions.wToDb(lw[0]);
+ double[] dbALw = AcousticIndicatorsFunctions.sumArray(dbLw, aWeightingArray);
+ globalLevel = AcousticIndicatorsFunctions.sumArray(AcousticIndicatorsFunctions.dBToW(dbALw)); // add day value only
+ }
+ assertEquals(89.6, AcousticIndicatorsFunctions.wToDb(globalLevel), 0.1);
+ }
}
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/utils/DatabaseUtilitiesTest.java b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/utils/DatabaseUtilitiesTest.java
new file mode 100644
index 000000000..5c9312aa3
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/java/org/noise_planet/noisemodelling/jdbc/utils/DatabaseUtilitiesTest.java
@@ -0,0 +1,43 @@
+/**
+ * NoiseModelling is a library capable of producing noise maps. It can be freely used either for research and education, as well as by experts in a professional use.
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Official webpage : http://noise-planet.org/noisemodelling.html
+ * Contact: contact@noise-planet.org
+ */
+package org.noise_planet.noisemodelling.jdbc.utils;
+
+import org.h2gis.functions.factory.H2GISDBFactory;
+import org.h2gis.utilities.JDBCUtilities;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DatabaseUtilitiesTest {
+
+ private Connection connection;
+ @BeforeEach
+ public void tearUp() throws Exception {
+ connection = JDBCUtilities.wrapConnection(H2GISDBFactory.createSpatialDataBase(DatabaseUtilitiesTest.class.getSimpleName(), true, ""));
+ }
+
+ @AfterEach
+ public void tearDown() throws Exception {
+ if(connection != null) {
+ connection.close();
+ }
+ }
+
+ @org.junit.jupiter.api.Test
+ public void testIsMetric() throws SQLException {
+ assertTrue(DataBaseUtilities.isSridMetric(connection, 2154));
+ assertFalse(DataBaseUtilities.isSridMetric(connection, 4326));
+ assertTrue(DataBaseUtilities.isSridMetric(connection, 3857));
+ }
+}
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_BUILDINGS.geojson b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_BUILDINGS.geojson
new file mode 100644
index 000000000..ba2593ca6
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_BUILDINGS.geojson
@@ -0,0 +1,17 @@
+{
+"type": "FeatureCollection",
+"name": "SPARSE_BUILDINGS",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::25830" } },
+"features": [
+{ "type": "Feature", "properties": { "PK": 10969, "HEIGHT": 6.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 80161.860002701054327, 4833066.852778820320964 ], [ 80169.869061314035207, 4833066.454220755025744 ], [ 80169.470503664633725, 4833058.445162490010262 ], [ 80161.471440037305001, 4833058.84299670625478 ], [ 80161.860002701054327, 4833066.852778820320964 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 11051, "HEIGHT": 6.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 80177.683659280999564, 4833031.285758446902037 ], [ 80177.965240004006773, 4833036.289069429971278 ], [ 80183.469026502978522, 4833035.981329499743879 ], [ 80184.437674048764165, 4833052.981722740456462 ], [ 80190.441935708629899, 4833052.64782256539911 ], [ 80189.190984554530587, 4833030.63412518799305 ], [ 80177.683659280999564, 4833031.285758446902037 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 52002, "HEIGHT": 6.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 56080.236868263222277, 4771893.160226408392191 ], [ 56071.475574310403317, 4771894.732260803692043 ], [ 56073.798237371724099, 4771907.660668190568686 ], [ 56073.951942767540459, 4771908.544082844629884 ], [ 56075.092216819466557, 4771915.065067369490862 ], [ 56076.800851418287493, 4771924.89189463108778 ], [ 56085.436129299399909, 4771921.721435539424419 ], [ 56121.4263957234798, 4771908.54138657823205 ], [ 56119.843596900755074, 4771899.48942793905735 ], [ 56117.505210357252508, 4771886.4817562289536 ], [ 56110.360772794287186, 4771887.757118243724108 ], [ 56110.06837262964109, 4771886.059562499634922 ], [ 56109.254101179947611, 4771881.424673886038363 ], [ 56094.737408430024516, 4771884.021859549917281 ], [ 56095.62673041888047, 4771889.003113359212875 ], [ 56095.864077237143647, 4771890.352873080410063 ], [ 56080.236868263222277, 4771893.160226408392191 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 77856, "HEIGHT": 12.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5218.092880871030502, 4775419.805283922702074 ], [ -5215.316854651377071, 4775426.827393616549671 ], [ -5207.989677675417624, 4775423.536711522378027 ], [ -5210.928724291734397, 4775416.204454878345132 ], [ -5215.211501885496546, 4775418.331643437035382 ], [ -5218.092880871030502, 4775419.805283922702074 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 80384, "HEIGHT": 6.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5211.023607755894773, 4775497.437510111369193 ], [ -5210.515927983098663, 4775495.25897079333663 ], [ -5218.052182536222972, 4775493.526026429608464 ], [ -5218.342819418874569, 4775494.663189637474716 ], [ -5218.325772261829115, 4775494.669511063024402 ], [ -5212.272412438818719, 4775496.964290008880198 ], [ -5211.023607755894773, 4775497.437510111369193 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 80385, "HEIGHT": 9.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5204.591646414715797, 4775479.26609661616385 ], [ -5204.784337604069151, 4775477.278537680394948 ], [ -5217.863189820549451, 4775478.617902361787856 ], [ -5216.697159170755185, 4775489.979351364076138 ], [ -5214.980472064577043, 4775489.796010376885533 ], [ -5214.859439565509092, 4775490.923792188055813 ], [ -5213.332861687638797, 4775490.754072255454957 ], [ -5213.46246613265248, 4775489.647018854506314 ], [ -5200.064147841418162, 4775488.27470603492111 ], [ -5200.501085560128558, 4775483.860769413411617 ], [ -5205.422447767690755, 4775484.37429179623723 ], [ -5206.058906577178277, 4775479.421509193256497 ], [ -5204.591646414715797, 4775479.26609661616385 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 80386, "HEIGHT": 12.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5204.591646414715797, 4775479.26609661616385 ], [ -5206.058906577178277, 4775479.421509193256497 ], [ -5205.422447767690755, 4775484.37429179623723 ], [ -5200.501085560128558, 4775483.860769413411617 ], [ -5201.016758459329139, 4775478.909390083514154 ], [ -5204.591646414715797, 4775479.26609661616385 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 82367, "HEIGHT": 6.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -5167.123484180134255, 4775458.006084401160479 ], [ -5172.095585513277911, 4775425.807474149391055 ], [ -5182.086837074253708, 4775427.42846579477191 ], [ -5176.837472065701149, 4775459.426186511293054 ], [ -5167.123484180134255, 4775458.006084401160479 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 123899, "HEIGHT": 9.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 23612.718799001420848, 4739509.195243808440864 ], [ 23617.618566322664265, 4739512.495669572614133 ], [ 23618.74634156859247, 4739510.826934972777963 ], [ 23622.10431598010473, 4739513.081025155261159 ], [ 23630.959969657997135, 4739499.933520769700408 ], [ 23622.702017646224704, 4739494.368965859524906 ], [ 23619.000866809568834, 4739499.860254124738276 ], [ 23612.718799001420848, 4739509.195243808440864 ] ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 124200, "HEIGHT": 9.0 }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 23610.638181187445298, 4739494.095555815845728 ], [ 23605.538662376406137, 4739501.542769785039127 ], [ 23605.342303414014168, 4739501.827689703553915 ], [ 23608.902476637915242, 4739504.217678520828485 ], [ 23607.765669232525397, 4739505.858404481783509 ], [ 23612.718799001420848, 4739509.195243808440864 ], [ 23619.000866809568834, 4739499.860254124738276 ], [ 23621.84367823402863, 4739495.642519230023026 ], [ 23622.702017646224704, 4739494.368965859524906 ], [ 23614.440120165236294, 4739488.544310073368251 ], [ 23613.609550614026375, 4739489.756068754941225 ], [ 23610.638181187445298, 4739494.095555815845728 ] ] ] ] } }
+]
+}
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_ROADS.geojson b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_ROADS.geojson
new file mode 100644
index 000000000..90cd47a4e
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/SPARSE_ROADS.geojson
@@ -0,0 +1,13 @@
+{
+"type": "FeatureCollection",
+"name": "SPARSE_ROADS",
+"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::25830" } },
+"features": [
+{ "type": "Feature", "properties": { "PK": 743, "LWD63": 80.3486155561326, "LWD125": 82.104336191012351, "LWD250": 80.78996746676458, "LWD500": 81.836515280118135, "LWD1000": 88.225840704315488, "LWD2000": 85.913447656227987, "LWD4000": 77.314256600968577, "LWD8000": 68.30124722811469, "LWE63": 80.076866302497066, "LWE125": 81.168523384491465, "LWE250": 80.077806804601323, "LWE500": 81.322883569696259, "LWE1000": 87.118596162986591, "LWE2000": 84.682493246479964, "LWE4000": 76.171564437338318, "LWE8000": 67.422480142111255, "LWN63": 74.632789126841459, "LWN125": 75.132279956060032, "LWN250": 74.26054232775536, "LWN500": 75.682530319356644, "LWN1000": 80.898352918914881, "LWN2000": 78.319617447395672, "LWN4000": 69.911339583674419, "LWN8000": 61.444236920757042 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 56052.517237801752344, 4772072.618361883796751, 0.05 ], [ 56047.884300000034273, 4771896.700200000777841, 0.05 ], [ 55970.999863491582801, 4771581.933159243315458, 0.05 ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 7107, "LWD63": 84.598149266889038, "LWD125": 79.113372452136659, "LWD250": 77.8589530401606, "LWD500": 78.441370335238474, "LWD1000": 81.751359167933828, "LWD2000": 78.414936879637565, "LWD4000": 70.805957145654347, "LWD8000": 63.309246099924721, "LWE63": 80.091537453027598, "LWE125": 74.846067618433082, "LWE250": 73.548395677861549, "LWE500": 74.228234577527161, "LWE1000": 78.056763989040164, "LWE2000": 74.84237869700334, "LWE4000": 67.012684863547378, "LWE8000": 59.223075815425616, "LWN63": 74.018401076213308, "LWN125": 69.141077717876385, "LWN250": 67.780588269503539, "LWN500": 68.599800820575936, "LWN1000": 73.060477331307411, "LWN2000": 69.972764248012538, "LWN4000": 61.908460874110474, "LWN8000": 53.76543902767483 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -5257.488900000229478, 4774948.592000000178814, 0.05 ], [ -5213.488800000399351, 4775309.595300000160933, 0.05 ], [ -5197.320968560166875, 4775444.958329167217016, 0.05 ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 7108, "LWD63": 84.598149266889038, "LWD125": 79.113372452136659, "LWD250": 77.8589530401606, "LWD500": 78.441370335238474, "LWD1000": 81.751359167933828, "LWD2000": 78.414936879637565, "LWD4000": 70.805957145654347, "LWD8000": 63.309246099924721, "LWE63": 80.091537453027598, "LWE125": 74.846067618433082, "LWE250": 73.548395677861549, "LWE500": 74.228234577527161, "LWE1000": 78.056763989040164, "LWE2000": 74.84237869700334, "LWE4000": 67.012684863547378, "LWE8000": 59.223075815425616, "LWN63": 74.018401076213308, "LWN125": 69.141077717876385, "LWN250": 67.780588269503539, "LWN500": 68.599800820575936, "LWN1000": 73.060477331307411, "LWN2000": 69.972764248012538, "LWN4000": 61.908460874110474, "LWN8000": 53.76543902767483 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -5197.320968560166875, 4775444.958329167217016, 0.05 ], [ -5170.489900000393391, 4775669.597899999469519, 0.05 ], [ -5076.786293628678322, 4775926.825857835821807, 0.05 ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 8440, "LWD63": 73.22441312882448, "LWD125": 66.343761542383547, "LWD250": 64.815629033394956, "LWD500": 65.236295458784753, "LWD1000": 67.725736434380451, "LWD2000": 64.121563640174756, "LWD4000": 57.244764210956077, "LWD8000": 49.86582315885709, "LWE63": 71.235132207640376, "LWE125": 64.302225825760601, "LWE250": 62.720487511378948, "LWE500": 63.322831913053925, "LWE1000": 66.472676902151392, "LWE2000": 62.994942650837757, "LWE4000": 55.850840020706968, "LWE8000": 48.152492104930602, "LWN63": 62.121388874725916, "LWN125": 54.985455812221396, "LWN250": 53.182334133593578, "LWN500": 54.483799094073369, "LWN1000": 59.368384544460959, "LWN2000": 56.133591827991829, "LWN4000": 48.449543544266767, "LWN8000": 39.945860560070322 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 23301.010099999606609, 4739127.30519999936223, 0.05 ], [ 23451.011400000192225, 4739357.307, 0.05 ], [ 23610.073733090513997, 4739517.01789503917098, 0.05 ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 8441, "LWD63": 73.22441312882448, "LWD125": 66.343761542383547, "LWD250": 64.815629033394956, "LWD500": 65.236295458784753, "LWD1000": 67.725736434380451, "LWD2000": 64.121563640174756, "LWD4000": 57.244764210956077, "LWD8000": 49.86582315885709, "LWE63": 71.235132207640376, "LWE125": 64.302225825760601, "LWE250": 62.720487511378948, "LWE500": 63.322831913053925, "LWE1000": 66.472676902151392, "LWE2000": 62.994942650837757, "LWE4000": 55.850840020706968, "LWE8000": 48.152492104930602, "LWN63": 62.121388874725916, "LWN125": 54.985455812221396, "LWN250": 53.182334133593578, "LWN500": 54.483799094073369, "LWN1000": 59.368384544460959, "LWN2000": 56.133591827991829, "LWN4000": 48.449543544266767, "LWN8000": 39.945860560070322 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 23610.073733090513997, 4739517.01789503917098, 0.05 ], [ 23800.424599999561906, 4739708.144899999722838, 0.05 ], [ 23916.336500000208616, 4739900.904400000348687, 0.05 ], [ 23918.969178123097663, 4739905.5361303742975, 0.05 ] ] ] } },
+{ "type": "Feature", "properties": { "PK": 10941, "LWD63": 87.828961208847133, "LWD125": 83.643041037189093, "LWD250": 82.350232552383716, "LWD500": 82.711616780159559, "LWD1000": 87.352956832089859, "LWD2000": 84.767909994842967, "LWD4000": 76.653055939564695, "LWD8000": 68.526339120124902, "LWE63": 88.509302532662204, "LWE125": 83.719278028229795, "LWE250": 82.513353790589733, "LWE500": 82.848522595280542, "LWE1000": 86.682160312701853, "LWE2000": 83.88426948939815, "LWE4000": 76.04011824560142, "LWE8000": 68.348526892483505, "LWN63": 83.518153310686628, "LWN125": 78.334266791290091, "LWN250": 77.190774653296472, "LWN500": 77.507331324692345, "LWN1000": 80.656222093991687, "LWN2000": 77.633069914846203, "LWN4000": 70.07163521081813, "LWN8000": 62.767922150702489 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ 80163.17578007587872, 4832824.531530040316284, 0.05 ], [ 80150.767599999904633, 4833080.2511, 0.05 ], [ 80170.591018238148536, 4833323.424005336128175, 0.05 ] ] ] } }
+]
+}
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/buildings.geojson.gz b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/buildings.geojson.gz
new file mode 100644
index 000000000..1fde10332
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/buildings.geojson.gz differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/sources.geojson.gz b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/sources.geojson.gz
new file mode 100644
index 000000000..ebb449cf5
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/delaunaytest/sources.geojson.gz differ
diff --git a/wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/RailTrack.cpg b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.cpg
similarity index 100%
rename from wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/RailTrack.cpg
rename to noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.cpg
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.dbf b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.dbf
new file mode 100644
index 000000000..9391869ed
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.dbf differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.prj b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.prj
new file mode 100644
index 000000000..12f46005d
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.prj
@@ -0,0 +1 @@
+PROJCS["WGS_1984_UTM_Zone_33N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.qmd b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.qmd
new file mode 100644
index 000000000..d686fc086
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.qmd
@@ -0,0 +1,27 @@
+
+
+
+
+
+ dataset
+
+
+
+
+
+
+
+
+ PROJCRS["WGS 84 / UTM zone 33N",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],MEMBER["World Geodetic System 1984 (G2296)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["UTM zone 33N",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",15,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Navigation and medium accuracy spatial referencing."],AREA["Between 12°E and 18°E, northern hemisphere between equator and 84°N, onshore and offshore. Austria. Bosnia and Herzegovina. Cameroon. Central African Republic. Chad. Congo. Croatia. Czechia. Democratic Republic of the Congo (Zaire). Gabon. Germany. Hungary. Italy. Libya. Malta. Niger. Nigeria. Norway. Poland. San Marino. Slovakia. Slovenia. Svalbard. Sweden. Vatican City State."],BBOX[0,12,84,18]],ID["EPSG",32633]]
+ +proj=utm +zone=33 +datum=WGS84 +units=m +no_defs
+ 3117
+ 32633
+ EPSG:32633
+ WGS 84 / UTM zone 33N
+ utm
+ EPSG:7030
+ false
+
+
+
+
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shp b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shp
new file mode 100644
index 000000000..79bc27ba1
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shp differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shx b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shx
new file mode 100644
index 000000000..332262646
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/buldings_test.shx differ
diff --git a/wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/buildings2.cpg b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.cpg
similarity index 100%
rename from wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/buildings2.cpg
rename to noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.cpg
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.dbf b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.dbf
new file mode 100644
index 000000000..8ba7eda24
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.dbf differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.prj b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.prj
new file mode 100644
index 000000000..12f46005d
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.prj
@@ -0,0 +1 @@
+PROJCS["WGS_1984_UTM_Zone_33N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.qmd b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.qmd
new file mode 100644
index 000000000..d686fc086
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.qmd
@@ -0,0 +1,27 @@
+
+
+
+
+
+ dataset
+
+
+
+
+
+
+
+
+ PROJCRS["WGS 84 / UTM zone 33N",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],MEMBER["World Geodetic System 1984 (G2296)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["UTM zone 33N",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",15,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Navigation and medium accuracy spatial referencing."],AREA["Between 12°E and 18°E, northern hemisphere between equator and 84°N, onshore and offshore. Austria. Bosnia and Herzegovina. Cameroon. Central African Republic. Chad. Congo. Croatia. Czechia. Democratic Republic of the Congo (Zaire). Gabon. Germany. Hungary. Italy. Libya. Malta. Niger. Nigeria. Norway. Poland. San Marino. Slovakia. Slovenia. Svalbard. Sweden. Vatican City State."],BBOX[0,12,84,18]],ID["EPSG",32633]]
+ +proj=utm +zone=33 +datum=WGS84 +units=m +no_defs
+ 3117
+ 32633
+ EPSG:32633
+ WGS 84 / UTM zone 33N
+ utm
+ EPSG:7030
+ false
+
+
+
+
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shp b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shp
new file mode 100644
index 000000000..a56535aee
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shp differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shx b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shx
new file mode 100644
index 000000000..c5a763612
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/receivers_test.shx differ
diff --git a/wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/receivers_Railway_.cpg b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.cpg
similarity index 100%
rename from wps_scripts/src/test/resources/org/noise_planet/noisemodelling/wps/Train/receivers_Railway_.cpg
rename to noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.cpg
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.dbf b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.dbf
new file mode 100644
index 000000000..4d2277170
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.dbf differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.prj b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.prj
new file mode 100644
index 000000000..12f46005d
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.prj
@@ -0,0 +1 @@
+PROJCS["WGS_1984_UTM_Zone_33N",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",500000.0],PARAMETER["False_Northing",0.0],PARAMETER["Central_Meridian",15.0],PARAMETER["Scale_Factor",0.9996],PARAMETER["Latitude_Of_Origin",0.0],UNIT["Meter",1.0]]
\ No newline at end of file
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.qmd b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.qmd
new file mode 100644
index 000000000..4af55e635
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.qmd
@@ -0,0 +1,44 @@
+
+
+
+
+
+ dataset
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PROJCRS["WGS 84 / UTM zone 33N",BASEGEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],MEMBER["World Geodetic System 1984 (G2296)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4326]],CONVERSION["UTM zone 33N",METHOD["Transverse Mercator",ID["EPSG",9807]],PARAMETER["Latitude of natural origin",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8801]],PARAMETER["Longitude of natural origin",15,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8802]],PARAMETER["Scale factor at natural origin",0.9996,SCALEUNIT["unity",1],ID["EPSG",8805]],PARAMETER["False easting",500000,LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["(N)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Navigation and medium accuracy spatial referencing."],AREA["Between 12°E and 18°E, northern hemisphere between equator and 84°N, onshore and offshore. Austria. Bosnia and Herzegovina. Cameroon. Central African Republic. Chad. Congo. Croatia. Czechia. Democratic Republic of the Congo (Zaire). Gabon. Germany. Hungary. Italy. Libya. Malta. Niger. Nigeria. Norway. Poland. San Marino. Slovakia. Slovenia. Svalbard. Sweden. Vatican City State."],BBOX[0,12,84,18]],ID["EPSG",32633]]
+ +proj=utm +zone=33 +datum=WGS84 +units=m +no_defs
+ 3117
+ 32633
+ EPSG:32633
+ WGS 84 / UTM zone 33N
+ utm
+ EPSG:7030
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shp b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shp
new file mode 100644
index 000000000..2595f7372
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shp differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shx b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shx
new file mode 100644
index 000000000..693082172
Binary files /dev/null and b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/dynamicConfMaxErrorTest/sources_test.shx differ
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/scenario_skip_far_source.sql b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/scenario_skip_far_source.sql
index 0d0cabd37..26225ccea 100644
--- a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/scenario_skip_far_source.sql
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/scenario_skip_far_source.sql
@@ -37,7 +37,7 @@ VALUES(7482, 'SRID=2154; LINESTRING Z(376800.1 6667058.5 0.05, 376839.2 6667115.
DROP TABLE IF EXISTS RECEIVERS;
CREATE TABLE RECEIVERS(the_geom GEOMETRY(POINTZ), GID SERIAL PRIMARY KEY);
-INSERT INTO RECEIVERS(the_geom) VALUES ('SRID=2154; POINTZ(375773.1272896934 6667129.820221064 4)');
+INSERT INTO RECEIVERS(the_geom) VALUES ('SRID=2154; POINTZ(375821.14511295804 6667083.880145698 4)');
CREATE TABLE PUBLIC.BUILDINGS (
diff --git a/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/testGeometryWithLWFields.sql b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/testGeometryWithLWFields.sql
new file mode 100644
index 000000000..5f0a8a340
--- /dev/null
+++ b/noisemodelling-jdbc/src/test/resources/org/noise_planet/noisemodelling/jdbc/testGeometryWithLWFields.sql
@@ -0,0 +1,47 @@
+CREATE CACHED TABLE "PUBLIC"."SOURCES"(
+ "PK" INTEGER NOT NULL PRIMARY KEY,
+ "THE_GEOM" GEOMETRY,
+ "HZ50" DOUBLE PRECISION,
+ "HZ63" DOUBLE PRECISION,
+ "HZ80" DOUBLE PRECISION,
+ "HZ100" DOUBLE PRECISION,
+ "HZ125" DOUBLE PRECISION,
+ "HZ160" DOUBLE PRECISION,
+ "HZ200" DOUBLE PRECISION,
+ "HZ250" DOUBLE PRECISION,
+ "HZ315" DOUBLE PRECISION,
+ "HZ400" DOUBLE PRECISION,
+ "HZ500" DOUBLE PRECISION,
+ "HZ630" DOUBLE PRECISION,
+ "HZ800" DOUBLE PRECISION,
+ "HZ1000" DOUBLE PRECISION,
+ "HZ1250" DOUBLE PRECISION,
+ "HZ1600" DOUBLE PRECISION,
+ "HZ2000" DOUBLE PRECISION,
+ "HZ2500" DOUBLE PRECISION,
+ "HZ3150" DOUBLE PRECISION,
+ "HZ4000" DOUBLE PRECISION,
+ "HZ5000" DOUBLE PRECISION,
+ "HZ6300" DOUBLE PRECISION,
+ "HZ8000" DOUBLE PRECISION,
+ "HZ10000" DOUBLE PRECISION
+);
+
+INSERT INTO SOURCES VALUES (1, 'POINTZ (304149 2252906 0.2)', 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100);
+
+-- Buildings table
+drop table if exists buildings;
+create table buildings ( the_geom GEOMETRY, height double );
+insert into buildings values
+('POLYGON ((304081.971140762 2252842.2377583543, 304087.2927055786 2252852.3894956196, 304120.9740158394 2252833.548805693, 304115.1308573535 2252825.994945729, 304111.00898608495 2252828.2631819816, 304109.4290281007 2252825.6481492408, 304098.86882524966 2252831.967185081, 304100.45122728107 2252834.281990951, 304081.971140762 2252842.2377583543))', 18),
+('POLYGON ((304066.507676677 2252934.287102647, 304066.8030005697 2252934.890006138, 304080.97572407214 2252927.3994797524, 304076.9437141237 2252918.6594214477, 304071.7177355701 2252921.3189708022, 304070.2378479165 2252918.7047548825, 304067.5243737129 2252920.0837501315, 304069.2019599965 2252922.9998262255, 304066.58774516406 2252924.4797140043, 304065.2079349268 2252921.866315325, 304062.0925250804 2252923.4421931794, 304061.1885783817 2252923.8351413365, 304066.507676677 2252934.287102647))', 6),
+('POLYGON ((304116.64878738014 2252885.2559329206, 304110.6548854257 2252883.905929764, 304107.2804970879 2252892.685601917, 304111.92112771556 2252900.4298086576, 304118.5497980566 2252897.5815475294, 304112.3680011234 2252894.728779357, 304116.64878738014 2252885.2559329206))', 4),
+('POLYGON ((304116.8496270616 2252909.677588094, 304135.2385924306 2252900.620172194, 304130.6061355095 2252891.875211711, 304116.6486897959 2252885.2559239715, 304112.3678984563 2252894.7287699394, 304118.54969539 2252897.5815381147, 304111.9210250483 2252900.42979924, 304116.8496270616 2252909.677588094))', 14),
+('POLYGON ((304147.06500050006 2252935.3451201664, 304148.63721490436 2252926.6508168606, 304144.60684562515 2252917.710608132, 304115.06539856613 2252932.0814030115, 304121.1687605225 2252944.541408861, 304142.47134331334 2252934.106631618, 304147.06500050006 2252935.3451201664))', 15),
+('POLYGON ((304025.02294641937 2252903.723622542, 304025.4158539388 2252904.6275655366, 304026.60040863417 2252906.6388771087, 304028.6742245948 2252910.05859954, 304055.60392891726 2252896.867466023, 304063.6704399612 2252914.0473602274, 304065.45140236564 2252916.56395479, 304073.6910965696 2252912.527862693, 304054.0027388107 2252872.33428636, 304018.5265914384 2252890.3596659536, 304025.02294641937 2252903.723622542))', 14);
+
+-- Receivers table
+
+CREATE TABLE RECEIVERS(the_geom GEOMETRY(POINTZ), GID SERIAL PRIMARY KEY);
+INSERT INTO RECEIVERS(the_geom) VALUES ('POINTZ(304100 2252900 1.6)');
\ No newline at end of file
diff --git a/noisemodelling-pathfinder/pom.xml b/noisemodelling-pathfinder/pom.xml
index 555cd047c..79dd91ec8 100644
--- a/noisemodelling-pathfinder/pom.xml
+++ b/noisemodelling-pathfinder/pom.xml
@@ -10,7 +10,7 @@
org.noise-planet
noisemodelling-parent
- 5.0.2-SNAPSHOT
+ 6.0.1-SNAPSHOT
../pom.xml
Compute sound propagation rays.
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/PathFinder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/PathFinder.java
index 4133597ce..1cc1defc1 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/PathFinder.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/PathFinder.java
@@ -24,10 +24,8 @@
import org.noise_planet.noisemodelling.pathfinder.path.MirrorReceiversCompute;
import org.noise_planet.noisemodelling.pathfinder.path.MirrorReceiver;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.*;
-import org.noise_planet.noisemodelling.pathfinder.utils.geometry.CurvedProfileGenerator;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.Orientation;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.JTSUtility;
-import org.noise_planet.noisemodelling.pathfinder.utils.geometry.QueryRTree;
import org.noise_planet.noisemodelling.pathfinder.utils.profiler.ProfilerThread;
import org.noise_planet.noisemodelling.pathfinder.utils.profiler.ReceiverStatsMetric;
import org.slf4j.Logger;
@@ -42,7 +40,6 @@
import static java.lang.Double.isNaN;
import static java.lang.Math.*;
import static org.noise_planet.noisemodelling.pathfinder.PathFinder.ComputationSide.LEFT;
-import static org.noise_planet.noisemodelling.pathfinder.PathFinder.ComputationSide.RIGHT;
/**
* @author Nicolas Fortin
@@ -489,8 +486,6 @@ public List computeSideHull(boolean left, Coordinate p1, Coordinate
int indexp1 = 0;
int indexp2 = 0;
- boolean convexHullIntersects = true;
-
input.add(p1);
input.add(p2);
@@ -506,81 +501,57 @@ public List computeSideHull(boolean left, Coordinate p1, Coordinate
data.profileBuilder.getWallsOnPath(p1, p2, buildingIntersectionPathVisitor);
int k;
- while (convexHullIntersects) {
- ConvexHull convexHull = new ConvexHull(input.toArray(new Coordinate[0]), GEOMETRY_FACTORY);
- Geometry convexhull = convexHull.getConvexHull();
-
- coordinates = convexhull.getCoordinates();
- // for the length we do not count the return ray from receiver to source (closed polygon here)
- double convexHullLength = Length.ofLine(
- CoordinateArraySequenceFactory.instance()
- .create(Arrays.copyOfRange(coordinates, 0, coordinates.length - 1)));
- if (convexHullLength / p1.distance(p2) > MAX_RATIO_HULL_DIRECT_PATH ||
- convexHullLength >= data.maxSrcDist) {
- return new ArrayList<>();
- }
- convexHullIntersects = false;
+ ConvexHull convexHull = new ConvexHull(input.toArray(new Coordinate[0]), GEOMETRY_FACTORY);
+ Geometry convexhull = convexHull.getConvexHull();
- input.clear();
- input.addAll(Arrays.asList(coordinates));
-
- indexp1 = -1;
- for (int i = 0; i < coordinates.length - 1; i++) {
- if (coordinates[i].equals(p1)) {
- indexp1 = i;
- break;
- }
- }
- if (indexp1 == -1) {
- // P1 does not belong to convex vertices, cannot compute diffraction
- // TODO handle concave path
- return new ArrayList<>();
- }
- // Transform array to set p1 at index=0
- Coordinate[] coordinatesShifted = new Coordinate[coordinates.length];
- // Copy from P1 to end in beginning of new array
- int len = (coordinates.length - 1) - indexp1;
- System.arraycopy(coordinates, indexp1, coordinatesShifted, 0, len);
- // Copy from 0 to P1 in the end of array
- System.arraycopy(coordinates, 0, coordinatesShifted, len, coordinates.length - len - 1);
- coordinatesShifted[coordinatesShifted.length - 1] = coordinatesShifted[0];
- coordinates = coordinatesShifted;
- indexp1 = 0;
- indexp2 = -1;
- for (int i = 1; i < coordinates.length - 1; i++) {
- if (coordinates[i].equals(p2)) {
- indexp2 = i;
- break;
- }
- }
- if (indexp2 == -1) {
- // P2 does not belong to convex vertices, cannot compute diffraction
- // TODO handle concave path
- return new ArrayList<>();
- }
- for (k = 0; k < coordinates.length - 1; k++) {
- LineSegment freeFieldTestSegment = new LineSegment(coordinates[k], coordinates[k + 1]);
-
- // Ignore intersection if iterating over other side (not parts of what is returned)
- if (left && k < indexp2 || !left && k >= indexp2) {
- if (!freeFieldSegments.contains(freeFieldTestSegment)) {
-
- int inputPointsBefore = input.size();
+ coordinates = convexhull.getCoordinates();
+ // for the length we do not count the return ray from receiver to source (closed polygon here)
+ double convexHullLength = Length.ofLine(
+ CoordinateArraySequenceFactory.instance()
+ .create(Arrays.copyOfRange(coordinates, 0, coordinates.length - 1)));
+ if (convexHullLength / p1.distance(p2) > MAX_RATIO_HULL_DIRECT_PATH ||
+ convexHullLength >= data.maxSrcDist) {
+ return new ArrayList<>();
+ }
- // Visit buildings that are between the provided hull points
- data.profileBuilder.getWallsOnPath(coordinates[k], coordinates[k + 1], buildingIntersectionPathVisitor);
+ input.clear();
+ input.addAll(Arrays.asList(coordinates));
- if (inputPointsBefore == input.size()) {
- freeFieldSegments.add(freeFieldTestSegment);
- } else {
- convexHullIntersects = true;
- break;
- }
- }
- }
+ indexp1 = -1;
+ for (int i = 0; i < coordinates.length - 1; i++) {
+ if (coordinates[i].equals(p1)) {
+ indexp1 = i;
+ break;
}
}
+ if (indexp1 == -1) {
+ // P1 does not belong to convex vertices, cannot compute diffraction
+ // TODO handle concave path
+ return new ArrayList<>();
+ }
+ // Transform array to set p1 at index=0
+ Coordinate[] coordinatesShifted = new Coordinate[coordinates.length];
+ // Copy from P1 to end in beginning of new array
+ int len = (coordinates.length - 1) - indexp1;
+ System.arraycopy(coordinates, indexp1, coordinatesShifted, 0, len);
+ // Copy from 0 to P1 in the end of array
+ System.arraycopy(coordinates, 0, coordinatesShifted, len, coordinates.length - len - 1);
+ coordinatesShifted[coordinatesShifted.length - 1] = coordinatesShifted[0];
+ coordinates = coordinatesShifted;
+ indexp1 = 0;
+ indexp2 = -1;
+ for (int i = 1; i < coordinates.length - 1; i++) {
+ if (coordinates[i].equals(p2)) {
+ indexp2 = i;
+ break;
+ }
+ }
+ if (indexp2 == -1) {
+ // P2 does not belong to convex vertices, cannot compute diffraction
+ // TODO handle concave path
+ return new ArrayList<>();
+ }
// restore coordinates order from source to receiver
if (left) {
@@ -830,11 +801,14 @@ public static double splitLineStringIntoPoints(LineString geom, double segmentSi
// Return mid point
Coordinate[] points = geom.getCoordinates();
double segmentLength = 0;
- final double targetSegmentSize = geomLength / 2.0;
+ final double targetSegmentSize = geomLength / 2.0; // take middle point
for (int i = 0; i < points.length - 1; i++) {
Coordinate a = points[i];
final Coordinate b = points[i + 1];
double length = a.distance3D(b);
+ if(Double.isNaN(length)) {
+ length = a.distance(b);
+ }
if (length + segmentLength > targetSegmentSize) {
double segmentLengthFraction = (targetSegmentSize - segmentLength) / length;
Coordinate midPoint = new Coordinate(a.x + segmentLengthFraction * (b.x - a.x),
@@ -847,7 +821,7 @@ public static double splitLineStringIntoPoints(LineString geom, double segmentSi
}
return geom.getLength();
} else {
- double targetSegmentSize = geomLength / ceil(geomLength / segmentSizeConstraint);
+ double targetSegmentSize = Math.max(1, geomLength / ceil(geomLength / segmentSizeConstraint));
Coordinate[] points = geom.getCoordinates();
double segmentLength = 0.;
@@ -897,110 +871,6 @@ public static double splitLineStringIntoPoints(LineString geom, double segmentSi
}
- /**
- * Apply a linestring over the digital elevation model by offsetting the z value with the ground elevation.
- * @param lineString
- * @param profileBuilder
- * @param epsilon ignore elevation point where linear interpolation distance is inferior that this value
- * @return computed lineString
- */
- private static LineString splitLineSource(LineString lineString, ProfileBuilder profileBuilder, double epsilon) {
- boolean warned = false;
- ArrayList newGeomCoordinates = new ArrayList<>();
- Coordinate[] coordinates = lineString.getCoordinates();
- for(int idPoint = 0; idPoint < coordinates.length - 1; idPoint++) {
- Coordinate p0 = coordinates[idPoint];
- Coordinate p1 = coordinates[idPoint + 1];
- List groundProfileCoordinates = new ArrayList<>();
- profileBuilder.fetchTopographicProfile(groundProfileCoordinates, p0, p1, false);
- newGeomCoordinates.ensureCapacity(newGeomCoordinates.size() + groundProfileCoordinates.size());
- if(groundProfileCoordinates.size() < 2) {
- if(profileBuilder.hasDem()) {
- if(!warned) {
- LOGGER.warn( "Source line out of DEM area {}",
- new WKTWriter(3).write(lineString));
- warned = true;
- }
- }
- newGeomCoordinates.add(p0);
- newGeomCoordinates.add(p1);
- } else {
- if (idPoint == 0) {
- newGeomCoordinates.add(new Coordinate(p0.x, p0.y, p0.z + groundProfileCoordinates.get(0).z));
- }
- Coordinate previous = groundProfileCoordinates.get(0);
- for (int groundPoint = 1; groundPoint < groundProfileCoordinates.size() - 1; groundPoint++) {
- final Coordinate current = groundProfileCoordinates.get(groundPoint);
- final Coordinate next = groundProfileCoordinates.get(groundPoint + 1);
- // Do not add topographic points which are simply the linear interpolation between two points
- // triangulation add a lot of interpolated lines from line segment DEM
- if (CGAlgorithms3D.distancePointSegment(current, previous, next) >= epsilon) {
- // interpolate the Z (height) values of the source then add the altitude
- previous = current;
- newGeomCoordinates.add(
- new Coordinate(current.x, current.y, current.z + Vertex.interpolateZ(current, p0, p1)));
- }
- }
- newGeomCoordinates.add(new Coordinate(p1.x, p1.y, p1.z +
- groundProfileCoordinates.get(groundProfileCoordinates.size() - 1).z));
- }
- }
- return GEOMETRY_FACTORY.createLineString(newGeomCoordinates.toArray(new Coordinate[0]));
- }
-
- /**
- * Update ground Z coordinates of sound sources absolute to sea levels
- */
- public void makeSourceRelativeZToAbsolute() {
- List sourceCopy = new ArrayList<>(data.sourceGeometries.size());
- for (Geometry source : data.sourceGeometries) {
- Geometry offsetGeometry;
- if (source instanceof LineString) {
- offsetGeometry = splitLineSource((LineString) source, data.profileBuilder, ProfileBuilder.MILLIMETER);
- } else if (source instanceof MultiLineString) {
- LineString[] newGeom = new LineString[source.getNumGeometries()];
- for (int idGeom = 0; idGeom < source.getNumGeometries(); idGeom++) {
- newGeom[idGeom] = splitLineSource((LineString) source.getGeometryN(idGeom),
- data.profileBuilder, ProfileBuilder.MILLIMETER);
- }
- offsetGeometry = GEOMETRY_FACTORY.createMultiLineString(newGeom);
- } else if (source instanceof Point) {
- Coordinate sourceCoord = source.getCoordinate();
- offsetGeometry = GEOMETRY_FACTORY.createPoint(new Coordinate(sourceCoord.x, sourceCoord.y,
- sourceCoord.z + data.profileBuilder.getZGround(sourceCoord)));
- // Check if the source is into a building
- Building building = data.profileBuilder.getBuildingAtCoordinate(sourceCoord);
- if(building != null && building.getHeight() >= sourceCoord.z) {
- LOGGER.warn("Point source has been ignored as it is inside a building (building height {} m), it should be moved higher SOURCE: {}",
- building.getHeight(),new WKTWriter(3).write(source));
- continue;
- }
- } else {
- throw new IllegalArgumentException("Unsupported source geometry " + source.getGeometryType());
- }
- // Offset the geometry with value of elevation for each coordinate
- sourceCopy.add(offsetGeometry);
- }
- data.setSources(sourceCopy);
- }
-
- /**
- * Update ground Z coordinates of sound sources and receivers absolute to sea levels
- */
- public void makeRelativeZToAbsolute() {
- makeSourceRelativeZToAbsolute();
- makeReceiverRelativeZToAbsolute();
- }
-
- /**
- * Update ground Z coordinates of receivers absolute to sea levels
- */
- public void makeReceiverRelativeZToAbsolute() {
- for(Coordinate receiver : data.receivers) {
- receiver.setZ(receiver.getZ() + data.profileBuilder.getZGround(receiver));
- }
- }
-
/**
* Compute li to equation 4.1 NMPB 2008 (June 2009)
* @param source
@@ -1012,9 +882,9 @@ public void makeReceiverRelativeZToAbsolute() {
private void addLineSource(LineString source, Coordinate receiverCoord, int srcIndex, List sourceList) {
ArrayList pts = new ArrayList<>();
Coordinate nearestPoint = JTSUtility.getNearestPoint(receiverCoord, source);
- double segmentSizeConstraint = max(1, receiverCoord.distance3D(nearestPoint) / 2.0);
+ double segmentSizeConstraint = max(1, receiverCoord.distance3D(nearestPoint) / this.data.lineSourceSpacingRatio);
if (isNaN(segmentSizeConstraint)) {
- segmentSizeConstraint = max(1, receiverCoord.distance(nearestPoint) / 2.0);
+ segmentSizeConstraint = max(1, receiverCoord.distance(nearestPoint) / this.data.lineSourceSpacingRatio);
}
double li = splitLineStringIntoPoints(source, segmentSizeConstraint, pts);
for (int ptIndex = 0; ptIndex < pts.size(); ptIndex++) {
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/LayerTinfour.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/LayerTinfour.java
index 4fc28b710..d30355e2a 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/LayerTinfour.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/LayerTinfour.java
@@ -12,37 +12,55 @@
import org.locationtech.jts.algorithm.Orientation;
import org.locationtech.jts.geom.*;
+import org.locationtech.jts.geom.prep.PreparedPolygon;
import org.locationtech.jts.index.quadtree.Quadtree;
+import org.locationtech.jts.index.strtree.STRtree;
+import org.locationtech.jts.io.ParseException;
+import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.tinfour.common.*;
+import org.tinfour.common.IConstraint;
+import org.tinfour.common.LinearConstraint;
+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.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
+import java.io.*;
import java.util.*;
+import java.util.stream.IntStream;
+import java.util.stream.StreamSupport;
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 = "";
-
List constraints = new ArrayList<>();
- List constraintIndex = new ArrayList<>();
-
Quadtree ptsIndex = new Quadtree();
private boolean computeNeighbors = false;
private double maxArea = 0;
-
+ private boolean verbose = true;
// 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 List vertices = new ArrayList<>();
+ private final List triangles = new ArrayList();
+ private final List neighbors = new ArrayList(); // The first neighbor triangle is opposite the first corner of triangle i
+ /**
+ * RTree for polygon spatial index
+ */
+ private final STRtree polygonRtree = new STRtree();
+ /**
+ * Use PreparedPolygon for faster point-in-polygon tests
+ */
+ private final Map polygonMap = new HashMap<>();
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
/**
*
@@ -71,18 +89,6 @@ private Vertex addCoordinate(Coordinate coordinate, int index) {
}
- /**
- *
- * @param incrementalTin
- * @return
- */
- private List computeTriangles(IncrementalTin incrementalTin) {
- ArrayList triangles = new ArrayList<>(incrementalTin.countTriangles().getCount());
- Triangle.TriangleBuilder triangleBuilder = new Triangle.TriangleBuilder(triangles);
- TriangleCollector.visitSimpleTriangles(incrementalTin, triangleBuilder);
- return triangles;
- }
-
/**
* @return When an exception occur, this folder with receiver the input data
*/
@@ -127,11 +133,11 @@ private static Coordinate getCentroid(SimpleTriangle triangle) {
return new Coordinate( cx, cy, cz);
}
- public void dumpData() {
+ public void dumpData(File destinationCsv) {
GeometryFactory factory = new GeometryFactory();
WKTWriter wktWriter = new WKTWriter(3);
try {
- try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(dumpFolder, "tinfour_dump.csv")))) {
+ try (BufferedWriter writer = new BufferedWriter(new FileWriter(destinationCsv))) {
for(Object vObj : ptsIndex.queryAll()) {
if(vObj instanceof Vertex) {
final Vertex v = (Vertex)vObj;
@@ -174,72 +180,111 @@ public void dumpData() {
}
}
+ /**
+ * Find polygon index by point
+ * @param point Point to search
+ * @return Polygon index or -1 if not found
+ */
+ public int findPolygonIndexByPoint(Point point) {
+ for(Integer polygonIndex : (List)polygonRtree.query(point.getEnvelopeInternal())) {
+ PreparedPolygon polygon = polygonMap.get(polygonIndex);
+ if(polygon.contains(point)) {
+ return polygonIndex;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Read dump file
+ * @param dumpPath Path to dump file
+ * @throws IOException Read error
+ * @throws LayerDelaunayError Error in delaunay processing
+ * @throws ParseException Error in WKT parsing
+ */
+ public void readDump(File dumpPath) throws IOException, LayerDelaunayError, ParseException {
+ WKTReader wktReader = new WKTReader();
+ 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) {
+ addVertex(obj.getCoordinate());
+ } else if(obj instanceof Polygon) {
+ addPolygon((Polygon)obj, index++);
+ } else if (obj instanceof LineString) {
+ addLineString((LineString)obj, index++);
+ }
+ }
+ }
+ }
+
/**
* Launch delaunay process
*/
@Override
public void processDelaunay() throws LayerDelaunayError {
+ polygonRtree.build(); // build the rtree as it must not be built when using a multi-thread query
triangles.clear();
vertices.clear();
- List meshPoints = ptsIndex.queryAll();
-
IncrementalTin tin;
boolean refine;
- List simpleTriangles = new ArrayList<>();
- do {
- // Triangulate
- tin = new IncrementalTin();
- // Add points
- tin.add(meshPoints, null);
- // Add constraints
- try {
- tin.addConstraints(constraints, false);
- }catch (IllegalStateException ex) {
- // Got error
- // Dump input data
- if(!dumpFolder.isEmpty()) {
- dumpData();
- }
- throw new LayerDelaunayError(ex);
+ // Triangulate
+ tin = new IncrementalTin(epsilon);
+ // Add points
+ tin.add(ptsIndex.queryAll(), null);
+ // Add constraints
+ try {
+ tin.addConstraints(constraints, false);
+ }catch (IllegalStateException ex) {
+ // Got error
+ // Dump input data
+ if(!dumpFolder.isEmpty()) {
+ dumpData(new File(dumpFolder, "tinfour_dump.csv"));
}
- refine = false;
+ throw new LayerDelaunayError(ex);
+ }
- simpleTriangles = computeTriangles(tin);
+ do {
+ refine = false;
// Will triangulate multiple time if refinement is necessary
- if(maxArea > 0) {
- for (SimpleTriangle triangle : simpleTriangles) {
- if(triangle.getArea() > maxArea) {
- // Insert steiner point in circumcircle
- Coordinate centroid = getCentroid(triangle);
- meshPoints.add(new Vertex(centroid.x, centroid.y, centroid.z));
- refine = true;
+ if (maxArea > 0) {
+ ArrayList newSteinerPoints = StreamSupport.stream(tin.triangles().spliterator(), true)
+ .filter(triangle -> triangle.getArea() > maxArea)
+ .map(SimpleTriangle::getCentroid)
+ .collect(java.util.stream.Collectors.toCollection(ArrayList::new));
+ if (!newSteinerPoints.isEmpty()) {
+ tin.add(newSteinerPoints, null);
+ refine = true;
+ if (verbose) {
+ LOGGER.info("Refining Delaunay with {} points", newSteinerPoints.size());
}
}
}
} while (refine);
List verts = tin.getVertices();
- vertices = new ArrayList<>(verts.size());
Map vertIndex = new HashMap<>();
- for(Vertex v : verts) {
- vertIndex.put(v, vertices.size());
- vertices.add(toCoordinate(v));
+ this.vertices = new ArrayList<>(verts.size());
+ for (int i = 0; i < verts.size(); i++) {
+ Vertex v = verts.get(i);
+ vertIndex.put(v, i);
+ this.vertices.add(toCoordinate(v));
}
+ // Collect triangles but with tin index of vertices
Map edgeIndexToTriangleIndex = new HashMap<>();
- for(SimpleTriangle t : simpleTriangles) {
- int triangleAttribute = 0;
- if(t.getContainingRegion() != null) {
- if(t.getContainingRegion().getConstraintIndex() < constraintIndex.size()) {
- triangleAttribute = constraintIndex.get(t.getContainingRegion().getConstraintIndex());
- }
+ for(SimpleTriangle t : tin.triangles()) {
+ Triangle newTriangle = new Triangle(vertIndex.get(t.getVertexA()), vertIndex.get(t.getVertexB()), vertIndex.get(t.getVertexC()), 0);
+ triangles.add(newTriangle);
+ if(computeNeighbors) {
+ edgeIndexToTriangleIndex.put(t.getEdgeA().getIndex(), triangles.size() - 1);
+ edgeIndexToTriangleIndex.put(t.getEdgeB().getIndex(), triangles.size() - 1);
+ edgeIndexToTriangleIndex.put(t.getEdgeC().getIndex(), triangles.size() - 1);
}
- 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);
}
if(computeNeighbors) {
- for(SimpleTriangle t : simpleTriangles) {
+ for(SimpleTriangle t : tin.triangles()) {
Integer neighA = edgeIndexToTriangleIndex.get(t.getEdgeA().getDual().getIndex());
Integer neighB = edgeIndexToTriangleIndex.get(t.getEdgeB().getDual().getIndex());
Integer neighC = edgeIndexToTriangleIndex.get(t.getEdgeC().getDual().getIndex());
@@ -248,8 +293,24 @@ public void processDelaunay() throws LayerDelaunayError {
neighC != null ? neighC : -1));
}
}
+ // Update triangle polygon association
+ if(!polygonMap.isEmpty()) {
+ GeometryFactory gf = new GeometryFactory();
+ IntStream.range(0, triangles.size()).parallel().forEach(i -> {
+ Triangle triangle = triangles.get(i);
+ // Look for associated polygon area
+ Coordinate inCenter = org.locationtech.jts.geom.Triangle.inCentre(toCoordinate(verts.get(triangle.getA())),
+ toCoordinate(verts.get(triangle.getB())), toCoordinate(verts.get(triangle.getC())));
+ Point inCenterPoint = gf.createPoint(inCenter);
+ int polygonIndex = findPolygonIndexByPoint(inCenterPoint);
+ if (polygonIndex != -1) {
+ triangle.setAttribute(polygonIndex);
+ }
+ });
+ }
}
+
/**
* Append a polygon into the triangulation
*
@@ -272,27 +333,27 @@ public void addPolygon(Polygon newPoly, int buildingId) throws LayerDelaunayErro
polygonConstraint.complete();
if(polygonConstraint.isValid()) {
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);
- final Coordinate[] hCoordinates = holeLine.getCoordinates();
- // Holes must be CW
- if(Orientation.isCCW(hCoordinates)) {
- CoordinateArrays.reverse(hCoordinates);
- }
- List vertexList = new ArrayList<>(hCoordinates.length);
- for(int vId = 0; vId < hCoordinates.length - 1 ; vId++) {
- vertexList.add(addCoordinate(hCoordinates[vId], buildingId));
- }
- PolygonConstraint polygonConstraint = new PolygonConstraint(vertexList);
- polygonConstraint.complete();
- if(polygonConstraint.isValid()) {
- constraints.add(polygonConstraint);
- constraintIndex.add(buildingId);
+ polygonRtree.insert(newPoly.getEnvelopeInternal(), buildingId);
+ polygonMap.put(buildingId, new PreparedPolygon(newPoly));
+ // Append holes
+ final int holeCount = newPoly.getNumInteriorRing();
+ for (int holeIndex = 0; holeIndex < holeCount; holeIndex++) {
+ LineString holeLine = newPoly.getInteriorRingN(holeIndex);
+ final Coordinate[] hCoordinates = holeLine.getCoordinates();
+ // Holes must be CW
+ if(Orientation.isCCW(hCoordinates)) {
+ CoordinateArrays.reverse(hCoordinates);
+ }
+ vertexList = new ArrayList<>(hCoordinates.length);
+ for(int vId = 0; vId < hCoordinates.length - 1 ; vId++) {
+ vertexList.add(addCoordinate(hCoordinates[vId], buildingId));
+ }
+ polygonConstraint = new PolygonConstraint(vertexList);
+ polygonConstraint.complete();
+ if(polygonConstraint.isValid()) {
+ constraints.add(polygonConstraint);
+ }
+ }
}
}
}
@@ -356,7 +417,6 @@ public void addLineString(LineString lineToProcess, int buildingID) throws Layer
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/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/Triangle.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/Triangle.java
index 7d099c079..7eaeb4e59 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/Triangle.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/delaunay/Triangle.java
@@ -9,6 +9,8 @@
package org.noise_planet.noisemodelling.pathfinder.delaunay;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Polygon;
import org.tinfour.common.SimpleTriangle;
import java.util.ArrayList;
@@ -16,93 +18,101 @@
/**
* A triangle built from the combination of the 3 vertices index.
- *
+ *
* @author Nicolas Fortin
*/
public class Triangle {
- private int a = 0;
- private int b = 0;
- private int c = 0;
- private int attribute =-1;
+ private int a = 0;
+ private int b = 0;
+ private int c = 0;
+ private int attribute = -1;
+
+ public int getA() {
+ return a;
+ }
+
+ public int get(int id) {
+ switch (id) {
+ case 0:
+ return a;
+ case 1:
+ return b;
+ default:
+ return c;
+ }
+ }
- public int getA() {
- return a;
- }
+ public int getAttribute() {
+ return this.attribute;
+ }
- public int get(int id) {
- switch (id) {
- case 0:
- return a;
- case 1:
- return b;
- default:
- return c;
- }
+ /**
+ * @param attribute new Attribute value for this triangle
+ */
+ public void setAttribute(int attribute) {
+ this.attribute = attribute;
}
- public int getAttribute(){
- return this.attribute;
+
+ public void set(int id, int index) {
+ switch (id) {
+ case 0:
+ a = index;
+ break;
+ case 1:
+ b = index;
+ break;
+ default:
+ c = index;
}
-
-
- public void set(int id,int index) {
- switch (id) {
- case 0:
- a=index;
- break;
- case 1:
- b=index;
- break;
- default:
- c=index;
- }
- }
+ }
- public void setA(int a) {
- this.a = a;
- }
+ public void setA(int a) {
+ this.a = a;
+ }
- public int getB() {
- return b;
- }
+ public int getB() {
+ return b;
+ }
- public void setB(int b) {
- this.b = b;
- }
+ public void setB(int b) {
+ this.b = b;
+ }
- public int getC() {
- return c;
- }
+ public int getC() {
+ return c;
+ }
- public void setC(int c) {
- this.c = c;
- }
+ public void setC(int c) {
+ this.c = c;
+ }
- public Triangle(int a, int b, int c, int attribute) {
- super();
- this.a = a;
- this.b = b;
- this.c = c;
- this.attribute = attribute;
- }
-
- public Triangle(int a, int b, int c) {
- super();
- this.a = a;
- this.b = b;
- this.c = c;
-
- }
- public static class TriangleBuilder implements Consumer {
- ArrayList triangles;
+ public Triangle(int a, int b, int c, int attribute) {
+ super();
+ this.a = a;
+ this.b = b;
+ this.c = c;
+ this.attribute = attribute;
+ }
- public TriangleBuilder(ArrayList triangles) {
- this.triangles = triangles;
- }
+ public Triangle(int a, int b, int c) {
+ super();
+ this.a = a;
+ this.b = b;
+ this.c = c;
- @Override
- public void accept(SimpleTriangle triangle) {
- triangles.add(triangle);
- }
- }
+ }
+
+ public static class TriangleBuilder implements Consumer {
+ ArrayList triangles;
+
+ public TriangleBuilder(ArrayList triangles) {
+ this.triangles = triangles;
+ }
+
+ @Override
+ public void accept(SimpleTriangle triangle) {
+ triangles.add(triangle);
+ }
+ }
}
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/path/Scene.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/path/Scene.java
index 34fabf182..82cc94988 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/path/Scene.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/path/Scene.java
@@ -59,6 +59,12 @@ public class Scene {
public int reflexionOrder = 1;
public double defaultGroundAttenuation = 0;
+ /**
+ * dictates the density of source points created from a line sound source;
+ * a higher value means more points and finer discretization
+ * sourcePointDistance = DistanceSourceReceiver / lineSourceSpacingRatio
+ */
+ public double lineSourceSpacingRatio = 2;
public Scene() {
this.profileBuilder = new ProfileBuilder();
@@ -89,6 +95,8 @@ public void setBodyBarrier(boolean bodyBarrier) {
public double maxSrcDist = DEFAULT_MAX_PROPAGATION_DISTANCE;
/** Maximum reflection wall distance from receiver to source line */
public double maxRefDist = DEFAULT_MAXIMUM_REF_DIST;
+ /** Maximum receiver-to-wall horizontal distance for the optional reflection cut profile filter */
+ private double closeReceiverReflectionWallDistance = 0;
/**
@@ -121,19 +129,6 @@ public void addSource(Long pk, Geometry geom, Orientation orientation) {
sourceOrientation.put(pk, orientation);
}
- /**
- * Replace the sources by the given list
- * @param sourceGeometries
- */
- public void setSources(List sourceGeometries) {
- sourcesIndex = new QueryRTree();
- int i = 0;
- for(Geometry source : sourceGeometries) {
- sourcesIndex.appendGeometry(source, i++);
- }
- this.sourceGeometries = sourceGeometries;
- }
-
/**
*
* @param receiver
@@ -171,6 +166,14 @@ public void setDefaultGroundAttenuation(double gS) {
this.defaultGroundAttenuation = gS;
}
+ public double getCloseReceiverReflectionWallDistance() {
+ return closeReceiverReflectionWallDistance;
+ }
+
+ public void setCloseReceiverReflectionWallDistance(double closeReceiverReflectionWallDistance) {
+ this.closeReceiverReflectionWallDistance = closeReceiverReflectionWallDistance;
+ }
+
public void clearSources() {
sourceGeometries.clear();
sourceOrientation.clear();
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/BuildingIntersectionPathVisitor.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/BuildingIntersectionPathVisitor.java
index f289c385a..3ac064ad9 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/BuildingIntersectionPathVisitor.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/BuildingIntersectionPathVisitor.java
@@ -136,8 +136,6 @@ public void addItem(int id) {
if (!roofPoints.isEmpty()) {
input.addAll(roofPoints);
pushedBuildingsWideAnglePoints.add(processedWall.originId);
- // Stop iterating bounding boxes
- throw new IllegalStateException();
}
} else if(processedWall.type == ProfileBuilder.IntersectionType.WALL) {
// A wall not related to a building (polygon)
@@ -172,8 +170,6 @@ public void addItem(int id) {
if (!roofPoints.isEmpty()) {
pushedWallsPoints.add(processedWall.originId);
input.addAll(roofPoints);
- // Stop iterating bounding boxes
- throw new IllegalStateException();
}
}
}
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutPointReflection.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutPointReflection.java
index c40766c79..3384a8f3d 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutPointReflection.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutPointReflection.java
@@ -70,6 +70,19 @@ public void setWallAlpha(List wallAlpha) {
this.wallAlpha = wallAlpha;
}
+ @Override
+ public CutPoint clone() {
+ CutPointReflection cloned = (CutPointReflection) super.clone();
+ // Deep copy the wall LineSegment
+ if (this.wall != null) {
+ cloned.wall = new LineSegment(
+ new Coordinate(this.wall.p0),
+ new Coordinate(this.wall.p1)
+ );
+ }
+ return cloned;
+ }
+
@Override
public String toString() {
return "CutPointReflection{" +
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutProfile.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutProfile.java
index 137a528b3..4e8ae5eab 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutProfile.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/CutProfile.java
@@ -15,6 +15,7 @@
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.LineSegment;
import org.noise_planet.noisemodelling.pathfinder.path.Scene;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.CurvedProfileGenerator;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.JTSUtility;
@@ -177,6 +178,43 @@ public double getGPath() {
}
}
+ /**
+ * Compute the G coefficient for path segment using indices instead of CutPoint references.
+ * This avoids indexOf() calls that fail when using transformed CutPoints.
+ * @param i0 Index of first CutPoint in segment
+ * @param i1 Index of last CutPoint in segment
+ * @param buildingRoofG Ground absorption coefficient for building roofs
+ * @return Weighted average of ground absorption coefficients along the segment
+ */
+ @JsonIgnore
+ public double getGPathByIndex(int i0, int i1, double buildingRoofG) {
+ if(i0 == -1 || i1 == -1 || i1 < i0 || i0 >= cutPoints.size() || i1 >= cutPoints.size()) {
+ return 0.0;
+ }
+
+ double totalLength = 0;
+ double rsLength = 0.0;
+
+ boolean aboveRoof = false;
+ for(int index = 0; index < i1; index++) {
+ CutPoint current = cutPoints.get(index);
+ if(current instanceof CutPointWall) {
+ CutPointWall currentWall = (CutPointWall) current;
+ if(!aboveRoof && currentWall.intersectionType.equals(CutPointWall.INTERSECTION_TYPE.BUILDING_ENTER)) {
+ aboveRoof = true;
+ } else if(aboveRoof && currentWall.intersectionType.equals(CutPointWall.INTERSECTION_TYPE.BUILDING_EXIT)) {
+ aboveRoof = false;
+ }
+ }
+ if(index >= i0) {
+ double segmentLength = current.getCoordinate().distance(cutPoints.get(index + 1).getCoordinate());
+ rsLength += segmentLength * (aboveRoof ? buildingRoofG : current.getGroundCoefficient());
+ totalLength += segmentLength;
+ }
+ }
+ return rsLength / totalLength;
+ }
+
/**
*
* @return
@@ -186,6 +224,34 @@ public boolean isFreeField() {
return !hasBuildingIntersection && !hasTopographyIntersection;
}
+ /**
+ * @param maximumReceiverWallDistance Maximum horizontal receiver-to-wall distance in meters
+ * @return True if this reflection profile contains a last reflection before the receiver and the receiver
+ * is closer than the provided distance to that reflective wall, even if other events occur afterwards
+ */
+ @JsonIgnore
+ public boolean hasCloseReflectionBeforeReceiver(double maximumReceiverWallDistance) {
+ if(profileType != PROFILE_TYPE.REFLECTION || cutPoints.size() < 3 || maximumReceiverWallDistance < 0) {
+ return false;
+ }
+ CutPointReceiver receiver = getReceiver();
+ if(receiver == null) {
+ return false;
+ }
+ CutPointReflection lastReflectionBeforeReceiver = null;
+ for(int i = cutPoints.size() - 2; i >= 1; i--) {
+ CutPoint cutPoint = cutPoints.get(i);
+ if(cutPoint instanceof CutPointReflection) {
+ lastReflectionBeforeReceiver = (CutPointReflection) cutPoint;
+ break;
+ }
+ }
+ if(lastReflectionBeforeReceiver == null || lastReflectionBeforeReceiver.wall == null) {
+ return false;
+ }
+ return lastReflectionBeforeReceiver.wall.distance(receiver.coordinate) < maximumReceiverWallDistance;
+ }
+
@Override
public String toString() {
@@ -210,10 +276,25 @@ public List computePts2DGround() {
* @return @return the computed coordinate list
*/
public List computePts2D(boolean curvedPath) {
+ return computePts2D(curvedPath, null);
+ }
+
+ /**
+ * Compute 2D coordinates, optionally applying curved transformation
+ * @param curvedPath Whether to apply curved transformation
+ * @param transformedCutPointsOut If not null and curvedPath is true, will be populated with transformed CutPoints
+ * @return The computed 2D coordinate list
+ */
+ public List computePts2D(boolean curvedPath, List transformedCutPointsOut) {
List pts2D;
if(curvedPath) {
- pts2D = CurvedProfileGenerator.applyTransformation(cutPoints
- , false).stream()
+ List transformedCutPoints = CurvedProfileGenerator.applyTransformation(cutPoints, false);
+ // If caller wants the transformed cut points, populate the output list
+ if (transformedCutPointsOut != null) {
+ transformedCutPointsOut.clear();
+ transformedCutPointsOut.addAll(transformedCutPoints);
+ }
+ pts2D = transformedCutPoints.stream()
.map(CutPoint::getCoordinate)
.collect(Collectors.toList());
} else {
@@ -233,6 +314,11 @@ public List computePts2D() {
}
public List getConvexHullIndices(List coordinates2d) {
+ return getConvexHullIndices(coordinates2d, false);
+ }
+
+
+ public List getConvexHullIndices(List coordinates2d, boolean ignoreWall) {
if(coordinates2d.size() != cutPoints.size()) {
throw new IllegalArgumentException("Coordinates size must be equal to cut points size");
}
@@ -246,7 +332,7 @@ public List getConvexHullIndices(List coordinates2d) {
// We only add the point at the top of the wall, not the point at the bottom of the wall
if(currentPoint instanceof CutPointTopography
|| (currentPoint instanceof CutPointWall
- && Double.compare(currentPoint.getCoordinate().z, currentPoint.getzGround()) != 0)) {
+ && Double.compare(currentPoint.getCoordinate().z, currentPoint.getzGround()) != 0 && !ignoreWall)) {
convexHullInput.add(coordinates2d.get(idPoint));
}
}
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/ProfileBuilder.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/ProfileBuilder.java
index f15eb5714..e0cbce114 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/ProfileBuilder.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/profilebuilder/ProfileBuilder.java
@@ -13,6 +13,7 @@
import org.locationtech.jts.algorithm.CGAlgorithms3D;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.index.strtree.STRtree;
+import org.locationtech.jts.io.WKTWriter;
import org.locationtech.jts.math.Vector2D;
import org.locationtech.jts.math.Vector3D;
import org.locationtech.jts.operation.distance.DistanceOp;
@@ -282,6 +283,131 @@ public ProfileBuilder addBuilding(Coordinate[] coords, double height, int id) {
return addBuilding(coords, height, new ArrayList<>(), id);
}
+
+ /**
+ * Apply a linestring over the digital elevation model by offsetting the z value with the ground elevation.
+ * @param lineString
+ * @param epsilon ignore elevation point where linear interpolation distance is inferior that this value
+ * @return computed lineString
+ */
+ public LineString splitGeometryLineToDem(LineString lineString, double epsilon) {
+ boolean warned = false;
+ ArrayList newGeomCoordinates = new ArrayList<>();
+ Coordinate[] coordinates = lineString.getCoordinates();
+ for(int idPoint = 0; idPoint < coordinates.length - 1; idPoint++) {
+ Coordinate p0 = coordinates[idPoint];
+ Coordinate p1 = coordinates[idPoint + 1];
+ List groundProfileCoordinates = new ArrayList<>();
+ fetchTopographicProfile(groundProfileCoordinates, p0, p1, false);
+ newGeomCoordinates.ensureCapacity(newGeomCoordinates.size() + groundProfileCoordinates.size());
+ if(groundProfileCoordinates.size() < 2) {
+ if(hasDem()) {
+ if(!warned) {
+ LOGGER.warn( "Source line out of DEM area {}",
+ new WKTWriter(3).write(lineString));
+ warned = true;
+ }
+ }
+ newGeomCoordinates.add(p0);
+ newGeomCoordinates.add(p1);
+ } else {
+ if (idPoint == 0) {
+ newGeomCoordinates.add(new Coordinate(p0.x, p0.y, p0.z + groundProfileCoordinates.get(0).z));
+ }
+ Coordinate previous = groundProfileCoordinates.get(0);
+ for (int groundPoint = 1; groundPoint < groundProfileCoordinates.size() - 1; groundPoint++) {
+ final Coordinate current = groundProfileCoordinates.get(groundPoint);
+ final Coordinate next = groundProfileCoordinates.get(groundPoint + 1);
+ // Do not add topographic points which are simply the linear interpolation between two points
+ // triangulation add a lot of interpolated lines from line segment DEM
+ if (CGAlgorithms3D.distancePointSegment(current, previous, next) >= epsilon) {
+ // interpolate the Z (height) values of the source then add the altitude
+ previous = current;
+ newGeomCoordinates.add(
+ new Coordinate(current.x, current.y, current.z + Vertex.interpolateZ(current, p0, p1)));
+ }
+ }
+ newGeomCoordinates.add(new Coordinate(p1.x, p1.y, p1.z +
+ groundProfileCoordinates.get(groundProfileCoordinates.size() - 1).z));
+ }
+ }
+ return lineString.getFactory().createLineString(newGeomCoordinates.toArray(new Coordinate[0]));
+ }
+
+ /**
+ * Update ground Z coordinates of sound sources absolute to sea levels
+ * @param geometry Geometry to offset the Z value
+ * @param checkForBuildingVolumes If true, this function will print a warning if the geometry contain at least one point under a building roof
+ */
+ public Geometry makeGeometryRelativeZToAbsolute(Geometry geometry, boolean checkForBuildingVolumes) {
+ Geometry offsetGeometry;
+ if (geometry instanceof LineString) {
+ offsetGeometry = splitGeometryLineToDem((LineString) geometry, ProfileBuilder.MILLIMETER);
+ if (checkForBuildingVolumes) {
+ Coordinate[] coordinates = offsetGeometry.getCoordinates();
+ logWarningIfCoordinatesIntoBuildings(coordinates);
+ }
+ } else if (geometry instanceof MultiLineString) {
+ LineString[] newGeom = new LineString[geometry.getNumGeometries()];
+ for (int idGeom = 0; idGeom < geometry.getNumGeometries(); idGeom++) {
+ newGeom[idGeom] = splitGeometryLineToDem((LineString) geometry.getGeometryN(idGeom),
+ MILLIMETER);
+ }
+ offsetGeometry = geometry.getFactory().createMultiLineString(newGeom);
+ if (checkForBuildingVolumes) {
+ Coordinate[] coordinates = offsetGeometry.getCoordinates();
+ logWarningIfCoordinatesIntoBuildings(coordinates);
+ }
+ } else if (geometry instanceof Point) {
+ Coordinate geometryCoordinate = geometry.getCoordinate();
+ offsetGeometry = geometry.getFactory().createPoint(offsetCoordinateToAltitudeUsingDigitalElevationModel(geometryCoordinate, checkForBuildingVolumes));
+ } else {
+ throw new IllegalArgumentException("Unsupported source geometry " + geometry.getGeometryType());
+ }
+ return offsetGeometry;
+ }
+
+ /**
+ * Update ground Z coordinates of sound sources absolute to sea levels
+ * @param geometryCoordinates Geometry coordinates to offset the Z value
+ * @param checkForBuildingVolumes If true, this function will print a warning if the geometry contain at least one point under a building roof
+ */
+ public Coordinate[] offsetCoordinatesToAltitudeUsingDigitalElevationModel(Coordinate[] geometryCoordinates, boolean checkForBuildingVolumes) {
+ Coordinate[] offsetCoordinates = new Coordinate[geometryCoordinates.length];
+ for (int i = 0; i < geometryCoordinates.length; i++) {
+ offsetCoordinates[i] = offsetCoordinateToAltitudeUsingDigitalElevationModel(geometryCoordinates[i], checkForBuildingVolumes);
+ }
+ return offsetCoordinates;
+ }
+
+ /**
+ * Offset a coordinate to altitude using the digital elevation model. The Z value of the coordinate is updated with the ground elevation at this point.
+ * @param geometryCoordinate Coordinate to offset
+ * @param checkForBuildingVolumes If true, this function will print a warning if the geometry contain at least one point under a building roof
+ * @return
+ */
+ public Coordinate offsetCoordinateToAltitudeUsingDigitalElevationModel(Coordinate geometryCoordinate, boolean checkForBuildingVolumes) {
+ Coordinate offsetCoordinate = new Coordinate(geometryCoordinate.x, geometryCoordinate.y, geometryCoordinate.z + getZGround(geometryCoordinate));
+ if(checkForBuildingVolumes) {
+ logWarningIfCoordinatesIntoBuildings(geometryCoordinate);
+ }
+ return offsetCoordinate;
+ }
+
+ private void logWarningIfCoordinatesIntoBuildings(Coordinate... coordinates) {
+ for (Coordinate coordinate : coordinates) {
+ // Check if the source is into a building
+ Building building = getBuildingAtCoordinate(coordinate);
+ if (building != null && building.getHeight() >= coordinate.z) {
+ LOGGER.warn("Geometry (Source point or Receiver point) has been defined inside a building" +
+ " (building height {} m), it should be moved higher Geometry: {}",
+ building.getHeight(), new WKTWriter(3).write(new GeometryFactory().createPoint(coordinate)));
+ break;
+ }
+ }
+ }
+
+
/**
* Add the given {@link Geometry} footprint, height and alphas (absorption coefficients) as building.
* @param geom Building footprint.
@@ -398,15 +524,6 @@ public ProfileBuilder addWall(Coordinate[] coords, double height, int id) {
return addWall(FACTORY.createLineString(coords), height, new ArrayList<>(), id);
}
- /**
- * Add the given {@link Geometry} footprint, height, alphas (absorption coefficients) and a database id as wall.
- * @param geom Wall footprint.
- * @param id Database key.
- */
- /*public ProfileBuilder addWall(LineString geom, int id) {
- return addWall(geom, 0.0, new ArrayList<>(), id);
- }*/
-
/**
* Add the given {@link Geometry} footprint, height, alphas (absorption coefficients) and a database id as wall.
* @param coords Wall footprint coordinates.
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/geometry/CurvedProfileGenerator.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/geometry/CurvedProfileGenerator.java
index 103546f41..eab650c87 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/geometry/CurvedProfileGenerator.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/geometry/CurvedProfileGenerator.java
@@ -11,7 +11,9 @@
package org.noise_planet.noisemodelling.pathfinder.utils.geometry;
import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.LineSegment;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPoint;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointReflection;
import java.util.List;
import java.util.ArrayList;
@@ -58,6 +60,23 @@ public static List applyTransformation(List flatProfile, boo
CutPoint newCp = cp.clone();
newCp.setZGround(groundCoords[i].z);
newCp.setCoordinate(curvedCoords[i]);
+
+ // If this is a reflection point, also transform the wall coordinates
+ if (newCp instanceof CutPointReflection) {
+ CutPointReflection reflectionPoint = (CutPointReflection) newCp;
+ if (reflectionPoint.wall != null) {
+ // Transform wall endpoints using the same curved transformation
+ Coordinate[] wallCoords = new Coordinate[]{
+ new Coordinate(reflectionPoint.wall.p0),
+ new Coordinate(reflectionPoint.wall.p1)
+ };
+ Coordinate[] transformedWallCoords = applyTransformation(cs, cr, wallCoords, inversed);
+
+ // Create a NEW LineSegment with transformed coordinates
+ reflectionPoint.wall = new LineSegment(transformedWallCoords[0], transformedWallCoords[1]);
+ }
+ }
+
curvedProfile.add(newCp);
}
return curvedProfile;
@@ -75,7 +94,7 @@ public static Coordinate[] applyTransformation(Coordinate cs, Coordinate cr, Coo
Coordinate[] curvedProfile = new Coordinate[flatProfile.length];
// Calculate projected distance between source and receiver on the vertical plane
- double d = cs.distance3D(cr);
+ double d = cs.distance(cr);
// Calculate radius of curvature (Γ)
double radius = Math.max(1000, 8 * d);
@@ -87,7 +106,7 @@ public static Coordinate[] applyTransformation(Coordinate cs, Coordinate cr, Coo
// Apply equation (4) for z coordinate transformation
double z = base -
- Math.sqrt(radius * radius - Math.pow(p.distance3D(cs) - d/2, 2));
+ Math.sqrt(radius * radius - Math.pow(p.distance(cs) - d/2, 2));
if(inverse) {
z = -z;
diff --git a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/profiler/DefaultProgressVisitor.java b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/profiler/DefaultProgressVisitor.java
index 72c71322c..30d3db739 100644
--- a/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/profiler/DefaultProgressVisitor.java
+++ b/noisemodelling-pathfinder/src/main/java/org/noise_planet/noisemodelling/pathfinder/utils/profiler/DefaultProgressVisitor.java
@@ -57,6 +57,7 @@ public int getStepCount() {
@Override
public void endOfProgress() {
+ pushProgression(subprocessSize - subprocessDone);
}
@Override
diff --git a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right.json b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right.json
index 66a16d37e..a3821ea39 100644
--- a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right.json
+++ b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right.json
@@ -42,6 +42,52 @@
},
"zGround" : 0.0,
"groundCoefficient" : 0.5
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 92.81851301610914,
+ "y" : 10.240289036493747,
+ "z" : 10.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 88.86,
+ "y" : 2.9,
+ "z" : 10.0
+ },
+ "p1" : {
+ "x" : 94.9,
+ "y" : 14.1,
+ "z" : 10.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_ENTER"
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 96.9374845595576,
+ "y" : 12.970240933322229,
+ "z" : 10.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 94.9,
+ "y" : 14.1,
+ "z" : 10.0
+ },
+ "p1" : {
+ "x" : 98.02,
+ "y" : 12.37,
+ "z" : 10.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_EXIT"
}, {
"type" : "GroundEffect",
"coordinate" : {
diff --git a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right_Curved.json b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right_Curved.json
index 5fe3836be..ed40007d2 100644
--- a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right_Curved.json
+++ b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC15_Right_Curved.json
@@ -42,6 +42,52 @@
},
"zGround" : 0.0,
"groundCoefficient" : 0.5
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 92.81851301610914,
+ "y" : 10.240289036493747,
+ "z" : 10.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 88.86,
+ "y" : 2.9,
+ "z" : 10.0
+ },
+ "p1" : {
+ "x" : 94.9,
+ "y" : 14.1,
+ "z" : 10.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_ENTER"
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 96.9374845595576,
+ "y" : 12.970240933322229,
+ "z" : 10.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 94.9,
+ "y" : 14.1,
+ "z" : 10.0
+ },
+ "p1" : {
+ "x" : 98.02,
+ "y" : 12.37,
+ "z" : 10.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_EXIT"
}, {
"type" : "GroundEffect",
"coordinate" : {
diff --git a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left.json b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left.json
index d557d8598..d6d74b593 100644
--- a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left.json
+++ b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left.json
@@ -19,25 +19,62 @@
"type" : "GroundEffect",
"coordinate" : {
"x" : 50.0,
- "y" : 18.894651178790934,
+ "y" : 16.063775909003617,
"z" : 0.0
},
"zGround" : 0.0,
"groundCoefficient" : 0.5
}, {
- "type" : "VEdgeDiffraction",
+ "type" : "Wall",
"coordinate" : {
- "x" : 99.9893933982822,
- "y" : 30.010606601717797,
- "z" : 7.232234504389141
+ "x" : 102.35169775461208,
+ "y" : 24.0,
+ "z" : 12.0
},
"zGround" : 0.0,
- "groundCoefficient" : 0.5
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 118.0,
+ "y" : 24.0,
+ "z" : 12.0
+ },
+ "p1" : {
+ "x" : 100.0,
+ "y" : 24.0,
+ "z" : 12.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_ENTER"
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 118.0,
+ "y" : 26.372194954309762,
+ "z" : 12.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 118.0,
+ "y" : 30.0,
+ "z" : 12.0
+ },
+ "p1" : {
+ "x" : 118.0,
+ "y" : 24.0,
+ "z" : 12.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_EXIT"
}, {
"type" : "Topography",
"coordinate" : {
"x" : 120.0,
- "y" : 31.344902798584958,
+ "y" : 26.67538374975994,
"z" : 0.0
},
"zGround" : 0.0,
@@ -46,10 +83,10 @@
"type" : "GroundEffect",
"coordinate" : {
"x" : 150.0,
- "y" : 33.34528623036851,
- "z" : 4.615384615384615
+ "y" : 31.223215681512652,
+ "z" : 4.615384615384616
},
- "zGround" : 4.615384615384615,
+ "zGround" : 4.615384615384616,
"groundCoefficient" : 0.2
}, {
"type" : "VEdgeDiffraction",
diff --git a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left_Curved.json b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left_Curved.json
index d66bbaaac..845a2b622 100644
--- a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left_Curved.json
+++ b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC19_Left_Curved.json
@@ -19,25 +19,62 @@
"type" : "GroundEffect",
"coordinate" : {
"x" : 50.0,
- "y" : 18.894651178790934,
+ "y" : 16.063775909003617,
"z" : 0.0
},
"zGround" : 0.0,
"groundCoefficient" : 0.5
}, {
- "type" : "VEdgeDiffraction",
+ "type" : "Wall",
"coordinate" : {
- "x" : 99.9893933982822,
- "y" : 30.010606601717797,
- "z" : 7.232234504389141
+ "x" : 102.35169775461208,
+ "y" : 24.0,
+ "z" : 12.0
},
"zGround" : 0.0,
- "groundCoefficient" : 0.5
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 118.0,
+ "y" : 24.0,
+ "z" : 12.0
+ },
+ "p1" : {
+ "x" : 100.0,
+ "y" : 24.0,
+ "z" : 12.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_ENTER"
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 118.0,
+ "y" : 26.372194954309762,
+ "z" : 12.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 118.0,
+ "y" : 30.0,
+ "z" : 12.0
+ },
+ "p1" : {
+ "x" : 118.0,
+ "y" : 24.0,
+ "z" : 12.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_EXIT"
}, {
"type" : "Topography",
"coordinate" : {
"x" : 120.0,
- "y" : 31.344902798584958,
+ "y" : 26.67538374975994,
"z" : 0.0
},
"zGround" : 0.0,
@@ -46,10 +83,10 @@
"type" : "GroundEffect",
"coordinate" : {
"x" : 150.0,
- "y" : 33.34528623036851,
- "z" : 4.615384615384615
+ "y" : 31.223215681512652,
+ "z" : 4.615384615384616
},
- "zGround" : 4.615384615384615,
+ "zGround" : 4.615384615384616,
"groundCoefficient" : 0.2
}, {
"type" : "VEdgeDiffraction",
diff --git a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC28_Left.json b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC28_Left.json
index e4a745bef..c17a4912c 100644
--- a/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC28_Left.json
+++ b/noisemodelling-pathfinder/src/main/resources/org/noise_planet/noisemodelling/pathfinder/test_cases/TC28_Left.json
@@ -24,6 +24,52 @@
},
"zGround" : 0.0,
"groundCoefficient" : 0.5
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 250.0,
+ "y" : 105.77403802834974,
+ "z" : 14.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 250.0,
+ "y" : 70.0,
+ "z" : 14.0
+ },
+ "p1" : {
+ "x" : 250.0,
+ "y" : 180.0,
+ "z" : 14.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_ENTER"
+ }, {
+ "type" : "Wall",
+ "coordinate" : {
+ "x" : 270.0,
+ "y" : 109.90491024993129,
+ "z" : 14.0
+ },
+ "zGround" : 0.0,
+ "groundCoefficient" : 0.5,
+ "wall" : {
+ "p0" : {
+ "x" : 270.0,
+ "y" : 180.0,
+ "z" : 14.0
+ },
+ "p1" : {
+ "x" : 270.0,
+ "y" : 70.0,
+ "z" : 14.0
+ }
+ },
+ "wallAlpha" : [ 0.004918209037966988, 0.008088162780793065, 0.013267721240171722, 0.02168493917994337, 0.03525564533465828, 0.056883929412881944, 0.09077715686040604, 0.14258583864393162 ],
+ "intersectionType" : "BUILDING_EXIT"
}, {
"type" : "VEdgeDiffraction",
"coordinate" : {
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 f8df380c8..e149549c4 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
@@ -100,8 +100,7 @@ public void testPolygonHole() throws ParseException, LayerDelaunayError {
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();
diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/PathFinderTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/PathFinderTest.java
index 45ce0bb0a..f2198c510 100644
--- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/PathFinderTest.java
+++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/PathFinderTest.java
@@ -120,7 +120,7 @@ public static void assertCutProfile(InputStream expected, CutProfile got) throws
public static void assertCutProfile(CutProfile expected, CutProfile got) {
assertNotNull(expected);
assertNotNull(got);
- assertEquals(expected.cutPoints.size(), got.cutPoints.size(), "Not the same number of cut points");
+ assertEquals(expected.cutPoints.size(), got.cutPoints.size(), got.getProfileType()+ " Not the same number of cut points");
assertEquals(expected.profileType, got.profileType, "Not the same profile type");
assertEquals(expected.curvedPath, got.curvedPath, "Not the same curved path attribute value");
@@ -811,7 +811,6 @@ public void TC14() throws Exception {
/**
* Test TC15 -- Flat ground with homogeneous acoustic properties and four buildings
- * right : error in value of b cnossos table 149 right path
*/
@Test
public void TC15() throws Exception {
@@ -833,6 +832,12 @@ public void TC15() throws Exception {
new Coordinate(93.3, 17.8, 10),
new Coordinate(87.3, 6.6, 10),
new Coordinate(84.1, 8.3, 10),
+ })
+ .addBuilding(new Coordinate[]{
+ new Coordinate(94.9, 14.1, 10),
+ new Coordinate(98.02, 12.37, 10),
+ new Coordinate(92.03, 1.2, 10),
+ new Coordinate(88.86, 2.9, 10),
});
profileBuilder.addGroundEffect(0, 100, 0.0, 150, 0.5);
profileBuilder.setzBuildings(true);
@@ -856,7 +861,6 @@ public void TC15() throws Exception {
//Run computation
computeRays.run(propDataOut);
-
assertEquals(5, propDataOut.getCutProfiles().size());
assertCutProfiles("TC15", propDataOut.cutProfiles);
@@ -1107,10 +1111,6 @@ public void TC19() throws Exception {
assertEquals(5, propDataOut.getCutProfiles().size());
assertCutProfiles("TC19", propDataOut.cutProfiles);
-
- //Different value with the TC because their z-profile left seems to be false, it follows the building top
- // border while it should not
- // assertCutProfile("TC19_Left", propDataOut.cutProfiles.poll());
}
/**
@@ -1558,12 +1558,11 @@ public void TC28() throws Exception {
new Coordinate(184, 91, 0),
new Coordinate(196, 22, 0)}, 10, -1)
-// this building is ignored in the test case
-// .addBuilding(new Coordinate[]{
-// new Coordinate(250, 70, 0),
-// new Coordinate(250, 180, 0),
-// new Coordinate(270, 180, 0),
-// new Coordinate(270, 70, 0)}, 14, -1)
+ .addBuilding(new Coordinate[]{
+ new Coordinate(250, 70, 0),
+ new Coordinate(250, 180, 0),
+ new Coordinate(270, 180, 0),
+ new Coordinate(270, 70, 0)}, 14, -1)
.addBuilding(new Coordinate[]{
new Coordinate(332, 32, 0),
diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/ProfileBuilderTest.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/ProfileBuilderTest.java
index d89ecedf1..bae2dcff2 100644
--- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/ProfileBuilderTest.java
+++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/ProfileBuilderTest.java
@@ -286,10 +286,8 @@ public void testRelativeSourceLineProjection() throws ParseException {
Scene scene = new Scene(profileBuilder);
WKTReader wktReader = new WKTReader();
Geometry geometry = wktReader.read("MultiLineStringZ ((10 10 1, 200 50 1))");
- scene.addSource(1L, geometry);
- PathFinder pathFinder = new PathFinder(scene);
- assertEquals(2, scene.sourceGeometries.get(0).getNumPoints());
- pathFinder.makeSourceRelativeZToAbsolute();
+ scene.addSource(1L, profileBuilder.makeGeometryRelativeZToAbsolute(geometry, false));
+ assertEquals(2, geometry.getNumPoints());
// The source line should now be made of 4 points (2 points being created by the elevated DEM)
assertEquals(4, scene.sourceGeometries.get(0).getNumPoints());
List expectedProfile = Arrays.asList(
diff --git a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestPathFinder.java b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestPathFinder.java
index cf51d5368..88f4c3cb1 100644
--- a/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestPathFinder.java
+++ b/noisemodelling-pathfinder/src/test/java/org/noise_planet/noisemodelling/pathfinder/TestPathFinder.java
@@ -10,17 +10,20 @@
package org.noise_planet.noisemodelling.pathfinder;
+import org.h2gis.functions.spatial.linear_referencing.ST_LineInterpolatePoint;
import org.junit.jupiter.api.Test;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.noise_planet.noisemodelling.pathfinder.path.Scene;
-import org.noise_planet.noisemodelling.pathfinder.profilebuilder.ProfileBuilder;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.*;
import org.noise_planet.noisemodelling.pathfinder.utils.geometry.JTSUtility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@@ -70,60 +73,6 @@ public void testMeanPlane() {
assertEquals(-2.33, intercept, 0.01);
}
- /**
- * Test vertical edge diffraction ray computation
- *
- * @throws ParseException
- */
- @Test
- public void TestcomputeVerticalEdgeDiffraction() throws ParseException {
- GeometryFactory factory = new GeometryFactory();
- WKTReader wktReader = new WKTReader(factory);
- //Create obstruction test object
- ProfileBuilder profileBuilder = new ProfileBuilder();
- profileBuilder.addBuilding(wktReader.read("POLYGON((5 6, 6 5, 7 5, 7 8, 6 8, 5 7, 5 6))"), 4, -1);
- profileBuilder.addBuilding(wktReader.read("POLYGON((9 7, 11 7, 11 11, 9 11, 9 7))"), 4, -1);
- profileBuilder.addBuilding(wktReader.read("POLYGON((12 8, 13 8, 13 10, 12 10, 12 8))"), 4, -1);
- profileBuilder.addBuilding(wktReader.read("POLYGON((10 4, 11 4, 11 6, 10 6, 10 4))"), 4, -1);
- profileBuilder.finishFeeding();
-
- PathFinder computeRays = new PathFinder(new Scene(profileBuilder));
- Coordinate p1 = new Coordinate(2, 6.5, 1.6);
- Coordinate p2 = new Coordinate(14, 6.5, 1.6);
-
- List ray = computeRays.computeSideHull(true, p1, p2);
- int i = 0;
- assertEquals(0, p1.distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(9, 11).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(11, 11).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(13, 10).distance(ray.get(i++)), 0.02);
- assertEquals(0, p2.distance(ray.get(i)), 0.02);
-
- ray = computeRays.computeSideHull(false, p1, p2);
- i = 0;
- assertEquals(0, p1.distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(6, 5).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(10, 4).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(11, 4).distance(ray.get(i++)), 0.02);
- assertEquals(0, p2.distance(ray.get(i)), 0.02);
-
- ray = computeRays.computeSideHull(false, p2, p1);
- i = 0;
- assertEquals(0, p2.distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(13, 10).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(11, 11).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(9, 11).distance(ray.get(i++)), 0.02);
- assertEquals(0, p1.distance(ray.get(i)), 0.02);
-
- ray = computeRays.computeSideHull(true, p2, p1);
- i = 0;
- assertEquals(0, p2.distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(11, 4).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(10, 4).distance(ray.get(i++)), 0.02);
- assertEquals(0, new Coordinate(6, 5).distance(ray.get(i++)), 0.02);
- assertEquals(0, p1.distance(ray.get(i)), 0.02);
- }
-
@Test
public void TestSplitLineSourceIntoPoints() {
GeometryFactory factory = new GeometryFactory();
@@ -144,6 +93,31 @@ public void TestSplitLineSourceIntoPoints() {
assertEquals(0, new Coordinate(4, 1.25, 0).distance3D(sourcePoints.get(1)), 1e-6);
}
+ @Test
+ public void TestSplitLineSourceIntoPointsRegression() throws ParseException, SQLException {
+ WKTReader wktReader = new WKTReader();
+ LineString lineSource = (LineString) wktReader.read("LINESTRING (171698.4427670392 177701.00951635558, 171706.8669046766 " +
+ "177687.91784651205, 171712.97191399403 177677.7644841401, 171720.4437829301 177663.66767532472, " +
+ "171728.96676823843 177647.13902381528, 171734.72943947717 177634.77051055804, 171739.0868822124 " +
+ "177625.43309380766, 171745.47147686878 177610.67544839345, 171753.22554062092 177591.36253726296, " +
+ "171759.58780549894 177571.8658362897, 171769.10714508523 177535.77365270816, 171772.756902288 177519" +
+ ".09122776985, 171776.23273920204 177500.27220100444, 171778.51597507883 177485.76444646623, 171780" +
+ ".4296797503 177467.1168851778, 171781.83025393772 177445.0297520226, 171789.99859531887 177262" +
+ ".8134907158, 171791.8784187642 177220.3374281656, 171793.76627182262 177175.94801528286, 171794" +
+ ".85835770285 177143.55845900718, 171796.33670644296 177099.5455712648, 171804.84062152542 176921" +
+ ".4135355642, 171805.81436789143 176907.24518988002, 171807.89555734128 176888.98771900777, 171813" +
+ ".93869655454 176851.86881521158, 171816.43073227373 176841.3444906231, 171819.2233892781 176829" +
+ ".58662367053, 171826.7446807982 176804.04324781243, 171836.06368822214 176775.90431953594, 171841" +
+ ".44715023096 176760.3082989417, 171842.8699331516 176756.52086056024, 171846.55237681692 176747" +
+ ".3253388824)");
+ List sourcePoints = new ArrayList<>();
+ splitLineStringIntoPoints(lineSource, 1550.50, sourcePoints);
+ assertEquals(1, sourcePoints.size());
+ // Check if the point is the middle point of the line
+ Coordinate expectedPoint = ST_LineInterpolatePoint.execute(lineSource, 0.5).getCoordinate();
+ assertEquals(0, expectedPoint.distance(sourcePoints.get(0)), 1e-6);
+ }
+
@Test
public void TestSplitRegression() throws ParseException {
LineString geom = (LineString)new WKTReader().read("LINESTRING (26.3 175.5 0.0000034909259558, 111.9 90.9 0, 123 -70.9 0, 345.2 -137.8 0)");
@@ -282,4 +256,27 @@ public void TestVerticalEdgeDiffractionAirplaneSource() throws ParseException {
assertTrue(ray.isEmpty());
}
+
+ @Test
+ public void testMegaHeight() {
+ // create cut profile
+
+ CutProfile cutProfile = new CutProfile();
+ cutProfile.cutPoints.add(new CutPointSource(new Coordinate(843938.5179467835, 6519642.72565055, 0.05)));
+ cutProfile.cutPoints.add(new CutPointWall(13489, new Coordinate(844060.5664716291, 6519616.108346815, 1649.0),
+ new LineSegment(new Coordinate(844062.998140139, 6519613.740667035, 1649.0),
+ new Coordinate(844060.4359994413, 6519616.235385661, 1649.0)),
+ Arrays.asList(0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1)));
+ cutProfile.cutPoints.add(new CutPointWall(13482, new Coordinate(844086.1833603203, 6519610.521613932, 1649.0),
+ new LineSegment(new Coordinate(844086.6283247864, 6519610.685363031, 1649.0),
+ new Coordinate(844083.275146627, 6519609.451377079, 1649.0)),
+ Arrays.asList(0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1)));
+ cutProfile.cutPoints.add(new CutPointReceiver(new Coordinate(844089.2305787631, 6519609.85705253, 4.0)));
+ cutProfile.hasBuildingIntersection = true;
+
+ List pts2d = cutProfile.computePts2D(true);
+ for(Coordinate pt : pts2d) {
+ assertFalse(Double.isNaN(pt.y));
+ }
+ }
}
\ No newline at end of file
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 a8b86d4e2..c4e637d5b 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
@@ -20,6 +20,7 @@
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointReceiver;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointReflection;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointSource;
+import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutPointTopography;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.CutProfile;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.ProfileBuilder;
import org.noise_planet.noisemodelling.pathfinder.profilebuilder.Wall;
@@ -31,11 +32,48 @@
import java.util.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class TestWallReflection {
+ @Test
+ public void testCloseReflectionBeforeReceiverEvenWithPointAfterReflection() {
+ CutProfile cutProfile = new CutProfile();
+ cutProfile.setProfileType(CutProfile.PROFILE_TYPE.REFLECTION);
+ cutProfile.cutPoints.add(new CutPointSource(new Coordinate(11.49, 0.0, 2.0)));
+
+ CutPoint reflectionBasePoint = new CutPoint(new Coordinate(1.0, 0.0, 2.0), 0.0, 0.0);
+ cutProfile.cutPoints.add(new CutPointReflection(reflectionBasePoint,
+ new LineSegment(new Coordinate(1.0, -2.5, 0.0), new Coordinate(1.0, 2.5, 5.0)),
+ Collections.emptyList()));
+
+ // This point stands for a later event on the path (for example a diffraction-related point).
+ cutProfile.cutPoints.add(new CutPointTopography(new Coordinate(1.2, 0.0, 3.0)));
+ cutProfile.cutPoints.add(new CutPointReceiver(new Coordinate(1.49, 0.0, 2.0)));
+
+ assertTrue(cutProfile.hasCloseReflectionBeforeReceiver(0.5),
+ "The last reflection before the receiver should trigger filtering even if another point follows it.");
+ }
+
+ @Test
+ public void testReflectionOutsideReceiverWallDistanceThreshold() {
+ CutProfile cutProfile = new CutProfile();
+ cutProfile.setProfileType(CutProfile.PROFILE_TYPE.REFLECTION);
+ cutProfile.cutPoints.add(new CutPointSource(new Coordinate(11.51, 0.0, 2.0)));
+
+ CutPoint reflectionBasePoint = new CutPoint(new Coordinate(1.0, 0.0, 2.0), 0.0, 0.0);
+ cutProfile.cutPoints.add(new CutPointReflection(reflectionBasePoint,
+ new LineSegment(new Coordinate(1.0, -2.5, 0.0), new Coordinate(1.0, 2.5, 5.0)),
+ Collections.emptyList()));
+
+ cutProfile.cutPoints.add(new CutPointReceiver(new Coordinate(1.50, 0.0, 2.0)));
+
+ assertFalse(cutProfile.hasCloseReflectionBeforeReceiver(0.5),
+ "The optional filter must only reject reflections with a receiver-to-wall distance strictly below 0.5 m.");
+ }
+
@Test
public void testWideWall() {
Coordinate cA = new Coordinate(50, 100, 5);
diff --git a/noisemodelling-propagation/pom.xml b/noisemodelling-propagation/pom.xml
index 5935d6084..8dc046860 100644
--- a/noisemodelling-propagation/pom.xml
+++ b/noisemodelling-propagation/pom.xml
@@ -10,7 +10,7 @@
org.noise-planet
noisemodelling-parent
- 5.0.2-SNAPSHOT
+ 6.0.1-SNAPSHOT
../pom.xml
Compute sound propagation rays.
@@ -65,6 +65,11 @@
noisemodelling-emission
${project.version}
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
diff --git a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/AttenuationVisitor.java b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/AttenuationVisitor.java
index 06eee2646..595ad2a14 100644
--- a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/AttenuationVisitor.java
+++ b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/AttenuationVisitor.java
@@ -37,6 +37,10 @@ public AttenuationVisitor(AttenuationComputeOutput multiThreadParent) {
@Override
public PathSearchStrategy onNewCutPlane(CutProfile cutProfile) {
final SceneWithAttenuation scene = multiThreadParent.scene;
+ if(scene.getCloseReceiverReflectionWallDistance() > 0
+ && cutProfile.hasCloseReflectionBeforeReceiver(scene.getCloseReceiverReflectionWallDistance())) {
+ return PathSearchStrategy.CONTINUE;
+ }
// Source surface reflectivity
double gs = scene.sourceGs.getOrDefault(cutProfile.getSource().sourcePk, SceneWithAttenuation.DEFAULT_GS);
for(CnossosPath cnossosPath : CnossosPathBuilder.computeCnossosPathsFromCutProfile(cutProfile, scene.isBodyBarrier(),
@@ -115,4 +119,4 @@ public void finalizeReceiver(PathFinder.ReceiverPointInfo receiver) {
receiverAttenuationLevels.clear();
}
-}
\ No newline at end of file
+}
diff --git a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/SceneWithAttenuation.java b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/SceneWithAttenuation.java
index 067beac4e..1c1de6f5a 100644
--- a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/SceneWithAttenuation.java
+++ b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/SceneWithAttenuation.java
@@ -34,10 +34,6 @@ public class SceneWithAttenuation extends Scene {
*/
public Map sourceGs = new HashMap<>();
- /**
- * Cached source table fields
- */
- public Map sourceFieldNames = new HashMap<>();
/**
* If {@link #cnossosParametersPerPeriod} is empty, attenuation visitor will use this default settings and output
@@ -109,7 +105,7 @@ public void setDirectionAttributes(Map directionAttr
* @param geom Source geometry
* @param rs Additional attributes fetched from database
*/
- public void addSource(Long pk, Geometry geom, SpatialResultSet rs) throws SQLException {
+ public void addSource(Long pk, Geometry geom, SpatialResultSet rs, Map sourceFieldNames) throws SQLException {
if(sourceFieldNames.isEmpty()) {
List fieldNames = JDBCUtilities.getColumnNames(rs.getMetaData());
for(int idField = 0; idField < fieldNames.size(); idField++) {
@@ -186,7 +182,6 @@ public double[] getSourceAttenuation(int srcIndex, double[] frequencies, double
public void clearSources() {
super.clearSources();
sourceEmissionAttenuation.clear();
- sourceFieldNames.clear();
sourceGs.clear();
directionAttributes.clear();
}
diff --git a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/AttenuationCnossos.java b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/AttenuationCnossos.java
index 90c0e7f02..fb536a1b4 100644
--- a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/AttenuationCnossos.java
+++ b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/AttenuationCnossos.java
@@ -182,10 +182,10 @@ private static double getAGroundMin(SegmentPath segmentPath, CnossosPath pathPar
if (pathParameters.isFavourable()) {
// The lower bound of Aground,F (calculated with unmodified heights) depends on the geometry of the path
- if (segmentPath.testFormF <= 1) {
+ if (segmentPath.testFormH <= 1) {
aGroundMin = -3 * (1 - segmentPath.gm);
} else {
- aGroundMin = -3 * (1 - segmentPath.gm) * (1 + 2 * (1 - (1 / segmentPath.testFormF)));
+ aGroundMin = -3 * (1 - segmentPath.gm) * (1 + 2 * (1 - (1 / segmentPath.testFormH)));
}
} else {
aGroundMin = -3;
@@ -231,7 +231,8 @@ public static double[] aAtm(double[] alphaAtmosphericKm, double distance) {
*/
private static boolean isValidRcrit(CnossosPath pp, int freq) {
double lambda = 340.0/freq;
- return pp.delta > -lambda / 20 && pp.delta > lambda / 4 - pp.deltaPrime || pp.delta > 0;
+ // Eq 2.5.21: if delta >= 0, diffraction always applies; Rayleigh criterion only for delta < 0
+ return pp.delta >= 0 || (pp.delta > -lambda / 20 && pp.delta > lambda / 4 - pp.deltaPrime);
}
/**
@@ -306,8 +307,8 @@ public static double[] deltaRetrodif(CnossosPath reflect, AttenuationParameters
s = pointPath.coordinate;
} else if (pointPath.type.equals(REFL)) {
// Look for the next DIFH of the receiver
- for(int idPointNext=0; idPointNext= -2 ? 10 * ch * log10(3 + testForm) : 0; // 2.5.37
retroDiff[i] = dLRetro;
@@ -377,14 +379,11 @@ private static double aDif(CnossosPath proPathParameters, AttenuationParameters
(1+pow(5*lambda/ proPathParameters.e, 2))/(1./3+pow(5*lambda/ proPathParameters.e, 2));
double _delta = proPathParameters.delta;
- double deltaDStar = (proPathParameters.getSegmentList().get(0).dPrime +
- proPathParameters.getSegmentList().get(proPathParameters.getSegmentList().size() - 1).dPrime -
- proPathParameters.getSRSegment().dPrime);
double deltaDiffSR = 0;
double testForm = 40 / lambda * cSecond * _delta;
- if(_delta >= 0 || (_delta > -lambda/20 && _delta > lambda/4 - deltaDStar)) {
+ if(_delta >= 0 || (_delta > -lambda/20 && _delta > lambda/4 - proPathParameters.deltaPrime)) {
deltaDiffSR = testForm>=-2 ? 10*ch*log10(3+testForm) : 0;
} else if(type.equals(DIFH)) {
return 0;
@@ -409,8 +408,31 @@ private static double aDif(CnossosPath proPathParameters, AttenuationParameters
double aGroundOR = proPathParameters.isFavourable() ? aGroundF(proPathParameters, last, data, frequencyIndex, true) : aGroundH(proPathParameters, last, data, frequencyIndex, true);
//If the source or the receiver are under the mean plane, change the computation of deltaDffSR and deltaGround
- double deltaGroundSO = -20*log10(1+(pow(10, -aGroundSO/20)-1)*pow(10, -(deltaDiffSPrimeR-deltaDiffSR)/20));
- double deltaGroundOR = -20*log10(1+(pow(10, -aGroundOR/20)-1)*pow(10, -(deltaDiffSRPrime-deltaDiffSR)/20));
+ //
+ double deltaGroundSO;
+ double deltaGroundOR;
+ if(first.s.y >= first.sMeanPlane.y) {
+ // If the source is above the mean plane
+ deltaGroundSO = -20 * log10(1 + (pow(10, -aGroundSO / 20) - 1) * pow(10,
+ -(deltaDiffSPrimeR - deltaDiffSR) / 20));
+ } else {
+ // Source is below the mean plane
+ //equation 2.5.31 et 2.5.33
+ //https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:02002L0049-20210729
+ deltaGroundSO = aGroundSO;
+ deltaDiffSR = deltaDiffSPrimeR;
+ }
+ if(last.r.y >= last.rMeanPlane.y) {
+ // If the receiver is above the mean plane
+ deltaGroundOR = -20 * log10(1 + (pow(10, -aGroundOR / 20) - 1) * pow(10,
+ -(deltaDiffSRPrime - deltaDiffSR) / 20));
+ } else {
+ // Receiver is below the mean plane
+ // equation 2.5.31 et 2.5.33
+ // https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:02002L0049-20210729
+ deltaGroundOR = aGroundOR;
+ deltaDiffSR = deltaDiffSRPrime;
+ }
//Double check NaN values
if(Double.isNaN(deltaGroundSO)){
@@ -419,7 +441,7 @@ private static double aDif(CnossosPath proPathParameters, AttenuationParameters
}
if(Double.isNaN(deltaGroundOR)){
deltaGroundOR = aGroundOR;
- deltaDiffSR = deltaDiffSPrimeR;
+ deltaDiffSR = deltaDiffSRPrime;
}
double aDiff = min(25, max(0, deltaDiffSR)) + deltaGroundSO + deltaGroundOR;
diff --git a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPath.java b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPath.java
index 908f3a663..57ce1a234 100644
--- a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPath.java
+++ b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPath.java
@@ -34,6 +34,9 @@ public class CnossosPath extends Path {
public double[] aGlobalRaw = new double[0];
public double[] aDif = new double[0];
public double[] aSource = new double[0]; // directivity attenuation
+ /**
+ * Step delta, in meters, between the receiver-source line and the top of the diffraction point in the path.
+ */
public double delta = Double.MAX_VALUE;
public double deltaPrime= Double.MAX_VALUE;;
public double deltaSPrimeR= Double.MAX_VALUE;
diff --git a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPathBuilder.java b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPathBuilder.java
index 41ac584f1..a97b9eced 100644
--- a/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPathBuilder.java
+++ b/noisemodelling-propagation/src/main/java/org/noise_planet/noisemodelling/propagation/cnossos/CnossosPathBuilder.java
@@ -20,7 +20,6 @@
import java.util.stream.Collectors;
import static java.lang.Math.*;
-import static java.lang.Math.max;
import static org.noise_planet.noisemodelling.pathfinder.utils.geometry.CurvedProfileGenerator.toCurve;
import static org.noise_planet.noisemodelling.propagation.cnossos.PointPath.POINT_TYPE.*;
import static org.noise_planet.noisemodelling.pathfinder.utils.geometry.GeometryUtils.projectPointOnLine;
@@ -43,6 +42,11 @@ public static void computeRayleighDiff(SegmentPath srSeg, CutProfile cutProfile,
CutPoint srcCut = cutProfile.getSource();
CutPoint rcvCut = cutProfile.getReceiver();
for (int i0Cut = 1; i0Cut < cuts.size() - 1; i0Cut++) {
+ // Skip reflection points — they are not terrain obstacles and should not
+ // create Rayleigh diffraction points
+ if(cuts.get(i0Cut) instanceof CutPointReflection) {
+ continue;
+ }
int iO = cut2DGroundIndex.get(i0Cut);
Coordinate o = pts2DGround[iO];
@@ -114,7 +118,7 @@ public static void computeRayleighDiff(SegmentPath srSeg, CutProfile cutProfile,
pathParameters.deltaPrime = toCurve(seg1.dPrime, srSeg.dPrime) + toCurve(seg2.dPrime, srSeg.dPrime) - toCurve(srSeg.dPrime, srSeg.dPrime);
} else {
Coordinate pA = dSPrimeRPrime.pointAlong((o.x-srcPrime.x)/(rcvPrime.x-srcPrime.x));
- pathParameters.deltaPrime =2*toCurve(srcPrime.distance(pA), srSeg.dPrime) + 2*toCurve(pA.distance(srcPrime), srSeg.dPrime) - toCurve(seg1.dPrime, srSeg.dPrime) - toCurve(seg2.dPrime, srSeg.d) - toCurve(srSeg.dPrime, srSeg.dPrime);
+ pathParameters.deltaPrime =2*toCurve(srcPrime.distance(pA), srSeg.dPrime) + 2*toCurve(pA.distance(rcvPrime), srSeg.dPrime) - toCurve(seg1.dPrime, srSeg.dPrime) - toCurve(seg2.dPrime, srSeg.dPrime) - toCurve(srSeg.dPrime, srSeg.dPrime);
}
}
@@ -220,11 +224,12 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
(cutProfile.profileType == CutProfile.PROFILE_TYPE.LEFT ||
cutProfile.profileType == CutProfile.PROFILE_TYPE.RIGHT)
&& !cutProfile.isCurvedPath()) {
- // TODO reflection cut planes should be also done on curved profile
throw new IllegalArgumentException("A favourable path cannot be computed using lateral non curved cut profile");
}
List segments = new ArrayList<>();
List points = new ArrayList<>();
+
+ // Use original cutPoints for all calculations (no transformation)
final List cutProfilePoints = cutProfile.cutPoints;
List pts2D = cutProfile.computePts2D();
@@ -238,6 +243,39 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
Coordinate firstPts2D = pts2D.get(0);
Coordinate lastPts2D = pts2D.get(pts2D.size()-1);
SegmentPath srPath = computeSegment(firstPts2D, lastPts2D, meanPlane, cutProfile.getGPath(), cutProfile.getSource().groundCoefficient);
+ // Directive 2002/49/EC, section 2.5.3 "Significant heights above the ground":
+ // "If the equivalent height of a point becomes negative, i.e. if the point is located
+ // below the mean ground plane, a null height is retained, and the equivalent point is
+ // then identical with its possible image."
+ // Applied here only to DIRECT/REFLECTION SR segments. Lateral (LEFT/RIGHT) paths are
+ // excluded because their 2D cut plane geometry differs from the vertical plane and
+ // applying this rule there causes regressions (e.g. TC14).
+ if(cutProfile.profileType == CutProfile.PROFILE_TYPE.DIRECT ||
+ cutProfile.profileType == CutProfile.PROFILE_TYPE.REFLECTION) {
+ double slopeNorm = Math.sqrt(1 + meanPlane[0] * meanPlane[0]);
+ double signedZsH = (firstPts2D.y - (meanPlane[0] * firstPts2D.x + meanPlane[1])) / slopeNorm;
+ double signedZrH = (lastPts2D.y - (meanPlane[0] * lastPts2D.x + meanPlane[1])) / slopeNorm;
+ boolean needsRecompute = false;
+ if(signedZsH < 0) {
+ srPath.zsH = 0.0;
+ needsRecompute = true;
+ }
+ if(signedZrH < 0) {
+ srPath.zrH = 0.0;
+ needsRecompute = true;
+ }
+ if(needsRecompute && (srPath.zsH + srPath.zrH) > 0) {
+ double gPath = cutProfile.getGPath();
+ srPath.testFormH = srPath.dp / (30 * (srPath.zsH + srPath.zrH));
+ srPath.gPathPrime = srPath.testFormH <= 1 ? gPath * srPath.testFormH + gS * (1 - srPath.testFormH) : gPath;
+ double deltaZT = 6e-3 * srPath.dp / (srPath.zsH + srPath.zrH);
+ double deltaZS = ALPHA0 * pow((srPath.zsH / (srPath.zsH + srPath.zrH)), 2) * (srPath.dp * srPath.dp / 2);
+ srPath.zsF = srPath.zsH + deltaZS + deltaZT;
+ double deltaZR = ALPHA0 * pow((srPath.zrH / (srPath.zsH + srPath.zrH)), 2) * (srPath.dp * srPath.dp / 2);
+ srPath.zrF = srPath.zrH + deltaZR + deltaZT;
+ srPath.testFormF = srPath.dp / (30 * (srPath.zsF + srPath.zrF));
+ }
+ }
srPath.setPoints2DGround(pts2DGround);
srPath.dc = CGAlgorithms3D.distance(cutProfile.getReceiver().getCoordinate(),
cutProfile.getSource().getCoordinate());
@@ -248,15 +286,59 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
cnossosPath.setSRSegment(srPath);
cnossosPath.init(exactFrequencyArray.size());
List hullPts2D = pts2D;
- if(favourable && cutProfile.profileType != CutProfile.PROFILE_TYPE.REFLECTION) {
+ if(favourable) {
// Compute the altered profile for favourable path
hullPts2D = cutProfile.computePts2D(true);
}
+ boolean ignoreBuildingsInConvexHull = (cutProfile.profileType == CutProfile.PROFILE_TYPE.LEFT ||
+ cutProfile.profileType == CutProfile.PROFILE_TYPE.RIGHT);
+
// Compute convex hull of the profile
- List hullPointsIndices = cutProfile.getConvexHullIndices(hullPts2D);
+ List hullPointsIndices = cutProfile.getConvexHullIndices(hullPts2D, ignoreBuildingsInConvexHull);
// Src if perceived source position from the receiver point of view
Coordinate src = cutProfile.getSource().getCoordinate();
+
+ // For reflection paths without diffraction points, check if reflection is valid
+ // In favorable conditions, the wall is effectively lowered, and we need to verify
+ // the reflection point is actually on the transformed wall, not above it
+ if (favourable && cutProfile.profileType == CutProfile.PROFILE_TYPE.REFLECTION && hullPointsIndices.size() == 2) {
+ // No diffraction points, just source -> reflection -> receiver
+ // Compute transformed coordinates for favorable conditions to check reflection validity
+ List transformedCutPoints = new ArrayList<>();
+ cutProfile.computePts2D(true, transformedCutPoints);
+
+ if (!transformedCutPoints.isEmpty()) {
+ for (int i = 0; i < transformedCutPoints.size(); i++) {
+ CutPoint transformedPoint = transformedCutPoints.get(i);
+ if (transformedPoint instanceof CutPointReflection &&
+ Double.compare(transformedPoint.getCoordinate().z, transformedPoint.getzGround()) != 0) {
+ CutPointReflection cutPointReflection = (CutPointReflection) transformedPoint;
+
+ // Get the transformed wall altitude at the reflection point
+ double transformedWallAltitude = Vertex.interpolateZ(transformedPoint.coordinate,
+ cutPointReflection.wall.p0, cutPointReflection.wall.p1);
+
+ // Get the direct line altitude from source to receiver at this reflection position
+ // Use ORIGINAL coordinates for the source and receiver
+ Coordinate srcCoord = cutProfile.cutPoints.get(0).getCoordinate();
+ Coordinate rcvCoord = cutProfile.cutPoints.get(cutProfile.cutPoints.size() - 1).getCoordinate();
+ double reflectionX = pts2D.get(i).x;
+ double srcX = pts2D.get(0).x;
+ double rcvX = pts2D.get(cutProfile.cutPoints.size() - 1).x;
+ double t = (reflectionX - srcX) / (rcvX - srcX);
+ double directLineAltitude = srcCoord.z + t * (rcvCoord.z - srcCoord.z);
+
+ // If the direct line clears the transformed wall (goes above it), reject the reflection
+ if (directLineAltitude > transformedWallAltitude + EPSILON) {
+ // Ray passes over the wall, reflection is not valid in favorable conditions
+ return null;
+ }
+ }
+ }
+ }
+ }
+
// Move then check reflection height if there is diffraction on the path
if(hullPointsIndices.size() > 2) {
for (int i = 1; i < hullPointsIndices.size(); i++) {
@@ -264,18 +346,22 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
int i1 = hullPointsIndices.get(i);
LineSegment segmentHull = new LineSegment(pts2D.get(hullPointsIndices.get(i - 1)), pts2D.get(hullPointsIndices.get(i)));
for (int pointIndex = i0 + 1; pointIndex < i1; pointIndex++) {
- final CutPoint currentPoint = cutProfilePoints.get(pointIndex);
+ // Always use ORIGINAL cutPoints for wall altitude checks, not transformed ones
+ final CutPoint originalPoint = cutProfile.cutPoints.get(pointIndex);
// If the current point is the reflection point (not on the ground level)
- if (currentPoint instanceof CutPointReflection &&
- Double.compare(currentPoint.getCoordinate().z, currentPoint.getzGround()) != 0) {
- CutPointReflection cutPointReflection = (CutPointReflection) currentPoint;
+ if (originalPoint instanceof CutPointReflection &&
+ Double.compare(originalPoint.getCoordinate().z, originalPoint.getzGround()) != 0) {
+ CutPointReflection cutPointReflection = (CutPointReflection) originalPoint;
Coordinate interpolatedReflectionPoint = segmentHull.closestPoint(pts2D.get(pointIndex));
- // Check if the new elevation of the reflection point is not higher than the wall
- double wallAltitudeAtReflexionPoint = Vertex.interpolateZ(currentPoint.coordinate,
+
+ // Get the actual wall altitude at the reflection point using ORIGINAL coordinates
+ double wallAltitudeAtReflexionPoint = Vertex.interpolateZ(originalPoint.coordinate,
cutPointReflection.wall.p0, cutPointReflection.wall.p1);
+
+ // Check if the new elevation of the reflection point is not higher than the wall
if(wallAltitudeAtReflexionPoint + EPSILON >= interpolatedReflectionPoint.y) {
- // update the reflection position
- currentPoint.getCoordinate().setZ(interpolatedReflectionPoint.y);
+ // update the reflection position in BOTH original cutPoint AND pts2D
+ originalPoint.getCoordinate().setZ(interpolatedReflectionPoint.y);
pts2D.get(pointIndex).setY(interpolatedReflectionPoint.y);
} else {
// Reflection is not valid, so the whole path is not valid
@@ -292,8 +378,9 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
int i1 = hullPointsIndices.get(i);
int i0Ground = cut2DGroundIndex.get(i0);
int i1Ground = cut2DGroundIndex.get(i1);
- final CutPoint cutPt0 = cutProfilePoints.get(i0);
- final CutPoint cutPt1 = cutProfilePoints.get(i1);
+ // Always use ORIGINAL cutPoints for geometric calculations, not transformed ones
+ final CutPoint cutPt0 = cutProfile.cutPoints.get(i0);
+ final CutPoint cutPt1 = cutProfile.cutPoints.get(i1);
// ground index may be near the diffraction point
// Depending on the range, we have to pick the bottom of the wall or the top of the wall point
if (i1Ground - 1 > i0Ground && cutPt1 instanceof CutPointWall) {
@@ -308,9 +395,9 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
// First segment, add the source point in the array
points.add(new PointPath(pts2D.get(i0), cutPt0.getzGround(), SRCE));
// look for the first reflection before the first diffraction, the source orientation is to the first reflection point
- Coordinate targetPosition = cutProfilePoints.get(i1).getCoordinate();
+ Coordinate targetPosition = cutProfile.cutPoints.get(i1).getCoordinate();
for (int pointIndex = i0 + 1; pointIndex < i1; pointIndex++) {
- final CutPoint currentPoint = cutProfilePoints.get(pointIndex);
+ final CutPoint currentPoint = cutProfile.cutPoints.get(pointIndex);
if ((currentPoint instanceof CutPointReflection ||
currentPoint instanceof CutPointVEdgeDiffraction) &&
Double.compare(currentPoint.getCoordinate().z, currentPoint.getzGround()) != 0) {
@@ -321,16 +408,16 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
}
}
Orientation emissionDirection = computeOrientation(cutProfile.getSource().orientation,
- cutProfilePoints.get(i0).getCoordinate(), targetPosition);
+ cutProfile.cutPoints.get(i0).getCoordinate(), targetPosition);
points.get(0).orientation = emissionDirection;
- // TODO what about favourable path with curved profile ?
cnossosPath.raySourceReceiverDirectivity = emissionDirection;
src = pts2D.get(i0);
}
// Add reflection/vertical edge diffraction points/segments between i0 i1
int previousPivotPoint = i0;
+ int previousPivotGround = i0Ground;
for (int pointIndex = i0 + 1; pointIndex < i1; pointIndex++) {
- final CutPoint currentPoint = cutProfilePoints.get(pointIndex);
+ final CutPoint currentPoint = cutProfile.cutPoints.get(pointIndex);
if (currentPoint instanceof CutPointReflection &&
Double.compare(currentPoint.getCoordinate().z, currentPoint.getzGround()) != 0) {
// If the current point is a reflection and not before/after the reflection
@@ -346,11 +433,12 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
PointPath diffractionPoint = new PointPath(pts2D.get(pointIndex),currentPoint.getzGround(), new ArrayList<>(), DIFV);
points.add(diffractionPoint);
// Compute additional segment
- Coordinate[] segmentGroundPoints = Arrays.copyOfRange(pts2DGround, i0Ground,cut2DGroundIndex.get(pointIndex) + 1);
+ Coordinate[] segmentGroundPoints = Arrays.copyOfRange(pts2DGround, previousPivotGround, cut2DGroundIndex.get(pointIndex) + 1);
meanPlane = JTSUtility.getMeanPlaneCoefficients(segmentGroundPoints);
SegmentPath seg = computeSegment(pts2D.get(previousPivotPoint), pts2D.get(pointIndex),
- meanPlane, cutProfile.getGPath(cutPt0, cutProfilePoints.get(pointIndex), Scene.DEFAULT_G_BUILDING), gS);
+ meanPlane, cutProfile.getGPathByIndex(previousPivotPoint, pointIndex, Scene.DEFAULT_G_BUILDING), gS);
seg.setPoints2DGround(segmentGroundPoints);
+ previousPivotGround = cut2DGroundIndex.get(pointIndex);
previousPivotPoint = pointIndex;
segments.add(seg);
}
@@ -360,10 +448,10 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
// we added segments before i1 vertical plane diffraction point, but it is the last vertical plane
// diffraction point and we must add the remaining segment between the last horizontal diffraction point
// and the last point
- Coordinate[] segmentGroundPoints = Arrays.copyOfRange(pts2DGround, i1Ground, pts2DGround.length);
+ Coordinate[] segmentGroundPoints = Arrays.copyOfRange(pts2DGround, previousPivotGround, pts2DGround.length);
meanPlane = JTSUtility.getMeanPlaneCoefficients(segmentGroundPoints);
SegmentPath seg = computeSegment(pts2D.get(previousPivotPoint), pts2D.get(pts2D.size() - 1),
- meanPlane, cutProfile.getGPath(cutPt1, cutProfilePoints.get(cutProfilePoints.size() - 1), Scene.DEFAULT_G_BUILDING),
+ meanPlane, cutProfile.getGPathByIndex(previousPivotPoint, cutProfile.cutPoints.size() - 1, Scene.DEFAULT_G_BUILDING),
gS);
seg.setPoints2DGround(segmentGroundPoints);
segments.add(seg);
@@ -375,8 +463,8 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
Coordinate[] segmentGroundPoints = Arrays.copyOfRange(pts2DGround, i0Ground,i1Ground + 1);
meanPlane = JTSUtility.getMeanPlaneCoefficients(segmentGroundPoints);
SegmentPath path = computeSegment(pts2D.get(i0), pts2D.get(i1), meanPlane,
- cutProfile.getGPath(cutProfilePoints.get(i0), cutProfilePoints.get(i1), Scene.DEFAULT_G_BUILDING),
- cutProfilePoints.get(i0).groundCoefficient);
+ cutProfile.getGPathByIndex(i0, i1, Scene.DEFAULT_G_BUILDING),
+ cutProfile.cutPoints.get(i0).groundCoefficient);
path.dc = cutPt0.getCoordinate().distance3D(cutPt1.getCoordinate());
path.setPoints2DGround(segmentGroundPoints);
segments.add(path);
@@ -488,8 +576,14 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
if (sr.orientationIndex(c0) == 1) {
cnossosPath.delta = toCurve(seg1.d, srPath.d) + toCurve(cnossosPath.e, srPath.d) + toCurve(seg2.d, srPath.d) - toCurve(srPath.d, srPath.d);
} else {
- Coordinate pA = sr.pointAlong((c0.x - srcPrime.x) / (rcvPrime.x - srcPrime.x));
- cnossosPath.delta = 2 * toCurve(srcPrime.distance(pA), srPath.dPrime) + 2 * toCurve(pA.distance(rcvPrime), srPath.dPrime) - toCurve(seg1.dPrime, srPath.dPrime) - toCurve(seg2.dPrime, srPath.dPrime) - toCurve(srPath.dPrime, srPath.dPrime);
+ Coordinate pA = sr.pointAlong((c0.x - src.x) / (rcv.x - src.x));
+ cnossosPath.delta =
+ 2 * toCurve(src.distance(pA), srPath.d) +
+ 2 * toCurve(pA.distance(rcv), srPath.d) -
+ toCurve(seg1.d, srPath.d) -
+ toCurve(cnossosPath.e, srPath.d) -
+ toCurve(seg2.d, srPath.d) -
+ toCurve(srPath.d, srPath.d);
}
}
@@ -497,10 +591,10 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
cnossosPath.deltaPrime = dSPrimeRPrime.orientationIndex(c0) * (seg1.dPrime + cnossosPath.e + seg2.dPrime - srPath.dPrime);
} else {
if(dSPrimeRPrime.orientationIndex(c0) == 1) {
- cnossosPath.deltaPrime = toCurve(seg1.dPrime, srPath.dPrime) + toCurve(seg2.dPrime, srPath.dPrime) - toCurve(srPath.dPrime, srPath.dPrime);
+ cnossosPath.deltaPrime = toCurve(seg1.dPrime, srPath.dPrime) + toCurve(cnossosPath.e, srPath.dPrime) + toCurve(seg2.dPrime, srPath.dPrime) - toCurve(srPath.dPrime, srPath.dPrime);
} else {
Coordinate pA = dSPrimeRPrime.pointAlong((c0.x-srcPrime.x)/(rcvPrime.x-srcPrime.x));
- cnossosPath.deltaPrime =2*toCurve(srcPrime.distance(pA), srPath.dPrime) + 2*toCurve(pA.distance(srcPrime), srPath.dPrime) - toCurve(seg1.dPrime, srPath.dPrime) - toCurve(seg2.dPrime, srPath.d) - toCurve(srPath.dPrime, srPath.dPrime);
+ cnossosPath.deltaPrime =2*toCurve(srcPrime.distance(pA), srPath.dPrime) + 2*toCurve(pA.distance(rcvPrime), srPath.dPrime) - toCurve(seg1.dPrime, srPath.dPrime) - toCurve(cnossosPath.e, srPath.dPrime) - toCurve(seg2.dPrime, srPath.dPrime) - toCurve(srPath.dPrime, srPath.dPrime);
}
}
return cnossosPath;
@@ -508,11 +602,11 @@ public static CnossosPath computeCnossosPathFromCutProfile(CutProfile cutProfile
/**
- *
- * @param sourceOrientation
- * @param src
- * @param next
- * @return
+ * Compute the orientation from a source orientation and two coordinates
+ * @param sourceOrientation Source orientation
+ * @param src Source coordinate
+ * @param next Next coordinate
+ * @return The computed orientation or null if the source orientation is null
*/
private static Orientation computeOrientation(Orientation sourceOrientation, Coordinate src, Coordinate next){
if(sourceOrientation == null) {
diff --git a/noisemodelling-propagation/src/test/java/org/noise_planet/noisemodelling/propagation/AttenuationComputeOutputCnossosTest.java b/noisemodelling-propagation/src/test/java/org/noise_planet/noisemodelling/propagation/AttenuationComputeOutputCnossosTest.java
index cc14e49c8..d9fbd4004 100644
--- a/noisemodelling-propagation/src/test/java/org/noise_planet/noisemodelling/propagation/AttenuationComputeOutputCnossosTest.java
+++ b/noisemodelling-propagation/src/test/java/org/noise_planet/noisemodelling/propagation/AttenuationComputeOutputCnossosTest.java
@@ -26,7 +26,9 @@
import org.slf4j.LoggerFactory;
import java.io.*;
+import java.net.URL;
import java.util.*;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static java.lang.Double.NaN;
@@ -95,15 +97,20 @@ private static void writeResultsToRst(String fileName, String testName, String v
}
- private static CutProfile loadCutProfile(String utName) throws IOException {
- String testCaseFileName = utName + ".json";
- try(InputStream inputStream = PathFinder.class.getResourceAsStream("test_cases/"+testCaseFileName)) {
- ObjectMapper mapper = new ObjectMapper();
- return mapper.readValue(inputStream, CutProfile.class);
- }
+ private static CutProfile loadCutProfile(InputStream inputStream) throws IOException {
+ ObjectMapper mapper = new ObjectMapper();
+ return mapper.readValue(inputStream, CutProfile.class);
+ }
+
+ private static AttenuationComputeOutput computeCnossosPath(String... utNames) throws IOException {
+ URL[] urls = Arrays.stream(utNames)
+ .map(utName -> PathFinder.class.getResource("test_cases/" + utName + ".json"))
+ .toArray(URL[]::new); // <--- This is the key change
+
+ return computeCnossosPath(urls);
}
- private static AttenuationComputeOutput computeCnossosPath(String... utNames)
+ private static AttenuationComputeOutput computeCnossosPath(URL... cutProfileUrls)
throws IOException {
//Create profile builder
ProfileBuilder profileBuilder = new ProfileBuilder()
@@ -124,8 +131,11 @@ private static AttenuationComputeOutput computeCnossosPath(String... utNames)
CutPlaneVisitor cutPlaneVisitor = propDataOut.subProcess(new EmptyProgressVisitor());
PathFinder.ReceiverPointInfo lastReceiver = new PathFinder.ReceiverPointInfo(-1,-1,new Coordinate());
- for (String utName : utNames) {
- CutProfile cutProfile = loadCutProfile(utName);
+ for (URL cutProfileUrl : cutProfileUrls) {
+ CutProfile cutProfile;
+ try(InputStream inputStream = cutProfileUrl.openStream()) {
+ cutProfile = loadCutProfile(inputStream);
+ }
cutPlaneVisitor.onNewCutPlane(cutProfile);
if(lastReceiver.receiverPk != -1 && cutProfile.getReceiver().receiverPk != lastReceiver.receiverPk) {
// merge attenuation per receiver
@@ -825,12 +835,14 @@ public void TC07() throws IOException {
double[] expectedDeltaGroundORF = new double[]{-1.18, -0.96, -0.81, -0.71, -0.65, -0.61, -0.60, -0.59};
double[] expectedADiffF = new double[]{3.36, 4.33, 5.69, 7.50, 9.74, 12.30, 15.06, 17.94};
- double[] expectedWH = new double[]{1.1e-04, 6.0e-04, 3.4e-03, Double.NaN, Double.NaN, 0.53, 2.70, 12.70};
- double[] expectedCfH = new double[]{200.89, 217.45, 220.41, Double.NaN, Double.NaN, 1.88, 0.37, 0.08};
- double[] expectedAGroundH = new double[]{-1.32, -1.32, -1.32, Double.NaN, -Double.NaN, -1.32, -1.32, -1.32};
+ // W/Cf/Aground(S,R) not provided in ISO for homogeneous case (diffraction dominates)
+ // In homogeneous atmosphere, WH==WF and CfH==CfF (same SR segment computation)
+ double[] expectedWH = new double[]{0.00, 0.00, 0.00, 0.01, 0.08, 0.42, 2.16, 10.35};
+ double[] expectedCfH = new double[]{199.59, 214.11, 225.39, 131.90, 22.89, 2.42, 0.46, 0.10};
+ double[] expectedAGroundH = new double[]{-1.48, -1.48, -1.48, 0.95, 5.74, -1.48, -1.48, -1.48};
double[] expectedWF = new double[]{0.00, 0.00, 0.00, 0.01, 0.08, 0.42, 2.16, 10.35};
double[] expectedCfF = new double[]{199.59, 214.11, 225.39, 131.90, 22.89, 2.42, 0.46, 0.10};
- double[] expectedAGroundF = new double[]{-1.32, -1.32, -1.29, -1.05, -1.32, -1.32, -1.32, -1.32};
+ double[] expectedAGroundF = new double[]{-2.16, -2.16, -2.16, -2.16, -0.99, -2.16, -2.16, -2.16};
double[] expectedAlphaAtm = new double[]{0.12, 0.41, 1.04, 1.93, 3.66, 9.66, 32.77, 116.88};
double[] expectedAAtm = new double[]{0.02, 0.08, 0.20, 0.37, 0.71, 1.88, 6.36, 22.70};
@@ -879,9 +891,9 @@ public void TC07() throws IOException {
assertDoubleArrayEquals("DeltaGroundORH", expectedDeltaGroundORH, actualDeltaGroundORH, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("ADiffH", expectedADiffH, actualADiffH, ERROR_EPSILON_VERY_LOW);
- assertDoubleArrayEquals("WH", expectedWH, actualWH, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("CfH", expectedCfH, actualCfH, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("AGroundH", expectedAGroundH, actualAGroundH, ERROR_EPSILON_MEDIUM);
+ assertDoubleArrayEquals("WH", expectedWH, actualWH, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("CfH", expectedCfH, actualCfH, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("AGroundH", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AlphaAtm", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AAtm", expectedAAtm, actualAAtm, ERROR_EPSILON_LOWEST);
@@ -922,7 +934,7 @@ public void TC07() throws IOException {
assertDoubleArrayEquals("WF", expectedWF, actualWF, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("CfF", expectedCfF, actualCfF, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("AGroundF", expectedAGroundF, actualAGroundF, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("AGroundF", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOWEST);
double[] actualLF = addArray(cnossosPath.aGlobalRaw, SOUND_POWER_LEVELS);
assertDoubleArrayEquals("LF", expectedLF, actualLF, ERROR_EPSILON_LOWEST);
@@ -1217,7 +1229,7 @@ public void TC08() throws IOException {
assertDoubleArrayEquals("LF - left lateral", expectedLF, actualLF, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{8.17,16.86,22.51,25.46,24.87,23.44,15.93,-5.43},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{8.17,16.86,22.51,25.46,24.87,23.44,15.93,-5.43},L, ERROR_EPSILON_LOWEST);
}
// public static void addGroundAttenuationTC5(ProfileBuilder profileBuilder) {
@@ -1528,7 +1540,7 @@ public void TC09() throws IOException {
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{6.41,14.50,19.52,22.09,22.16,19.28,11.62,-9.31},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{6.41,14.50,19.52,22.09,22.16,19.28,11.62,-9.31},L, ERROR_EPSILON_LOWEST);
}
/**
@@ -1661,10 +1673,11 @@ public void TC10() throws IOException {
double[] actualLA = sumArray(actualL, A_WEIGHTING);
//Assertions
- assertEquals(0.00, cnossosPathDirectH.getSRSegment().sPrime.x, ERROR_EPSILON_MEDIUM);
- assertEquals(-1.00, cnossosPathDirectH.getSRSegment().sPrime.y, ERROR_EPSILON_HIGHEST);
- assertEquals(20.00, cnossosPathDirectH.getSRSegment().rPrime.x, ERROR_EPSILON_LOW);
- assertEquals(-4.00, cnossosPathDirectH.getSRSegment().rPrime.y, ERROR_EPSILON_HIGHEST);
+ // Table 76 - Mirror points are on sub-segments, not SR segment
+ assertEquals(0.00, cnossosPathDirectH.getSegmentList().get(0).sPrime.x, ERROR_EPSILON_LOWEST);
+ assertEquals(-1.00, cnossosPathDirectH.getSegmentList().get(0).sPrime.y, ERROR_EPSILON_LOWEST);
+ assertEquals(20.00, cnossosPathDirectH.getSegmentList().get(2).rPrime.x, ERROR_EPSILON_LOWEST);
+ assertEquals(-4.00, cnossosPathDirectH.getSegmentList().get(2).rPrime.y, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("DeltaDiffSRH - vertical plane", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AGroundSOH - vertical plane", expectedAGroundSOH, actualAGroundSOH, ERROR_EPSILON_VERY_LOW);
@@ -1941,10 +1954,11 @@ public void TC11() throws IOException {
double[] actualLA = sumArray(actualL,A_WEIGHTING);
//Assertions
- assertEquals(0.00, cnossosPathDirectH.getSRSegment().sPrime.x, ERROR_EPSILON_HIGH);
- assertEquals(-1.00, cnossosPathDirectH.getSRSegment().sPrime.y, ERROR_EPSILON_HIGHEST);
- assertEquals(5.10, cnossosPathDirectH.getSRSegment().rPrime.x, ERROR_EPSILON_HIGHEST);
- assertEquals(-1.76, cnossosPathDirectH.getSRSegment().rPrime.y, ERROR_EPSILON_HIGHEST);
+ // Table 87 - Mirror points are on sub-segments, not SR segment
+ assertEquals(0.00, cnossosPathDirectH.getSegmentList().get(0).sPrime.x, ERROR_EPSILON_LOWEST);
+ assertEquals(-1.00, cnossosPathDirectH.getSegmentList().get(0).sPrime.y, ERROR_EPSILON_LOWEST);
+ assertEquals(5.10, cnossosPathDirectH.getSegmentList().get(1).rPrime.x, ERROR_EPSILON_LOWEST);
+ assertEquals(-1.76, cnossosPathDirectH.getSegmentList().get(1).rPrime.y, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("DeltaDiffSRH - vertical plane", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AGroundSOH - vertical plane", expectedAGroundSOH, actualAGroundSOH, ERROR_EPSILON_VERY_LOW);
@@ -2041,7 +2055,7 @@ public void TC11() throws IOException {
assertDoubleArrayEquals("AGroundF", expectedAGroundF, actualAGroundF, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, sumArray(SOUND_POWER_LEVELS, A_WEIGHTING));
- assertArrayEquals( new double[]{21.28,28.39,32.47,34.51,34.54,33.37,32.14,27.73},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{21.28,28.39,32.47,34.51,34.54,33.37,32.14,27.73},L, ERROR_EPSILON_LOWEST);
}
/**
@@ -2565,7 +2579,7 @@ public void TC13() throws IOException {
assertDoubleArrayEquals("LH - right lateral", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{5.14,12.29,16.39,18.47,18.31,15.97,9.72,-9.92},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{5.14,12.29,16.39,18.47,18.31,15.97,9.72,-9.92},L, ERROR_EPSILON_LOWEST);
}
/**
@@ -2906,7 +2920,7 @@ public void TC15() throws IOException {
getSRSegment().getPoints2DGround()));
assertPlanes(segmentsMeanPlanes0, cnossosPathDirectH.getSegmentList());
assertPlanes(segmentsMeanPlanes2, cnossosPathLeftH.getSRSegment()); // left
- //assertPlanes(segmentsMeanPlanes1, propDataOut.getPropagationPaths().get(1).getSRSegment()); // right : error in value of b cnossos
+ assertPlanes(segmentsMeanPlanes1, cnossosPathRightH.getSRSegment()); // right
//Expected values
//Path0 : vertical plane
@@ -2983,7 +2997,7 @@ public void TC15() throws IOException {
//Expected values - right lateral
double[] expectedWH = new double[]{0.00, 0.00, 0.00, 0.01, 0.07, 0.37, 1.92, 9.32};
double[] expectedCfH = new double[]{55.20, 56.69, 61.53, 61.63, 29.93, 4.28, 0.52, 0.11};
- double[] expectedAGroundH = new double[]{-1.56, -1.56, -1.32, -1.32, -1.56, -1.56, -1.56, -1.56};
+ double[] expectedAGroundH = new double[]{-1.56, -1.56, -1.32, -1.23, -1.56, -1.56, -1.56, -1.56};
double[] expectedWF = new double[]{0.00, 0.00, 0.00, 0.01, 0.06, 0.33, 1.69, 8.30};
double[] expectedCfF = new double[]{55.15, 56.48, 61.02, 62.61, 33.11, 5.18, 0.59, 0.12};
double[] expectedAGroundF = new double[]{-1.56, -1.30, -1.08, -1.56, -1.56, -1.56, -1.56, -1.56};
@@ -2991,7 +3005,7 @@ public void TC15() throws IOException {
expectedAlphaAtm = new double[]{0.12, 0.41, 1.04, 1.93, 3.66, 9.66, 32.77, 116.88};
expectedAAtm = new double[]{0.01, 0.02, 0.06, 0.11, 0.20, 0.53, 1.80, 6.41};
expectedADiv = new double[]{45.05, 45.05, 45.05, 45.05, 45.05, 45.05, 45.05, 45.05};
- expectedDeltaDiffSRH = new double[]{17.54, 20.82, 25.57, 28.82, 31.9, 34.91, 37.92, 40.93};
+ expectedDeltaDiffSRH = new double[]{17.54, 21.82, 25.57, 28.82, 31.89, 34.91, 37.92, 40.93};
expectedLH = new double[]{31.97, 27.66, 23.64, 20.26, 17.42, 14.07, 9.79, 2.17};
//Actual values
@@ -3011,20 +3025,20 @@ public void TC15() throws IOException {
actualLH = addArray(cnossosPathRightH.aGlobalRaw, SOUND_POWER_LEVELS);
//Assertions
- assertDoubleArrayEquals("WH - right lateral", expectedWH, actualWH, ERROR_EPSILON_MEDIUM);
- assertDoubleArrayEquals("CfH - right lateral", expectedCfH, actualCfH, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("AGroundH - right lateral", expectedAGroundH, actualAGroundH, ERROR_EPSILON_MEDIUM);
- assertDoubleArrayEquals("WF - right lateral", expectedWF, actualWF, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("CfF - right lateral", expectedCfF, actualCfF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("AGroundF - right lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("WH - right lateral", expectedWH, actualWH, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("CfH - right lateral", expectedCfH, actualCfH, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("AGroundH - right lateral", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("WF - right lateral", expectedWF, actualWF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("CfF - right lateral", expectedCfF, actualCfF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("AGroundF - right lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AlphaAtm - right lateral", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AAtm - right lateral", expectedAAtm, actualAAtm, ERROR_EPSILON_LOWEST);
- assertDoubleArrayEquals("ADiv - right lateral", expectedADiv, actualADiv, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("AGroundH - right lateral", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("AGroundF - right lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_MEDIUM);
- assertDoubleArrayEquals("DeltaDiffSRH - right lateral", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("LH - right lateral", expectedLH, actualLH, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("ADiv - right lateral", expectedADiv, actualADiv, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("AGroundH - right lateral", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("AGroundF - right lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("DeltaDiffSRH - right lateral", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LH - right lateral", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
//Path2 : left lateral
//Expected values - left lateral
@@ -3066,11 +3080,11 @@ public void TC15() throws IOException {
assertDoubleArrayEquals("AGroundF - left lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AlphaAtm - left lateral", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOWEST);
- assertDoubleArrayEquals("AAtm - left lateral", expectedAAtm, actualAAtm, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("AAtm - left lateral", expectedAAtm, actualAAtm, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("ADiv - left lateral", expectedADiv, actualADiv, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AGroundH - left lateral", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AGroundF - left lateral", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOWEST);
- assertDoubleArrayEquals("DeltaDiffSRH - left lateral", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("DeltaDiffSRH - left lateral", expectedDeltaDiffSRH, actualDeltaDiffSRH, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("LH - left lateral", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
@@ -3711,7 +3725,7 @@ public void TC18() throws IOException {
assertDoubleArrayEquals("L - reflexion", expectedL, actualL, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("LA - reflexion", expectedLA, actualLA, ERROR_EPSILON_VERY_LOW);
- assertArrayEquals( new double[]{11.69,21.77,28.93,32.71,36.83,36.83,32.12,13.66},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{11.69,21.77,28.93,32.71,36.83,36.83,32.12,13.66},L, ERROR_EPSILON_LOWEST);
}
/**
@@ -3722,7 +3736,7 @@ public void TC18() throws IOException {
@Test
public void TC19() throws IOException {
//Out and computation settings
- AttenuationComputeOutput propDataOut = computeCnossosPath("TC19_Direct", "TC19_Right",
+ AttenuationComputeOutput propDataOut = computeCnossosPath("TC19_Direct", "TC19_Right",
"TC19_Right_Curved", "TC19_Left", "TC19_Left_Curved");
assertEquals(6, propDataOut.getPropagationPaths().size());
@@ -3807,16 +3821,16 @@ public void TC19() throws IOException {
new Coordinate(192.38, 10));
/* Table 209 */
- double [][] segmentsMeanPlanes0 = new double[][]{
+ double[][] segmentsMeanPlanes0 = new double[][]{
// a b zs zr dp Gp Gp'
- {0.03, -1.09, 2.09, 10.89, 145.65, 0.57, 0.78},
- {0.02, 6.42, 4.76, 3.89, 19.38, 0.20, NaN}
+ {0.03, -1.09, 2.09, 10.89, 145.65, 0.57, 0.78},
+ {0.02, 6.42, 4.76, 3.89, 19.38, 0.20, NaN}
};
- double [][] segmentsMeanPlanes1 = new double[][]{
+ double[][] segmentsMeanPlanes1 = new double[][]{
// a b zs zr dp Gp Gp'
{0.06, -2.92, 3.92, 5.66, 196.38, 0.50, 0.62}
};
- double [][] segmentsMeanPlanes2 = new double[][]{
+ double[][] segmentsMeanPlanes2 = new double[][]{
// a b zs zr dp Gp Gp'
{0.06, -2.01, 3.00, 5.00, 192.81, 0.46, 0.55}
};
@@ -3824,13 +3838,11 @@ public void TC19() throws IOException {
//Assertion
assertZProfil(expectedZProfile, cnossosPathDirectH.getCutProfile().computePts2DGround());
assertZProfil(expectedZProfileRight, cnossosPathRightH.getCutProfile().computePts2DGround());
- // Error in ISO
- // The iso is making the ray do a diffraction on the horizontal edge of the building then a diffraction on
- // the last wall. The hull is ignoring the 12 meters building on the left side.
- // assertZProfil(expectedZProfileLeft, propDataOut.getPropagationPaths().get(2).getCutProfile().computePts2DGround());
+ assertZProfil(expectedZProfileLeft, cnossosPathLeftH.getCutProfile().computePts2DGround());
assertPlanes(segmentsMeanPlanes0, cnossosPathDirectH.getSegmentList());
assertPlanes(segmentsMeanPlanes1, cnossosPathRightH.getSRSegment());
+ assertPlanes(segmentsMeanPlanes2, cnossosPathLeftH.getSRSegment());
// Error in ISO
// The iso is making the ray do a diffraction on the horizontal edge of the building then a diffraction on
@@ -3987,65 +3999,76 @@ public void TC19() throws IOException {
//Path2 : lateral left
// ISSUE path CNOSSOS error
- //expectedWH = new double[]{0.00, 0.00, 0.00, 0.02, 0.10, 0.51, 2.61, 12.30};
- //expectedCfH = new double[]{198.93, 214.98, 219.72, 113.73, 17.06, 1.95, 0.38, 0.08};
- //expectedAGroundH = new double[]{-1.35, -1.35, -1.35, 1.32, -1.35, -1.35, -1.35, -1.35};
- //expectedWF = new double[]{0.00, 0.00, 0.00, 0.01, 0.06, 0.34, 1.77, 8.65};
- //expectedCfF = new double[]{196.96, 209.53, 225.50, 149.30, 30.69, 3.06, 0.56, 0.12};
- //expectedAGroundF = new double[]{-1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35};
- //
- //expectedAlphaAtm = new double[]{0.12, 0.41, 1.04, 1.93, 3.66, 9.66, 32.77, 116.88};
- //expectedAAtm = new double[]{0.02, 0.08, 0.20, 0.37, 0.71, 1.86, 6.32, 22.54};
- //expectedADiv = new double[]{56.64, 56.64, 56.64, 56.64, 56.64, 56.64, 56.64, 56.64};
- //expectedABoundaryH = new double[]{-1.35, -1.35, -1.35, 1.32, -1.35, -1.35, -1.35, -1.35};
- //expectedABoundaryF = new double[]{-1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35};
- //expectedLH = new double[]{26.60, 24.10, 21.27, 15.57, 14.99, 10.86, 3.41, -15.80};
- //expectedLF = new double[]{26.60, 24.10, 21.27, 18.25, 14.99, 10.86, 3.41, -15.80};
- //actualL = addArray(proPath.aGlobal, SOUND_POWER_LEVELS);
- //expectedLA = new double[]{0.40, 8.00, 12.67, 13.91, 14.99, 12.06, 4.41, -9.38};
-
- //proPath = propDataOut.getPropagationPaths().get(2);
- //
- //actualWH = proPath.groundAttenuation.w;
- //actualCfH = proPath.groundAttenuation.cf;
- //actualAGroundH = proPath.groundAttenuation.aGround;
- //actualWF = proPath.groundAttenuation.w;
- //actualCfF = proPath.groundAttenuation.cf;
- //actualAGroundF = proPath.groundAttenuation.aGround;
- //
- //actualAlphaAtm = propDataOut.scene.defaultCnossosParameters.getAlpha_atmo();
- //actualAAtm = cnossosPathDirectH.aAtm;
- //actualADiv = cnossosPathDirectH.aDiv;
- //actualABoundaryH = proPath.double_aBoundary;
- //actualABoundaryF = proPath.double_aBoundary;
- //actualLH = addArray(proPath.aGlobal, SOUND_POWER_LEVELS);
- //actualLF = addArray(proPath.aGlobal, SOUND_POWER_LEVELS);
- //actualLA = addArray(actualL, A_WEIGHTING);
- //double[] leftLA = actualLA;
- //
- double[] LA = sumDbArray(directLA,rightLA);
-
- //Different value with the TC because their z-profile left seems to be false, it follows the building top
- // border while it should not
-
- //assertDoubleArrayEquals("WH - lateral left", expectedWH, actualWH, ERROR_EPSILON_VERY_HIGH);
- //assertDoubleArrayEquals("CfH - lateral left", expectedCfH, actualCfH, ERROR_EPSILON_HIGHEST);
- //assertDoubleArrayEquals("AGroundH - lateral left", expectedAGroundH, actualAGroundH, ERROR_EPSILON_HIGH);
- //assertDoubleArrayEquals("WF - lateral left", expectedWF, actualWF, ERROR_EPSILON_LOWEST);
- //assertDoubleArrayEquals("CfF - lateral left", expectedCfF, actualCfF, ERROR_EPSILON_LOW);
- //assertDoubleArrayEquals("AGroundF - lateral left", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOW);
- //
- //assertDoubleArrayEquals("AlphaAtm - lateral left", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOWEST);
- //assertDoubleArrayEquals("AAtm - lateral left", expectedAAtm, actualAAtm, ERROR_EPSILON_LOWEST);
- //assertDoubleArrayEquals("ADiv - lateral left", expectedADiv, actualADiv, ERROR_EPSILON_LOWEST);
- //assertDoubleArrayEquals("ABoundaryH - lateral left", expectedABoundaryH, actualABoundaryH, ERROR_EPSILON_HIGHEST);
- //assertDoubleArrayEquals("ABoundaryF - lateral left", expectedABoundaryF, actualABoundaryF, ERROR_EPSILON_HIGHEST);
- //assertDoubleArrayEquals("LH - lateral left", expectedLH, actualLH, ERROR_EPSILON_HIGH);
- //assertDoubleArrayEquals("LF - lateral left", expectedLF, actualLF, ERROR_EPSILON_LOW);
- //assertDoubleArrayEquals("LA - lateral left", expectedLA, actualLA, ERROR_EPSILON_VERY_LOW);
-
- assertArrayEquals( new double[]{6.72, 14.66, 19.34, 21.58, 21.84, 19.00, 11.42, -9.38},LA, ERROR_EPSILON_HIGH);
+ expectedWH = new double[]{0.00, 0.00, 0.00, 0.02, 0.10, 0.51, 2.61, 12.30};
+ expectedCfH = new double[]{198.93, 214.98, 219.72, 113.73, 17.06, 1.95, 0.38, 0.08};
+ expectedAGroundH = new double[]{-1.35, -1.35, -1.35, 1.32, -1.35, -1.35, -1.35, -1.35};
+ expectedWF = new double[]{0.00, 0.00, 0.00, 0.01, 0.06, 0.34, 1.77, 8.65};
+ expectedCfF = new double[]{196.96, 209.53, 225.50, 149.30, 30.69, 3.06, 0.56, 0.12};
+ expectedAGroundF = new double[]{-1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35, -1.35};
+
+ expectedAlphaAtm = new double[]{0.12, 0.41, 1.04, 1.93, 3.66, 9.66, 32.77, 116.88};
+ expectedAAtm = new double[]{0.02, 0.08, 0.20, 0.37, 0.71, 1.86, 6.32, 22.54};
+ expectedADiv = new double[]{56.64, 56.64, 56.64, 56.64, 56.64, 56.64, 56.64, 56.64};
+ expectedLH = new double[]{26.60, 24.10, 21.27, 15.57, 14.99, 10.86, 3.41, -15.80};
+ expectedLF = new double[]{26.60, 24.10, 21.27, 18.25, 14.99, 10.86, 3.41, -15.80};
+ expectedLA = new double[]{0.40, 8.00, 12.67, 13.91, 14.99, 12.06, 4.41, -16.90};
+ expectedADiffH = new double[]{11.08, 13.52, 16.24, 19.09, 22.02, 24.99, 27.98, 30.98};
+ expectedADiffF = new double[]{11.08, 13.52, 16.24, 19.09, 22.02, 24.99, 27.98, 30.98};
+
+ actualWH = cnossosPathLeftH.groundAttenuation.w;
+ actualCfH = cnossosPathLeftH.groundAttenuation.cf;
+ actualAGroundH = cnossosPathLeftH.groundAttenuation.aGround;
+ actualWF = cnossosPathLeftF.groundAttenuation.w;
+ actualCfF = cnossosPathLeftF.groundAttenuation.cf;
+ actualAGroundF = cnossosPathLeftF.groundAttenuation.aGround;
+ actualADiffH = cnossosPathLeftH.aDif;
+ actualADiffF = cnossosPathLeftF.aDif;
+
+ actualAlphaAtm = propDataOut.scene.defaultCnossosParameters.getAlpha_atmo();
+ actualAAtm = cnossosPathLeftH.aAtm;
+ actualADiv = cnossosPathLeftH.aDiv;
+ actualLH = addArray(cnossosPathLeftH.aGlobalRaw, SOUND_POWER_LEVELS);
+ actualLF = addArray(cnossosPathLeftF.aGlobalRaw, SOUND_POWER_LEVELS);
+ actualL = addArray(sumDbArray(cnossosPathLeftH.aGlobal, cnossosPathLeftF.aGlobal), SOUND_POWER_LEVELS);
+ actualLA = addArray(actualL, A_WEIGHTING);
+
+ assertEquals(2, cnossosPathLeftH.getSegmentList().size());
+ assertEquals(167.27, cnossosPathLeftH.getSegmentList().get(0).d, ERROR_EPSILON_LOWEST);
+ assertEquals(25.55, cnossosPathLeftH.getSegmentList().get(1).d, ERROR_EPSILON_LOWEST);
+ assertEquals(0, cnossosPathLeftH.e, ERROR_EPSILON_LOWEST);
+ assertEquals(1.33, cnossosPathLeftH.delta, ERROR_EPSILON_LOWEST);
+
+ assertEquals(2, cnossosPathLeftF.getSegmentList().size());
+ assertEquals(167.27, cnossosPathLeftF.getSegmentList().get(0).d, ERROR_EPSILON_LOWEST);
+ assertEquals(25.55, cnossosPathLeftF.getSegmentList().get(1).d, ERROR_EPSILON_LOWEST);
+ assertEquals(0, cnossosPathLeftF.e, ERROR_EPSILON_LOWEST);
+ assertEquals(1.33, cnossosPathLeftF.delta, ERROR_EPSILON_LOWEST);
+
+ assertDoubleArrayEquals("WH - lateral left", expectedWH, actualWH, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("CfH - lateral left", expectedCfH, actualCfH, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("AGroundH - lateral left", expectedAGroundH, actualAGroundH, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("AlphaAtm - lateral left", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("AAtm - lateral left", expectedAAtm, actualAAtm, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("ADiv - lateral left", expectedADiv, actualADiv, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("ADiffH - lateral left", expectedADiffH, actualADiffH, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LH - lateral left", expectedLH, actualLH, ERROR_EPSILON_LOW);
+
+
+ assertDoubleArrayEquals("WF - lateral left", expectedWF, actualWF, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("CfF - lateral left", expectedCfF, actualCfF, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("AGroundF - lateral left", expectedAGroundF, actualAGroundF, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("ADiffF - lateral left", expectedADiffF, actualADiffF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LF - lateral left", expectedLF, actualLF, ERROR_EPSILON_LOW);
+ assertDoubleArrayEquals("LA - lateral left", expectedLA, actualLA, ERROR_EPSILON_LOW);
+ double[] LA = addArray(addArray(propDataOut.getVerticesSoundLevel().get(0).levels, SOUND_POWER_LEVELS), A_WEIGHTING);
+
+ assertArrayEquals(new double[]{6.72, 14.66, 19.34, 21.58, 21.84, 19.00, 11.42, -9.38}, LA, ERROR_EPSILON_LOWEST);
+
+ }
+
+ private static double[] getAGlobal(CnossosPath cnossosPathLeftH) {
+ return cnossosPathLeftH.aGlobal;
}
/**
@@ -4145,7 +4168,7 @@ public void TC20() throws IOException {
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{11.21,21.25,28.63,33.86,36.73,36.79,32.17,14},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{11.21,21.25,28.63,33.86,36.73,36.79,32.17,14},L, ERROR_EPSILON_LOWEST);
}
/**
@@ -4710,7 +4733,7 @@ public void TC22() throws IOException {
assertDoubleArrayEquals("LF - lateral left", expectedLF, actualLF, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{-2.96,3.56,6.73,11.17,13.85,13.86,9.48,-7.64},L, ERROR_EPSILON_VERY_LOW);
+ assertArrayEquals( new double[]{-2.96,3.56,6.73,11.17,13.85,13.86,9.48,-7.64},L, ERROR_EPSILON_LOWEST);
}
@@ -4781,8 +4804,6 @@ public void TC23() throws IOException {
double[] expectedL = new double[]{38.90, 37.17, 36.26, 34.68, 31.42, 27.54, 22.75, 15.02};
double[] expectedLA = new double[]{12.70, 21.07, 27.66, 31.48, 31.42, 28.74, 23.75, 13.92};
- CnossosPath proPath = cnossosPathDirectH;
-
double[] actualDeltaDiffSRH = cnossosPathDirectH.aBoundary.deltaDiffSR;
double[] actualAGroundSOH = cnossosPathDirectH.aBoundary.aGroundSO;
double[] actualAGroundORH = cnossosPathDirectH.aBoundary.aGroundOR;
@@ -4905,6 +4926,22 @@ public void TC24() throws IOException {
};
assertPlanes(segmentsMeanPlanes0,cnossosPathDirectH.getSegmentList());
+ /* Table 290 */
+ double[][] segmentsMeanPlanesRefH = new double[][]{
+ // a b zs zr dp Gp Gp'
+ {0.19, -1.17, 2.13, 1.94, 22.86, 0.37, 0.07},
+ {-0.05, 2.8, 3.38, 4.72, 46.90, 0.18, NaN}
+ };
+ assertPlanes(segmentsMeanPlanesRefH,cnossosPathReflectionH.getSegmentList());
+
+ /* Table 291 */
+ double[][] segmentsMeanPlanesRefF = new double[][]{
+ // a b zs zr dp Gp Gp'
+ {0.19, -1.17, 2.13, 1.94, 22.86, 0.37, 0.07},
+ {-0.06, 3.41, 2.96, 4.90, 48.20, 0.20, NaN}
+ };
+ assertPlanes(segmentsMeanPlanesRefF,cnossosPathReflectionF.getSegmentList());
+
//Expected values
//Path0 : vertical plane
double[] expectedDeltaDiffSRH = new double[]{10.18, 13.64, 16.95, 20.02, 23.02, 26.01, 29.00, 32.01};
@@ -5057,26 +5094,31 @@ public void TC24() throws IOException {
assertDoubleArrayEquals("DeltaGroundORH reflection plane", expectedDeltaGroundORH, actualDeltaGroundORH, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("actualADiffH reflection plane", expectedADiffH, actualADiffH, ERROR_EPSILON_VERY_LOW);
- assertDoubleArrayEquals("DeltaDiffSRF reflection plane", expectedDeltaDiffSRF, actualDeltaDiffSRF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("AGroundSOF reflection plane", expectedAGroundSOF, actualAGroundSOF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("AGroundORF reflection plane", expectedAGroundORF, actualAGroundORF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("DeltaDiffSPrimeRF reflection plane", expectedDeltaDiffSPrimeRF, actualDeltaDiffSPrimeRF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("DeltaDiffSRPrimeF reflection plane", expectedDeltaDiffSRPrimeF, actualDeltaDiffSRPrimeF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("DeltaGroundSOF reflection plane", expectedDeltaGroundSOF, actualDeltaGroundSOF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("DeltaGroundORF reflection plane", expectedDeltaGroundORF, actualDeltaGroundORF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("ADiffF reflection plane", expectedADiffF, actualADiffF, ERROR_EPSILON_VERY_HIGH);
+ assertEquals(0.29, cnossosPathReflectionF.delta, ERROR_EPSILON_LOWEST);
+ // curved ray goes above beam in favorable
+ assertEquals(0, cnossosPathReflectionF.e, ERROR_EPSILON_LOWEST);
+ assertDoubleArrayEquals("DeltaDiffSRF reflection plane", expectedDeltaDiffSRF, actualDeltaDiffSRF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("AGroundSOF reflection plane", expectedAGroundSOF, actualAGroundSOF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("AGroundORF reflection plane", expectedAGroundORF, actualAGroundORF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("DeltaDiffSPrimeRF reflection plane", expectedDeltaDiffSPrimeRF, actualDeltaDiffSPrimeRF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("DeltaDiffSRPrimeF reflection plane", expectedDeltaDiffSRPrimeF, actualDeltaDiffSRPrimeF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("DeltaGroundSOF reflection plane", expectedDeltaGroundSOF, actualDeltaGroundSOF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("DeltaGroundORF reflection plane", expectedDeltaGroundORF, actualDeltaGroundORF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("ADiffF reflection plane", expectedADiffF, actualADiffF, ERROR_EPSILON_LOW);
assertDoubleArrayEquals("AlphaAtm reflection plane", expectedAlphaAtm, actualAlphaAtm, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("AAtm reflection plane", expectedAAtm, actualAAtm, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("ADiv reflection plane", expectedADiv, actualADiv, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("ABoundaryH reflection plane", expectedABoundaryH, actualABoundaryH, ERROR_EPSILON_VERY_LOW);
- assertDoubleArrayEquals("ABoundaryF reflection plane", expectedABoundaryF, actualABoundaryF, ERROR_EPSILON_VERY_HIGH);
+ assertDoubleArrayEquals("ABoundaryF reflection plane", expectedABoundaryF, actualABoundaryF, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("LH - Reflection plane", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
- assertDoubleArrayEquals("LF reflection plane", expectedLF, actualLF, ERROR_EPSILON_VERY_HIGH);
- assertDoubleArrayEquals("L reflection plane", expectedL, actualL, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("LA reflection plane", expectedLA, actualLA, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("LF reflection plane", expectedLF, actualLF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("L reflection plane", expectedL, actualL, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LA reflection plane", expectedLA, actualLA, ERROR_EPSILON_VERY_LOW);
+ double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93 - 26.2, 93 - 16.1, 93 - 8.6, 93 - 3.2, 93, 93 + 1.2, 93 + 1.0, 93 - 1.1});
+ assertArrayEquals(new double[]{14.31, 21.69, 27.76, 31.52, 31.49, 29.18, 25.39, 16.58}, L, ERROR_EPSILON_LOWEST);
}
/**
@@ -5420,14 +5462,17 @@ public void TC25() throws IOException {
/**
* TC26 – Road source with influence of retrodiffraction
- * Issue we compute and add favourable contribution, on reflexion path but not in test case reference
- * */
+ * No favourable reflection path: in favorable conditions the curved ray passes over the wall.
+ * EU Directive 2002/49 (consolidated 29.07.2021), Annex II, §2.5.20:
+ * "le point de réflexion est construit à l'aide de lignes droites dans des conditions de propagation
+ * homogènes et de lignes courbes dans des conditions de propagation favorables"
+ */
@Test
public void TC26() throws IOException {
AttenuationComputeOutput propDataOut = computeCnossosPath("TC26_Direct", "TC26_Reflection");
- assertEquals(4, propDataOut.getPropagationPaths().size());
+ assertEquals(3, propDataOut.getPropagationPaths().size());
final CnossosPath cnossosPathDirectH = propDataOut.getPropagationPaths().get(0);
assertFalse(cnossosPathDirectH.isFavourable());
@@ -5440,10 +5485,6 @@ public void TC26() throws IOException {
assertFalse(cnossosPathReflectionH.isFavourable());
assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPathReflectionH.getCutProfile().getProfileType());
- CnossosPath cnossosPathReflectionF = propDataOut.getPropagationPaths().get(3);
- assertTrue(cnossosPathReflectionF.isFavourable());
- assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPathReflectionF.getCutProfile().getProfileType());
-
//Expected values
//Path0 : vertical plane
double[] expectedWH = new double[]{0.00, 0.00, 0.00, 0.00, 0.00, 0.02, 0.12, 0.65};
@@ -5516,7 +5557,8 @@ public void TC26() throws IOException {
actualADiv = cnossosPathReflectionH.aDiv;
actualABoundaryH = cnossosPathReflectionH.double_aBoundary;
actualLH = addArray(cnossosPathReflectionH.aGlobalRaw, SOUND_POWER_LEVELS);
- actualL = addArray(sumDbArray(cnossosPathReflectionH.aGlobal, cnossosPathReflectionF.aGlobal), SOUND_POWER_LEVELS);
+ // No favorable reflection path, use aGlobal which accounts for p=0.5 weighting
+ actualL = addArray(cnossosPathReflectionH.aGlobal, SOUND_POWER_LEVELS);
actualLA = addArray(actualL, A_WEIGHTING);
assertDoubleArrayEquals("WH - reflexion", expectedWH, actualWH, ERROR_EPSILON_LOWEST);
@@ -5527,19 +5569,18 @@ public void TC26() throws IOException {
assertDoubleArrayEquals("ADiv - reflexion", expectedADiv, actualADiv, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("ABoundaryH - reflexion", expectedABoundaryH, actualABoundaryH, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("LH - reflexion", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
- assertDoubleArrayEquals("L - reflexion", expectedL, actualL, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("LA - reflexion", expectedLA, actualLA, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("L - reflexion", expectedL, actualL, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LA - reflexion", expectedLA, actualLA, ERROR_EPSILON_VERY_LOW);
double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93-26.2,93-16.1,93-8.6,93-3.2,93,93+1.2,93+1.0,93-1.1});
- assertArrayEquals( new double[]{17.50,27.52,34.89,40.14,43.10,43.59,40.55,29.15},L, ERROR_EPSILON_LOW);
+ assertArrayEquals( new double[]{17.50,27.52,34.89,40.14,43.10,43.59,40.55,29.15},L, ERROR_EPSILON_VERY_LOW);
}
/**
* TC27 – Road source with influence of retrodiffraction
- * Issue with wrong agroundF for reflection path
* */
@Test
public void TC27() throws IOException {
@@ -5595,7 +5636,6 @@ public void TC27() throws IOException {
SegmentPath sr = cnossosPathReflectionF.getSRSegment();
assertMirrorPoint(expectedSPrimeSR,expectedRPrimeSR,sr.sPrime,
sr.rPrime);
- assertEquals(2, cnossosPathReflectionF.getSegmentList().size());
SegmentPath so = cnossosPathReflectionF.getSegmentList().get(0);
SegmentPath or = cnossosPathReflectionF.getSegmentList().get(cnossosPathReflectionH.getSegmentList().size() - 1);
@@ -5647,8 +5687,8 @@ public void TC27() throws IOException {
expectedABoundaryF = new double[]{-0.59, -0.59, -0.59, -0.59, 4.43, 2.99, 0.42, -0.59};
double[] expectedDLabs = new double[] {-0.46, -0.97, -1.55, -2.22, -3.01, -3.98, -5.23, -3.01};
expectedLH = new double[]{35.56, 36.12, 38.09, 37.16, 32.44, 29.29, 25.96, 19.00};
- expectedLF = new double[]{37.83, 37.89, 38.82, 40.11, 34.12, 34.00, 32.98, 27.54};
- expectedLA = new double[]{10.64, 21.00, 29.97, 35.68, 33.36, 33.45, 31.76, 24.17};
+ expectedLF = new double[]{37.83, 37.89, 38.82, 40.11, 34.12, 34.00, 32.98, 27.74};
+ expectedLA = new double[]{10.64, 21.00, 29.87, 35.68, 33.36, 33.45, 31.76, 24.17};
actualAAtm = cnossosPathReflectionH.aAtm;
actualADiv = cnossosPathReflectionH.aDiv;
@@ -5660,21 +5700,20 @@ public void TC27() throws IOException {
actualLA = addArray(actualL, A_WEIGHTING);
double[] LA = sumDbArray(directLA, actualLA);
double[] expectedFullLA = new double[]{16.84, 26.97, 34.79, 40.23, 38.57, 38.58, 39.36, 29.60};
- double[] diffLa = diffArray(expectedFullLA, LA);
assertDoubleArrayEquals("AAtm - reflection", expectedAAtm, actualAAtm, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("ADiv - reflection", expectedADiv, actualADiv, ERROR_EPSILON_LOWEST);
assertDoubleArrayEquals("ABoundaryH - reflection", expectedABoundaryH, actualABoundaryH, ERROR_EPSILON_LOWEST);
- assertDoubleArrayEquals("ABoundaryF - reflection", expectedABoundaryF, actualABoundaryF, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("ABoundaryF - reflection", expectedABoundaryF, actualABoundaryF, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("dLabs - reflection", expectedDLabs, cnossosPathReflectionH.aRef, ERROR_EPSILON_LOWEST);
- assertDoubleArrayEquals("LH - reflection", expectedLH, actualLH, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("LF - reflection", expectedLF, actualLF, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("LA - reflection", expectedLA, actualLA, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("LH - reflection", expectedLH, actualLH, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LF - reflection", expectedLF, actualLF, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("LA - reflection", expectedLA, actualLA, ERROR_EPSILON_VERY_LOW);
- double[] L = addArray(propDataOut.getVerticesSoundLevel().get(0).levels, new double[]{93 - 26.2, 93 - 16.1, 93 - 8.6, 93 - 3.2, 93, 93 + 1.2, 93 + 1.0, 93 - 1.1});
+ double[] L = addArray(addArray(propDataOut.getVerticesSoundLevel().get(0).levels, SOUND_POWER_LEVELS), A_WEIGHTING);
- assertArrayEquals(expectedFullLA, L, ERROR_EPSILON_LOW);
+ assertArrayEquals(expectedFullLA, L, ERROR_EPSILON_VERY_LOW);
}
/**
@@ -5746,15 +5785,17 @@ public void TC28() throws IOException {
List expectedZProfileLeft = Arrays.asList(
new Coordinate(0.0, 0.0),
- new Coordinate(168.36, 0.0),
- //new Coordinate(256.17, 0.0), // building ignored in CNOSSOS
- //new Coordinate(276.59, 0.0), // building ignored in CNOSSOS
- new Coordinate(356.24, 0.0),
- new Coordinate(444.81, 0.0),
+ new Coordinate(168.34, 0.0),
+ new Coordinate(256.16, 0.0),
+ new Coordinate(256.16, 14.0),
+ new Coordinate(276.58, 14.0),
+ new Coordinate(276.58, 0.0),
+ new Coordinate(356.23, 0.0),
+ new Coordinate(444.79, 0.0),
new Coordinate(525.11, 0.0),
- new Coordinate(988.63, 0.0),
- new Coordinate(1002.95, 0.0),
- new Coordinate(1022.31, 0.0));
+ new Coordinate(988.62, 0.0),
+ new Coordinate(1002.96, 0.0),
+ new Coordinate(1022.27, 0.0));
/* Table 348 */
double [][] segmentsMeanPlanesDirect = new double[][]{
@@ -5893,12 +5934,12 @@ public void TC28() throws IOException {
assertEquals(CutProfile.PROFILE_TYPE.LEFT, cnossosPath.getCutProfile().getProfileType());
assertZProfil(expectedZProfileLeft, Arrays.asList(cnossosPath.getSRSegment().getPoints2DGround()));
- // TODO Weird plane, A B not at 0 (there is no DEM)
- //assertPlanes(segmentsMeanPlanesLeft, cnossosPath.getSRSegment());
+
+ assertPlanes(segmentsMeanPlanesLeft, cnossosPath.getSRSegment());
assertDoubleArrayEquals("w (H) - lateral left", expectedLeftH_W, cnossosPath.groundAttenuation.w, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("Cf (H) - lateral left", expectedLeftH_CF, cnossosPath.groundAttenuation.cf, 50);
- assertDoubleArrayEquals("AGround (H) - lateral left", expectedLeftH_Aground, cnossosPath.groundAttenuation.aGround, ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("Cf (H) - lateral left", expectedLeftH_CF, cnossosPath.groundAttenuation.cf, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("AGround (H) - lateral left", expectedLeftH_Aground, cnossosPath.groundAttenuation.aGround, ERROR_EPSILON_VERY_LOW);
double[] expectedLeftADiv = {71.01, 71.01, 71.01, 71.01, 71.01, 71.01, 71.01, 71.01};
double[] expectedLeftAGroundH = {-1.53, -1.53, -1.25, 15.70, 21.95, 13.29, 5.98, -0.45};
@@ -5907,8 +5948,8 @@ public void TC28() throws IOException {
assertDoubleArrayEquals("ADiv (H) - lateral left", expectedLeftADiv, cnossosPath.aDiv, ERROR_EPSILON_VERY_LOW);
assertDoubleArrayEquals("ADif (H) - lateral left", expectedLeftADifH, cnossosPath.aDif, ERROR_EPSILON_LOW);
- assertDoubleArrayEquals("AGround (H) - lateral left", expectedLeftAGroundH, cnossosPath.groundAttenuation.aGround, ERROR_EPSILON_HIGH);
- assertDoubleArrayEquals("L (H) - lateral left", expectedLeftLH, sumArray(cnossosPath.aGlobalRaw, LW_SOURCE), ERROR_EPSILON_HIGH);
+ assertDoubleArrayEquals("AGround (H) - lateral left", expectedLeftAGroundH, cnossosPath.groundAttenuation.aGround, ERROR_EPSILON_VERY_LOW);
+ assertDoubleArrayEquals("L (H) - lateral left", expectedLeftLH, sumArray(cnossosPath.aGlobalRaw, LW_SOURCE), ERROR_EPSILON_VERY_LOW);
// Left favourable path
cnossosPath = propDataOut.getPropagationPaths().get(5);
@@ -6354,6 +6395,428 @@ public void TestFavourableConditionAttenuationRose() {
}
}
+
+ /**
+ * Regression test for multi-diffraction favorable path: missing e term in deltaPrime calculation
+ * with positive orientation.
+ * Two buildings create multi-diffraction. In favorable conditions, the deltaPrime
+ * formula (Eq. 2.5.21) must include the e term (distance between diffraction edges).
+ * Without the fix, deltaPrime would be significantly lower (missing toCurve(e, dPrime) contribution).
+ */
+ @Test
+ public void regressionMultiDifFavourable_MissingETerm() throws IOException {
+ GeometryFactory f = new GeometryFactory();
+ ProfileBuilder profileBuilder = new ProfileBuilder();
+
+ // Two buildings creating multi-diffraction at 200m distance
+ // Building 1: x=[75,80], height 10m — Building 2: x=[120,125], height 10m
+ // e ≈ distance between building tops ≈ 40m (significant contribution to deltaPrime)
+ profileBuilder
+ .addBuilding(new Coordinate[]{
+ new Coordinate(75, -50, 0), new Coordinate(80, -50, 0),
+ new Coordinate(80, 50, 0), new Coordinate(75, 50, 0)
+ }, 10.0)
+ .addBuilding(new Coordinate[]{
+ new Coordinate(120, -50, 0), new Coordinate(125, -50, 0),
+ new Coordinate(125, 50, 0), new Coordinate(120, 50, 0)
+ }, 10.0)
+ .finishFeeding();
+
+ SceneWithAttenuation scene = new SceneWithAttenuation(profileBuilder);
+ scene.addSource(f.createPoint(new Coordinate(0, 0, 1)));
+ scene.addReceiver(new Coordinate(200, 0, 4));
+ scene.defaultGroundAttenuation = 0.5;
+ scene.reflexionOrder = 0;
+ scene.maxSrcDist = 300;
+ scene.setComputeHorizontalDiffraction(true);
+ scene.setComputeVerticalDiffraction(true);
+
+ AttenuationParameters attData = new AttenuationParameters();
+ attData.setHumidity(HUMIDITY);
+ attData.setTemperature(TEMPERATURE);
+ scene.defaultCnossosParameters = attData;
+
+ AttenuationComputeOutput propDataOut = new AttenuationComputeOutput(true, true, scene);
+ PathFinder pathFinder = new PathFinder(scene);
+ pathFinder.setThreadCount(1);
+ pathFinder.run(propDataOut);
+
+ // Find the favorable direct path with multi-diffraction (3+ segments = 2+ diffraction edges)
+ CnossosPath favDirectPath = null;
+ CnossosPath homDirectPath = null;
+ for (CnossosPath path : propDataOut.getPropagationPaths()) {
+ if (path.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.DIRECT
+ && path.getSegmentList().size() >= 3) {
+ if (path.isFavourable()) {
+ favDirectPath = path;
+ } else {
+ homDirectPath = path;
+ }
+ }
+ }
+ assertNotNull(favDirectPath, "Should find a favorable direct path with multi-diffraction");
+ assertNotNull(homDirectPath, "Should find a homogeneous direct path with multi-diffraction");
+
+ // === ASSERTIONS ===
+
+ // e > 0 confirms multi-diffraction (distance between diffraction edges)
+ assertTrue(favDirectPath.e > 0, "Multi-diffraction path must have e > 0");
+
+ // delta and deltaPrime should be positive for standard geometry (barriers above S-R line)
+ assertTrue(favDirectPath.delta > 0, "Favorable delta should be positive for barriers above S-R line");
+ assertTrue(favDirectPath.deltaPrime > 0, "Favorable deltaPrime should be positive");
+
+ // KEY REGRESSION CHECK:
+ // With correct fix: deltaPrime_F includes the e term via toCurve(e, dPrime)
+ // Without fix: deltaPrime_F is missing ~toCurve(e, dPrime) contribution
+ // For e ≈ 40m and dPrime ≈ 200m, the missing term is approximately 40m
+ // The homogeneous deltaPrime_H = seg1.dPrime + e + seg2.dPrime - srPath.dPrime
+ // The favorable deltaPrime_F should be close to deltaPrime_H (both include e)
+ // Without fix: deltaPrime_F ≈ deltaPrime_H - e (dramatically wrong)
+ double deltaPrimeDiff = Math.abs(favDirectPath.deltaPrime - homDirectPath.deltaPrime);
+ assertTrue(deltaPrimeDiff < homDirectPath.e,
+ String.format("Favorable deltaPrime (%.2f) should be close to homogeneous deltaPrime (%.2f), " +
+ "difference (%.2f) should be less than e (%.2f). " +
+ "A large difference indicates the e term is missing.",
+ favDirectPath.deltaPrime, homDirectPath.deltaPrime, deltaPrimeDiff, homDirectPath.e));
+
+ // aDif values should be physically bounded (diffraction cannot add more than 25dB)
+ assertTrue(Arrays.stream(favDirectPath.aDif).allMatch(d -> d >= -25),
+ "Diffraction attenuation should be >= -25 dB");
+
+ // Global attenuation must be negative (sound always loses energy)
+ assertTrue(Arrays.stream(favDirectPath.aGlobal).allMatch(d -> d < 0),
+ "Global attenuation should be negative");
+ assertTrue(Arrays.stream(favDirectPath.aGlobalRaw).allMatch(d -> d < 0),
+ "Raw global attenuation should be negative");
+
+ // Precise numeric assertions
+ assertEquals(50.0, favDirectPath.e, ERROR_EPSILON_LOWEST);
+ assertEquals(0.6407, favDirectPath.delta, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.9613, favDirectPath.deltaPrime, ERROR_EPSILON_VERY_LOW);
+ assertEquals(0.8651, favDirectPath.deltaSPrimeR, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.6569, favDirectPath.deltaSRPrime, ERROR_EPSILON_VERY_LOW);
+ }
+
+
+ /**
+ * JSON-based standalone regression test for multi-diffraction favorable (missing e term).
+ * Loads the CutProfile previously generated by regressionMultiDifFavourable_MissingETerm.
+ */
+ @Test
+ public void regressionMultiDifFavourable_Standalone() throws IOException {
+ AttenuationComputeOutput propDataOut = computeCnossosPath(
+ AttenuationComputeOutputCnossosTest.class.getResource("RegressionTestMultiDifFav.json"));
+ assertNotNull(propDataOut);
+ assertEquals(2, propDataOut.getPropagationPaths().size());
+
+ CnossosPath homPath = propDataOut.getPropagationPaths().get(0);
+ assertFalse(homPath.isFavourable());
+ CnossosPath favPath = propDataOut.getPropagationPaths().get(1);
+ assertTrue(favPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.DIRECT, favPath.getCutProfile().getProfileType());
+
+ // Multi-diffraction: e > 0
+ assertTrue(favPath.e > 0, "Multi-diffraction path must have e > 0");
+ // deltaPrime should be positive (barriers above S-R line)
+ assertTrue(favPath.deltaPrime > 0, "Favorable deltaPrime should be positive");
+ // Favorable deltaPrime should be close to homogeneous (both include e contribution)
+ double deltaPrimeDiff = Math.abs(favPath.deltaPrime - homPath.deltaPrime);
+ assertTrue(deltaPrimeDiff < homPath.e,
+ "deltaPrime difference between F and H should be less than e");
+ // aDif physically bounded
+ assertTrue(Arrays.stream(favPath.aDif).allMatch(d -> d >= -25));
+ // Global attenuation negative
+ assertTrue(Arrays.stream(favPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(favPath.aGlobalRaw).allMatch(d -> d < 0));
+
+ // Precise numeric assertions (same geometry as scene-based test)
+ assertEquals(50.0, favPath.e, ERROR_EPSILON_LOWEST);
+ assertEquals(0.6407, favPath.delta, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.9613, favPath.deltaPrime, ERROR_EPSILON_VERY_LOW);
+ assertEquals(0.8651, favPath.deltaSPrimeR, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.6569, favPath.deltaSRPrime, ERROR_EPSILON_VERY_LOW);
+ }
+
+
+ /**
+ * Regression test for single-diffraction favorable path deltaPrime calculation.
+ * A thin wall creates a single diffraction point. The favorable path delta and deltaPrime
+ * are computed using toCurve formulas. This test verifies that:
+ * - delta and deltaPrime are physically consistent between H and F paths
+ * - the favorable computations produce bounded, reasonable values
+ * - aDif and aGlobal values are physically valid
+ * The fix corrected the negative deltaPrime orientation branch (using rcvPrime
+ * instead of srcPrime and dPrime instead of d), which is only triggered in rare terrain
+ * configurations. This test validates the overall single-diffraction favorable code path.
+ */
+ @Test
+ public void regressionSingleDifFavourable_DeltaPrime() throws IOException {
+ GeometryFactory f = new GeometryFactory();
+ ProfileBuilder profileBuilder = new ProfileBuilder();
+
+ // Thin wall at x=20, height=6m creates single diffraction.
+ // Source at (0,0,1), Receiver at (50,0,4) → wall above S-R line.
+ List alphas = Arrays.asList(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+ profileBuilder
+ .addWall(new Coordinate[]{
+ new Coordinate(20, -100, 0),
+ new Coordinate(20, 100, 0)
+ }, 6.0, alphas, 1)
+ .finishFeeding();
+
+ SceneWithAttenuation scene = new SceneWithAttenuation(profileBuilder);
+ scene.addSource(f.createPoint(new Coordinate(0, 0, 1)));
+ scene.addReceiver(new Coordinate(50, 0, 4));
+ scene.defaultGroundAttenuation = 0.5;
+ scene.reflexionOrder = 0;
+ scene.maxSrcDist = 100;
+ scene.setComputeHorizontalDiffraction(true);
+ scene.setComputeVerticalDiffraction(true);
+
+ AttenuationParameters attData = new AttenuationParameters();
+ attData.setHumidity(HUMIDITY);
+ attData.setTemperature(TEMPERATURE);
+ scene.defaultCnossosParameters = attData;
+
+ AttenuationComputeOutput propDataOut = new AttenuationComputeOutput(true, true, scene);
+ PathFinder pathFinder = new PathFinder(scene);
+ pathFinder.setThreadCount(1);
+ pathFinder.run(propDataOut);
+
+ // Find favorable and homogeneous direct paths with diffraction
+ CnossosPath favDirectPath = null;
+ CnossosPath homDirectPath = null;
+ for (CnossosPath path : propDataOut.getPropagationPaths()) {
+ if (path.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.DIRECT
+ && path.getSegmentList().size() >= 2) {
+ if (path.isFavourable()) {
+ favDirectPath = path;
+ } else {
+ homDirectPath = path;
+ }
+ }
+ }
+ assertNotNull(favDirectPath, "Should find a favorable direct path with diffraction");
+ assertNotNull(homDirectPath, "Should find a homogeneous direct path with diffraction");
+
+ // Should have diffraction
+ boolean hasDiffraction = favDirectPath.getPointList().stream()
+ .anyMatch(p -> p.type.name().startsWith("DIF"));
+ assertTrue(hasDiffraction, "Diffraction should be triggered by wall above S-R line");
+
+ // delta should be positive (wall above S-R line)
+ assertTrue(favDirectPath.delta > 0,
+ "Favorable delta should be positive for wall above S-R line");
+
+ // deltaPrime should be positive and bounded
+ assertTrue(favDirectPath.deltaPrime > 0,
+ "Favorable deltaPrime should be positive");
+ assertTrue(favDirectPath.deltaPrime < 50,
+ "DeltaPrime should be bounded for a single wall");
+
+ // Favorable delta/deltaPrime should be close to homogeneous (curvature effect small at 50m)
+ double deltaDiff = Math.abs(favDirectPath.delta - homDirectPath.delta);
+ assertTrue(deltaDiff < Math.max(1.0, homDirectPath.delta * 0.5),
+ String.format("Favorable delta (%.4f) should be close to homogeneous (%.4f)",
+ favDirectPath.delta, homDirectPath.delta));
+
+ double deltaPrimeDiff = Math.abs(favDirectPath.deltaPrime - homDirectPath.deltaPrime);
+ assertTrue(deltaPrimeDiff < Math.max(1.0, homDirectPath.deltaPrime * 0.5),
+ String.format("Favorable deltaPrime (%.4f) should be close to homogeneous (%.4f)",
+ favDirectPath.deltaPrime, homDirectPath.deltaPrime));
+
+ // aDif should be physically bounded
+ assertTrue(Arrays.stream(favDirectPath.aDif).allMatch(d -> d >= -25),
+ "Diffraction attenuation should be >= -25 dB");
+
+ // Global attenuation must be negative
+ assertTrue(Arrays.stream(favDirectPath.aGlobal).allMatch(d -> d < 0),
+ "Global attenuation should be negative");
+ assertTrue(Arrays.stream(favDirectPath.aGlobalRaw).allMatch(d -> d < 0),
+ "Raw global attenuation should be negative");
+
+ // Precise numeric assertions
+ assertEquals(0.0, favDirectPath.e, ERROR_EPSILON_LOWEST);
+ assertEquals(0.5885, favDirectPath.delta, ERROR_EPSILON_VERY_LOW);
+ assertEquals(2.7190, favDirectPath.deltaPrime, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.0031, favDirectPath.deltaSPrimeR, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.9853, favDirectPath.deltaSRPrime, ERROR_EPSILON_VERY_LOW);
+ }
+
+
+ /**
+ * JSON-based standalone regression test for single-diffraction favorable path.
+ * Loads the CutProfile previously generated by regressionSingleDifFavourable_DeltaPrime.
+ */
+ @Test
+ public void regressionSingleDifFavourable_Standalone() throws IOException {
+ AttenuationComputeOutput propDataOut = computeCnossosPath(
+ AttenuationComputeOutputCnossosTest.class.getResource("RegressionTestSingleDif.json"));
+ assertNotNull(propDataOut);
+ assertEquals(2, propDataOut.getPropagationPaths().size());
+
+ CnossosPath homPath = propDataOut.getPropagationPaths().get(0);
+ assertFalse(homPath.isFavourable());
+ CnossosPath favPath = propDataOut.getPropagationPaths().get(1);
+ assertTrue(favPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.DIRECT, favPath.getCutProfile().getProfileType());
+
+ // Single diffraction: 2 segments
+ assertEquals(2, favPath.getSegmentList().size());
+ // delta positive (wall above SR line)
+ assertTrue(favPath.delta > 0);
+ // deltaPrime positive and bounded
+ assertTrue(favPath.deltaPrime > 0);
+ assertTrue(favPath.deltaPrime < 50);
+ // Favorable close to homogeneous
+ assertTrue(Math.abs(favPath.deltaPrime - homPath.deltaPrime) < 1.0);
+ // aDif physically bounded
+ assertTrue(Arrays.stream(favPath.aDif).allMatch(d -> d >= -25));
+ // Global attenuation negative
+ assertTrue(Arrays.stream(favPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(favPath.aGlobalRaw).allMatch(d -> d < 0));
+
+ // Precise numeric assertions (same geometry as scene-based test)
+ assertEquals(0.0, favPath.e, ERROR_EPSILON_LOWEST);
+ assertEquals(0.5885, favPath.delta, ERROR_EPSILON_VERY_LOW);
+ assertEquals(2.7190, favPath.deltaPrime, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.0031, favPath.deltaSPrimeR, ERROR_EPSILON_VERY_LOW);
+ assertEquals(1.9853, favPath.deltaSRPrime, ERROR_EPSILON_VERY_LOW);
+ }
+
+
+ /**
+ * Test regression issue, reflection in favourable over DEM
+ */
+ @Test
+ public void TCFavourableReflection() throws IOException {
+ AttenuationComputeOutput propDataOut = computeCnossosPath(AttenuationComputeOutputCnossosTest.class.getResource("RegressionTestReflection1.json"));
+ assertNotNull(propDataOut);
+ assertEquals(2, propDataOut.getPropagationPaths().size());
+ CnossosPath cnossosPath = propDataOut.getPropagationPaths().get(0);
+ assertFalse(cnossosPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPath.getCutProfile().getProfileType());
+ // check if cnossosPath.aDif array is positive
+ // Why 18dB gain, because up to 9dB gain for the reflection in favourable condition (eq. 2.5.20) on the ground for SO and OR
+ assertTrue(Arrays.stream(cnossosPath.aDif).allMatch(d -> d >= -18));
+ // Check attenuation is cnossosPath.aGlobal < 0 dB
+ assertTrue(Arrays.stream(cnossosPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(cnossosPath.aGlobalRaw).allMatch(d -> d < 0));
+ cnossosPath = propDataOut.getPropagationPaths().get(1);
+ assertTrue(cnossosPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPath.getCutProfile().getProfileType());
+ // check if cnossosPath.aDif array is positive
+ // Why 18dB gain, because up to 9dB gain for the reflection in favourable condition (eq. 2.5.20) on the ground for SO and OR
+ assertTrue(Arrays.stream(cnossosPath.aDif).allMatch(d -> d >= -18));
+ // Check attenuation is cnossosPath.aGlobal < 0 dB
+ assertTrue(Arrays.stream(cnossosPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(cnossosPath.aGlobalRaw).allMatch(d -> d < 0));
+ }
+
+
+ /**
+ * Test regression issue, reflection in favourable over DEM
+ */
+ @Test
+ public void TCFavourableReflection2() throws IOException {
+ AttenuationComputeOutput propDataOut = computeCnossosPath(AttenuationComputeOutputCnossosTest.class.getResource("RegressionTestReflection2.json"));
+ assertNotNull(propDataOut);
+ assertEquals(2, propDataOut.getPropagationPaths().size());
+ CnossosPath cnossosPath = propDataOut.getPropagationPaths().get(0);
+ assertFalse(cnossosPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPath.getCutProfile().getProfileType());
+ // check if cnossosPath.aDif array is positive
+ // Why 18dB gain, because up to 9dB gain for the reflection in favourable condition (eq. 2.5.20) on the ground for SO and OR
+ assertTrue(Arrays.stream(cnossosPath.aDif).allMatch(d -> d >= -18));
+ // Check attenuation is cnossosPath.aGlobal < 0 dB
+ assertTrue(Arrays.stream(cnossosPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(cnossosPath.aGlobalRaw).allMatch(d -> d < 0));
+ cnossosPath = propDataOut.getPropagationPaths().get(1);
+ assertTrue(cnossosPath.isFavourable());
+ assertEquals(CutProfile.PROFILE_TYPE.REFLECTION, cnossosPath.getCutProfile().getProfileType());
+ // check if cnossosPath.aDif array is positive
+ // Why 18dB gain, because up to 9dB gain for the reflection in favourable condition (eq. 2.5.20) on the ground for SO and OR
+ assertTrue(Arrays.stream(cnossosPath.aDif).allMatch(d -> d >= -18));
+ // Check attenuation is cnossosPath.aGlobal < 0 dB
+ assertTrue(Arrays.stream(cnossosPath.aGlobal).allMatch(d -> d < 0));
+ assertTrue(Arrays.stream(cnossosPath.aGlobalRaw).allMatch(d -> d < 0));
+ }
+
+ @Test
+ public void reflectionOnBuildingWallNearReceiver() throws IOException {
+ AttenuationComputeOutput propDataOut = computeReflectionOnBuildingWallNearReceiver(false);
+
+ List reflectionPaths = propDataOut.getPropagationPaths().stream()
+ .filter(path -> path.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.REFLECTION)
+ .collect(Collectors.toList());
+ assertFalse(reflectionPaths.isEmpty(),
+ "A reflection path should exist on the building wall when source and receiver are on the same side.");
+ assertTrue(reflectionPaths.stream().anyMatch(path ->
+ path.getPointList().stream().anyMatch(point -> point.type == PointPath.POINT_TYPE.REFL)),
+ "At least one computed reflection path should contain a reflection point on the wall.");
+ }
+
+ @Test
+ public void reflectionOnBuildingWallNearReceiverFilteredWhenOptionEnabled() throws IOException {
+ AttenuationComputeOutput propDataOut = computeReflectionOnBuildingWallNearReceiver(true);
+
+ List reflectionPaths = propDataOut.getPropagationPaths().stream()
+ .filter(path -> path.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.REFLECTION)
+ .collect(Collectors.toList());
+ assertTrue(reflectionPaths.isEmpty(),
+ "Reflection paths ending with a wall reflection within 0.5 m of the receiver should be filtered when the option is enabled.");
+ assertFalse(propDataOut.getPropagationPaths().isEmpty(),
+ "Enabling the reflection-profile filter should not remove the valid direct paths.");
+ assertTrue(propDataOut.getPropagationPaths().stream().allMatch(path ->
+ path.getCutProfile().getProfileType() == CutProfile.PROFILE_TYPE.DIRECT),
+ "Only direct paths should remain after filtering the near-receiver reflection profile.");
+ }
+
+ private AttenuationComputeOutput computeReflectionOnBuildingWallNearReceiver(boolean enableReflectionProfileFilter)
+ throws IOException {
+ GeometryFactory geometryFactory = new GeometryFactory();
+ ProfileBuilder profileBuilder = new ProfileBuilder();
+ ObjectMapper objectMapper = new ObjectMapper();
+
+ profileBuilder
+ .addBuilding(new Coordinate[]{
+ new Coordinate(0.0, -2.5, 0.0),
+ new Coordinate(1.0, -2.5, 0.0),
+ new Coordinate(1.0, 2.5, 0.0),
+ new Coordinate(0.0, 2.5, 0.0)
+ }, 5.0)
+ .finishFeeding();
+
+ SceneWithAttenuation scene = new SceneWithAttenuation(profileBuilder);
+ scene.addSource(geometryFactory.createPoint(new Coordinate(11.49, 0.0, 2.0)));
+ scene.addReceiver(new Coordinate(1.49, 0.0, 2.0));
+ scene.defaultGroundAttenuation = 0.0;
+ scene.reflexionOrder = 1;
+ scene.maxSrcDist = 50.0;
+ scene.setCloseReceiverReflectionWallDistance(enableReflectionProfileFilter ? 0.5 : 0.0);
+ scene.setComputeHorizontalDiffraction(false);
+ scene.setComputeVerticalDiffraction(false);
+
+ AttenuationParameters attData = new AttenuationParameters();
+ attData.setHumidity(HUMIDITY);
+ attData.setTemperature(TEMPERATURE);
+ scene.defaultCnossosParameters = attData;
+
+ AttenuationComputeOutput propDataOut = new AttenuationComputeOutput(true, true, scene);
+ PathFinder pathFinder = new PathFinder(scene);
+ pathFinder.setThreadCount(1);
+ pathFinder.run(propDataOut);
+
+ for (int i = 0; i < propDataOut.getPropagationPaths().size(); i++) {
+ CnossosPath path = propDataOut.getPropagationPaths().get(i);
+ LOGGER.info("Path {} profileType={}", i, path.getCutProfile().getProfileType());
+ LOGGER.info("Path {} segmentList={}", i, objectMapper.writeValueAsString(path.getSegmentList()));
+ LOGGER.info("Path {} pointList={}", i, objectMapper.writeValueAsString(path.getPointList()));
+ }
+ return propDataOut;
+ }
+
/**
* Assertions for a list of {@link CnossosPath}.
* @param expectedPts Array of arrays of array of expected coordinates (xyz) of points of paths. To each path
diff --git a/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestMultiDifFav.json b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestMultiDifFav.json
new file mode 100644
index 000000000..f01a39bd7
--- /dev/null
+++ b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestMultiDifFav.json
@@ -0,0 +1,166 @@
+{
+ "cutPoints": [
+ {
+ "type": "Source",
+ "coordinate": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "sourcePk": -1,
+ "li": 1.0,
+ "orientation": {
+ "yaw": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 75.0,
+ "y": 0.0,
+ "z": 10.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "wall": {
+ "p0": {
+ "x": 75.0,
+ "y": -50.0,
+ "z": 10.0
+ },
+ "p1": {
+ "x": 75.0,
+ "y": 50.0,
+ "z": 10.0
+ }
+ },
+ "wallAlpha": [
+ 0.004918209037966988,
+ 0.008088162780793065,
+ 0.013267721240171722,
+ 0.02168493917994337,
+ 0.03525564533465828,
+ 0.056883929412881944,
+ 0.09077715686040604,
+ 0.14258583864393162
+ ],
+ "intersectionType": "BUILDING_ENTER"
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 80.0,
+ "y": 0.0,
+ "z": 10.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "wall": {
+ "p0": {
+ "x": 80.0,
+ "y": 50.0,
+ "z": 10.0
+ },
+ "p1": {
+ "x": 80.0,
+ "y": -50.0,
+ "z": 10.0
+ }
+ },
+ "wallAlpha": [
+ 0.004918209037966988,
+ 0.008088162780793065,
+ 0.013267721240171722,
+ 0.02168493917994337,
+ 0.03525564533465828,
+ 0.056883929412881944,
+ 0.09077715686040604,
+ 0.14258583864393162
+ ],
+ "intersectionType": "BUILDING_EXIT"
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 120.0,
+ "y": 0.0,
+ "z": 10.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "wall": {
+ "p0": {
+ "x": 120.0,
+ "y": -50.0,
+ "z": 10.0
+ },
+ "p1": {
+ "x": 120.0,
+ "y": 50.0,
+ "z": 10.0
+ }
+ },
+ "wallAlpha": [
+ 0.004918209037966988,
+ 0.008088162780793065,
+ 0.013267721240171722,
+ 0.02168493917994337,
+ 0.03525564533465828,
+ 0.056883929412881944,
+ 0.09077715686040604,
+ 0.14258583864393162
+ ],
+ "intersectionType": "BUILDING_ENTER"
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 125.0,
+ "y": 0.0,
+ "z": 10.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "wall": {
+ "p0": {
+ "x": 125.0,
+ "y": 50.0,
+ "z": 10.0
+ },
+ "p1": {
+ "x": 125.0,
+ "y": -50.0,
+ "z": 10.0
+ }
+ },
+ "wallAlpha": [
+ 0.004918209037966988,
+ 0.008088162780793065,
+ 0.013267721240171722,
+ 0.02168493917994337,
+ 0.03525564533465828,
+ 0.056883929412881944,
+ 0.09077715686040604,
+ 0.14258583864393162
+ ],
+ "intersectionType": "BUILDING_EXIT"
+ },
+ {
+ "type": "Receiver",
+ "coordinate": {
+ "x": 200.0,
+ "y": 0.0,
+ "z": 4.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "receiverPk": 0
+ }
+ ],
+ "curvedPath": false,
+ "profileType": "DIRECT"
+}
\ No newline at end of file
diff --git a/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection1.json b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection1.json
new file mode 100644
index 000000000..cba51a0d1
--- /dev/null
+++ b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection1.json
@@ -0,0 +1,703 @@
+{
+ "cutPoints": [
+ {
+ "type": "Source",
+ "coordinate": {
+ "x": 661722.1280113459,
+ "y": 6858873.869236646,
+ "z": 42.44010598910836
+ },
+ "zGround": 42.39010598910655,
+ "groundCoefficient": 0,
+ "sourcePk": 861,
+ "li": 106.64301657903766,
+ "orientation": {
+ "yaw": 77.90634792780077,
+ "pitch": -0.6554503475403527,
+ "roll": 0
+ }
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661740.8114094897,
+ "y": 6858865.446918677,
+ "z": 42.14435438437163
+ },
+ "zGround": 42.14435438437163,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661747.9945218033,
+ "y": 6858862.208832358,
+ "z": 42.04987149831092
+ },
+ "zGround": 42.04987149831092,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661751.7860293298,
+ "y": 6858860.499652811,
+ "z": 42
+ },
+ "zGround": 42,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661755.5728488471,
+ "y": 6858858.792586579,
+ "z": 42.067560638528285
+ },
+ "zGround": 42.067560638528285,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661763.8811518921,
+ "y": 6858855.047273789,
+ "z": 42.153047956692596
+ },
+ "zGround": 42.215789054541034,
+ "groundCoefficient": 0.7,
+ "wall": {
+ "p0": {
+ "x": 661736.0001587445,
+ "y": 6858832.235552125,
+ "z": 42.153047956692596
+ },
+ "p1": {
+ "x": 661766.5767235617,
+ "y": 6858857.252741518,
+ "z": 42.153047956692596
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_ENTER",
+ "wallPk": 57
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661764.0847906731,
+ "y": 6858854.955475148,
+ "z": 42.153047956692596
+ },
+ "zGround": 42.21942217357285,
+ "groundCoefficient": 0.7,
+ "wall": {
+ "p0": {
+ "x": 661766.6691137244,
+ "y": 6858857.06992128,
+ "z": 42.153047956692596
+ },
+ "p1": {
+ "x": 661736.1268063026,
+ "y": 6858832.080760665,
+ "z": 42.153047956692596
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_EXIT",
+ "wallPk": 57
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661767.7305839066,
+ "y": 6858853.311982373,
+ "z": 42.28446676248358
+ },
+ "zGround": 42.28446676248358,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661768.2991860397,
+ "y": 6858853.0556613365,
+ "z": 42.29461119186949
+ },
+ "zGround": 42.29461119186949,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661784.4351092051,
+ "y": 6858845.781723556,
+ "z": 41.53994893837065
+ },
+ "zGround": 41.53994893837065,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661797.4492313622,
+ "y": 6858839.915067284,
+ "z": 44.704094138295034
+ },
+ "zGround": 44.704094138295034,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661800,
+ "y": 6858838.765202342,
+ "z": 45.32426676647552
+ },
+ "zGround": 45.32426676647552,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661807.82296041,
+ "y": 6858835.238677909,
+ "z": 47.226276163068746
+ },
+ "zGround": 47.226276163068746,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661815.0603271817,
+ "y": 6858831.976134139,
+ "z": 47.258794272967364
+ },
+ "zGround": 47.258794272967364,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661822.3446500928,
+ "y": 6858828.6924229385,
+ "z": 46.65822987937826
+ },
+ "zGround": 46.65822987937826,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661829.7054922022,
+ "y": 6858825.37421753,
+ "z": 44.786372809916564
+ },
+ "zGround": 44.786372809916564,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661843.0938927054,
+ "y": 6858819.338839743,
+ "z": 42.821250743592216
+ },
+ "zGround": 42.821250743592216,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661854.1685425139,
+ "y": 6858814.346481213,
+ "z": 42.08000419992201
+ },
+ "zGround": 42.08000419992201,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661855.3638512879,
+ "y": 6858813.807646121,
+ "z": 42
+ },
+ "zGround": 42,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661858.7995597002,
+ "y": 6858812.258857807,
+ "z": 42.12450789959489
+ },
+ "zGround": 42.12450789959489,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661860.5041478842,
+ "y": 6858811.490443861,
+ "z": 42.0958053711009
+ },
+ "zGround": 42.0958053711009,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661862.5864747086,
+ "y": 6858810.551748529,
+ "z": 42.06074233032198
+ },
+ "zGround": 42.06074233032198,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661864.2143704486,
+ "y": 6858809.817906888,
+ "z": 42.072032201719736
+ },
+ "zGround": 42.072032201719736,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661875.21625371,
+ "y": 6858804.858350903,
+ "z": 42.42231030395976
+ },
+ "zGround": 42.42231030395976,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661883.41951339,
+ "y": 6858801.160390774,
+ "z": 42.683485792249776
+ },
+ "zGround": 42.683485792249776,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661885.993631692,
+ "y": 6858800,
+ "z": 42.76544060419645
+ },
+ "zGround": 42.76544060419645,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661893.3239070332,
+ "y": 6858796.695573832,
+ "z": 42.99882200576676
+ },
+ "zGround": 42.99882200576676,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661893.3609066486,
+ "y": 6858796.678894718,
+ "z": 43
+ },
+ "zGround": 43,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661914.709625112,
+ "y": 6858787.055072877,
+ "z": 42.37809825388812
+ },
+ "zGround": 42.37809825388812,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661920.8909043318,
+ "y": 6858784.268604449,
+ "z": 42.19803365906798
+ },
+ "zGround": 42.19803365906798,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661923.2775714569,
+ "y": 6858783.192715081,
+ "z": 42.12850852785233
+ },
+ "zGround": 42.12850852785233,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661929.5027958902,
+ "y": 6858780.386436523,
+ "z": 42.14763386795743
+ },
+ "zGround": 42.14763386795743,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661940.9916210645,
+ "y": 6858775.207371239,
+ "z": 47.6
+ },
+ "zGround": 42.361908118193874,
+ "groundCoefficient": 0,
+ "wall": {
+ "p0": {
+ "x": 661943.5,
+ "y": 6858757,
+ "z": 47.6
+ },
+ "p1": {
+ "x": 661937.7,
+ "y": 6858799.1,
+ "z": 47.6
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_ENTER",
+ "wallPk": 25554
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661948.7511110671,
+ "y": 6858771.709458729,
+ "z": 42.50662778076166
+ },
+ "zGround": 42.50662778076166,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661959.027048498,
+ "y": 6858767.077152988,
+ "z": 47.6
+ },
+ "zGround": 42.26240031460237,
+ "groundCoefficient": 0,
+ "wall": {
+ "p0": {
+ "x": 661954.3,
+ "y": 6858800.9,
+ "z": 47.6
+ },
+ "p1": {
+ "x": 661960.1,
+ "y": 6858759.4,
+ "z": 47.6
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_EXIT",
+ "wallPk": 25554
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661966.5867443474,
+ "y": 6858763.669305995,
+ "z": 42.082729566847085
+ },
+ "zGround": 42.082729566847085,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661970.0676134975,
+ "y": 6858762.100159602,
+ "z": 42
+ },
+ "zGround": 42,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661995.2320435718,
+ "y": 6858750.756247217,
+ "z": 42.972074311698016
+ },
+ "zGround": 42.972074311698016,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661996.0050303232,
+ "y": 6858750.407791322,
+ "z": 42.95023309690925
+ },
+ "zGround": 42.95023309690925,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662000,
+ "y": 6858748.606892758,
+ "z": 42.837352778325354
+ },
+ "zGround": 42.837352778325354,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662037.0775299438,
+ "y": 6858731.892655642,
+ "z": 41.78970442774896
+ },
+ "zGround": 41.78970442774896,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662038.0421351499,
+ "y": 6858731.457819768,
+ "z": 43.05159988151934
+ },
+ "zGround": 43.05159988151934,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662042.7914561786,
+ "y": 6858729.31686599,
+ "z": 43.38033554820951
+ },
+ "zGround": 43.38033554820951,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Reflection",
+ "coordinate": {
+ "x": 662071.9044826354,
+ "y": 6858716.192959729,
+ "z": 43.5806546101346
+ },
+ "zGround": 44.56704258037549,
+ "groundCoefficient": 0,
+ "wall": {
+ "p0": {
+ "x": 662071.9,
+ "y": 6858715.2,
+ "z": 52.55489252838457
+ },
+ "p1": {
+ "x": 662072.1,
+ "y": 6858752,
+ "z": 52.55489252838457
+ }
+ },
+ "wallPk": 122063,
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662026.9941156902,
+ "y": 6858696.531985256,
+ "z": 43.887764860075244
+ },
+ "zGround": 43.887764860075244,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662025.823516249,
+ "y": 6858696.019517261,
+ "z": 43.84846633159078
+ },
+ "zGround": 43.84846633159078,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662025.618683483,
+ "y": 6858695.929845052,
+ "z": 43.65509803766903
+ },
+ "zGround": 43.65509803766903,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662021.3098566302,
+ "y": 6858694.043515892,
+ "z": 43.920358422920515
+ },
+ "zGround": 43.920358422920515,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662010.55493262,
+ "y": 6858689.335197952,
+ "z": 43.49235029882455
+ },
+ "zGround": 43.49235029882455,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662000,
+ "y": 6858684.714432749,
+ "z": 43.801217556298184
+ },
+ "zGround": 43.801217556298184,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661993.2069999373,
+ "y": 6858681.740575934,
+ "z": 44
+ },
+ "zGround": 44,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661982.5458493708,
+ "y": 6858677.073310369,
+ "z": 43.946991631699134
+ },
+ "zGround": 43.946991631699134,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661974.1912606092,
+ "y": 6858673.415817288,
+ "z": 42.83640757612829
+ },
+ "zGround": 42.83640757612829,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661967.6606602207,
+ "y": 6858670.556834469,
+ "z": 42
+ },
+ "zGround": 42,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661965.3842923581,
+ "y": 6858669.560280366,
+ "z": 42.28209942482084
+ },
+ "zGround": 42.28209942482084,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661963.3260077994,
+ "y": 6858668.659199325,
+ "z": 42.33211268305454
+ },
+ "zGround": 42.33211268305454,
+ "groundCoefficient": 0
+ },
+ {
+ "type": "Receiver",
+ "coordinate": {
+ "x": 661937.0197737766,
+ "y": 6858657.142789401,
+ "z": 47.963762705849874
+ },
+ "zGround": 43.963762705849874,
+ "groundCoefficient": 0,
+ "receiverPk": 121249
+ }
+ ],
+ "curvedPath": false,
+ "profileType": "REFLECTION"
+}
diff --git a/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection2.json b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection2.json
new file mode 100644
index 000000000..17344d2a5
--- /dev/null
+++ b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestReflection2.json
@@ -0,0 +1,715 @@
+{
+ "cutPoints": [
+ {
+ "type": "Source",
+ "coordinate": {
+ "x": 661889.9101599681,
+ "y": 6858926.937746482,
+ "z": 41.05
+ },
+ "zGround": 41.0,
+ "groundCoefficient": 0.0,
+ "sourcePk": 2091,
+ "li": 107.35421692812405,
+ "orientation": {
+ "yaw": 259.02175151813043,
+ "pitch": 0.6724250317751295,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661913.5029535431,
+ "y": 6858919.415421733,
+ "z": 41.0
+ },
+ "zGround": 41.0,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661916.6431041227,
+ "y": 6858918.414216329,
+ "z": 40.90051409332349
+ },
+ "zGround": 40.90051409332349,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661917.6079050247,
+ "y": 6858918.106599273,
+ "z": 40.87824060482728
+ },
+ "zGround": 40.87824060482728,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661920.9799701656,
+ "y": 6858917.0314502455,
+ "z": 41.0
+ },
+ "zGround": 41.0,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661926.8403330224,
+ "y": 6858915.162932538,
+ "z": 41.0
+ },
+ "zGround": 41.0,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661956.1829900739,
+ "y": 6858905.8073216975,
+ "z": 40.226139978743525
+ },
+ "zGround": 40.226139978743525,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661962.5638051131,
+ "y": 6858903.77286305,
+ "z": 40.05785741137583
+ },
+ "zGround": 40.05785741137583,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661964.6971017006,
+ "y": 6858903.092682907,
+ "z": 40.0
+ },
+ "zGround": 40.0,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661965.8226491843,
+ "y": 6858902.733813422,
+ "z": 40.0
+ },
+ "zGround": 40.0,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661969.0200300844,
+ "y": 6858901.714360706,
+ "z": 40.287948666312865
+ },
+ "zGround": 40.287948666312865,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661983.1292780923,
+ "y": 6858897.215769,
+ "z": 42.153047956692596
+ },
+ "zGround": 40.51237513806361,
+ "groundCoefficient": 0.7,
+ "wall": {
+ "p0": {
+ "x": 661875.3488122831,
+ "y": 6858885.041231335,
+ "z": 42.153047956692596
+ },
+ "p1": {
+ "x": 661994.8152757528,
+ "y": 6858898.535782025,
+ "z": 42.153047956692596
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_ENTER",
+ "wallPk": 57
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 661983.5954045337,
+ "y": 6858897.067149277,
+ "z": 42.153047956692596
+ },
+ "zGround": 40.51978950292175,
+ "groundCoefficient": 0.7,
+ "wall": {
+ "p0": {
+ "x": 661994.8372817191,
+ "y": 6858898.336995869,
+ "z": 42.153047956692596
+ },
+ "p1": {
+ "x": 661875.3849599758,
+ "y": 6858884.844042583,
+ "z": 42.153047956692596
+ }
+ },
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ],
+ "intersectionType": "BUILDING_EXIT",
+ "wallPk": 57
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661985.2038901383,
+ "y": 6858896.554299832,
+ "z": 40.54537461907603
+ },
+ "zGround": 40.54537461907603,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661985.81277611,
+ "y": 6858896.36016267,
+ "z": 40.57729626062577
+ },
+ "zGround": 40.57729626062577,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 661989.4344671343,
+ "y": 6858895.205422942,
+ "z": 40.88181465564866
+ },
+ "zGround": 40.88181465564866,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 661992.5869774569,
+ "y": 6858894.2002767585,
+ "z": 41.019235343444116
+ },
+ "zGround": 41.019235343444116,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662000.0,
+ "y": 6858891.836709139,
+ "z": 41.342375492822654
+ },
+ "zGround": 41.342375492822654,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662009.5252251249,
+ "y": 6858888.799686988,
+ "z": 41.75758840385592
+ },
+ "zGround": 41.75758840385592,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662013.5485240932,
+ "y": 6858887.51689861,
+ "z": 42.8328109063298
+ },
+ "zGround": 42.8328109063298,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662018.649741981,
+ "y": 6858885.890426635,
+ "z": 42.779690376966016
+ },
+ "zGround": 42.779690376966016,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662020.2446352348,
+ "y": 6858885.381910974,
+ "z": 43.33869814795687
+ },
+ "zGround": 43.33869814795687,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662030.4334065545,
+ "y": 6858882.133323798,
+ "z": 44.19074973480001
+ },
+ "zGround": 44.19074973480001,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662032.6278568481,
+ "y": 6858881.433645411,
+ "z": 44.232858911795375
+ },
+ "zGround": 44.232858911795375,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662044.9971252306,
+ "y": 6858877.489828695,
+ "z": 45.65462197291275
+ },
+ "zGround": 45.65462197291275,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662046.1567079299,
+ "y": 6858877.1201074235,
+ "z": 45.94366184062431
+ },
+ "zGround": 45.94366184062431,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662050.398266756,
+ "y": 6858875.767729087,
+ "z": 46.05098869749143
+ },
+ "zGround": 46.05098869749143,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662065.0595961693,
+ "y": 6858871.093111782,
+ "z": 44.943828226934514
+ },
+ "zGround": 44.943828226934514,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662067.8854306248,
+ "y": 6858870.192122911,
+ "z": 45.125203181355666
+ },
+ "zGround": 45.125203181355666,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662103.8345642475,
+ "y": 6858858.730103515,
+ "z": 45.15340234322309
+ },
+ "zGround": 45.15340234322309,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662106.6901667969,
+ "y": 6858857.8196233865,
+ "z": 44.89983825400298
+ },
+ "zGround": 44.89983825400298,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662107.4529237237,
+ "y": 6858857.576426018,
+ "z": 44.853570927785036
+ },
+ "zGround": 44.853570927785036,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662109.8352446188,
+ "y": 6858856.816846981,
+ "z": 43.915413852719844
+ },
+ "zGround": 43.915413852719844,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662112.4365645075,
+ "y": 6858855.987442318,
+ "z": 45.171376848433134
+ },
+ "zGround": 45.171376848433134,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662118.058582937,
+ "y": 6858854.194918333,
+ "z": 45.258225686208156
+ },
+ "zGround": 45.258225686208156,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662124.0032503043,
+ "y": 6858852.2995209815,
+ "z": 44.186594903938065
+ },
+ "zGround": 44.186594903938065,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662133.2520707784,
+ "y": 6858849.350627671,
+ "z": 42.51933244706706
+ },
+ "zGround": 42.51933244706706,
+ "groundCoefficient": 0.7
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662133.9864828198,
+ "y": 6858849.116467783,
+ "z": 42.58264711370354
+ },
+ "zGround": 42.58264711370354,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662147.8278512482,
+ "y": 6858844.703286786,
+ "z": 43.775930418111535
+ },
+ "zGround": 43.775930418111535,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662148.7251064538,
+ "y": 6858844.417205995,
+ "z": 43.74802009707152
+ },
+ "zGround": 43.74802009707152,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662157.8768260743,
+ "y": 6858841.499272314,
+ "z": 41.400029107436254
+ },
+ "zGround": 41.400029107436254,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662162.0412608084,
+ "y": 6858840.171484218,
+ "z": 41.47202149091655
+ },
+ "zGround": 41.47202149091655,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662172.3362832965,
+ "y": 6858836.889019926,
+ "z": 41.73890921652997
+ },
+ "zGround": 41.73890921652997,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662181.2042895042,
+ "y": 6858834.061545411,
+ "z": 41.96880303271502
+ },
+ "zGround": 41.96880303271502,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662182.2046410742,
+ "y": 6858833.7425933825,
+ "z": 42.0
+ },
+ "zGround": 42.0,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "Reflection",
+ "coordinate": {
+ "x": 662182.2984317525,
+ "y": 6858833.712689169,
+ "z": 42.02355479548803
+ },
+ "zGround": 42.010475103236914,
+ "groundCoefficient": 0.3,
+ "wall": {
+ "p0": {
+ "x": 662183.0,
+ "y": 6858829.1,
+ "z": 48.0560792335534
+ },
+ "p1": {
+ "x": 662181.8,
+ "y": 6858837.0,
+ "z": 48.0560792335534
+ }
+ },
+ "wallPk": 100530,
+ "wallAlpha": [
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1,
+ 0.1
+ ]
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662181.7796757604,
+ "y": 6858833.350006894,
+ "z": 42.031913426757605
+ },
+ "zGround": 42.031913426757605,
+ "groundCoefficient": 0.3
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662178.7927028377,
+ "y": 6858831.261699192,
+ "z": 42.275596183120854
+ },
+ "zGround": 42.275596183120854,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662165.3781838162,
+ "y": 6858821.883092785,
+ "z": 43.36997736936667
+ },
+ "zGround": 43.36997736936667,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662162.2306892686,
+ "y": 6858819.682558239,
+ "z": 42.78146451408848
+ },
+ "zGround": 42.78146451408848,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662157.7161949413,
+ "y": 6858816.526301548,
+ "z": 42.3850420509512
+ },
+ "zGround": 42.3850420509512,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662151.0290400238,
+ "y": 6858811.851054239,
+ "z": 42.5639259733648
+ },
+ "zGround": 42.5639259733648,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662138.8453854444,
+ "y": 6858803.332992525,
+ "z": 42.84243663546004
+ },
+ "zGround": 42.84243663546004,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662137.2654094909,
+ "y": 6858802.228370543,
+ "z": 42.882604707540764
+ },
+ "zGround": 42.882604707540764,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662135.3628447418,
+ "y": 6858800.89821432,
+ "z": 42.92289287225354
+ },
+ "zGround": 42.92289287225354,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "GroundEffect",
+ "coordinate": {
+ "x": 662134.078100233,
+ "y": 6858800.0,
+ "z": 42.95009825298734
+ },
+ "zGround": 42.95009825298734,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662119.6447983974,
+ "y": 6858789.909123328,
+ "z": 43.25573370738
+ },
+ "zGround": 43.25573370738,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662107.750763736,
+ "y": 6858781.593546041,
+ "z": 43.47209529936684
+ },
+ "zGround": 43.47209529936684,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662101.2436656111,
+ "y": 6858777.044183365,
+ "z": 43.62571829940858
+ },
+ "zGround": 43.62571829940858,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662047.7770049316,
+ "y": 6858739.663583614,
+ "z": 43.22019380950089
+ },
+ "zGround": 43.22019380950089,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662038.678030506,
+ "y": 6858733.302140486,
+ "z": 43.010128445261316
+ },
+ "zGround": 43.010128445261316,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Topography",
+ "coordinate": {
+ "x": 662037.1966272618,
+ "y": 6858732.266434478,
+ "z": 41.770316492253535
+ },
+ "zGround": 41.770316492253535,
+ "groundCoefficient": 0.0
+ },
+ {
+ "type": "Receiver",
+ "coordinate": {
+ "x": 662037.0197737766,
+ "y": 6858732.142789401,
+ "z": 45.78293649348511
+ },
+ "zGround": 41.78293649348511,
+ "groundCoefficient": 0.0,
+ "receiverPk": 122981
+ }
+ ],
+ "curvedPath": false,
+ "profileType": "REFLECTION"
+}
diff --git a/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestSingleDif.json b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestSingleDif.json
new file mode 100644
index 000000000..27cad7284
--- /dev/null
+++ b/noisemodelling-propagation/src/test/resources/org/noise_planet/noisemodelling/propagation/RegressionTestSingleDif.json
@@ -0,0 +1,67 @@
+{
+ "cutPoints": [
+ {
+ "type": "Source",
+ "coordinate": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "sourcePk": -1,
+ "li": 1.0,
+ "orientation": {
+ "yaw": 0.0,
+ "pitch": 0.0,
+ "roll": 0.0
+ }
+ },
+ {
+ "type": "Wall",
+ "coordinate": {
+ "x": 20.0,
+ "y": 0.0,
+ "z": 6.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "wall": {
+ "p0": {
+ "x": 20.0,
+ "y": -100.0,
+ "z": 6.0
+ },
+ "p1": {
+ "x": 20.0,
+ "y": 100.0,
+ "z": 6.0
+ }
+ },
+ "wallAlpha": [
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0
+ ],
+ "intersectionType": "THIN_WALL_ENTER_EXIT"
+ },
+ {
+ "type": "Receiver",
+ "coordinate": {
+ "x": 50.0,
+ "y": 0.0,
+ "z": 4.0
+ },
+ "zGround": 0.0,
+ "groundCoefficient": 0.5,
+ "receiverPk": 0
+ }
+ ],
+ "curvedPath": false,
+ "profileType": "DIRECT"
+}
\ No newline at end of file
diff --git a/noisemodelling-scripts/pom.xml b/noisemodelling-scripts/pom.xml
new file mode 100644
index 000000000..65d42e46d
--- /dev/null
+++ b/noisemodelling-scripts/pom.xml
@@ -0,0 +1,591 @@
+
+
+ 4.0.0
+
+ org.noise-planet
+ noisemodelling-parent
+ 6.0.1-SNAPSHOT
+ ../pom.xml
+
+
+ noisemodelling-scripts
+ NoiseModelling Scripts
+ WebServer and groovy scripts of NoiseModelling
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+
+
+
+ unix-config
+
+ unix
+
+
+ sh
+ -c
+
+
+ command -v python3 >/dev/null 2>&1 && (
+ python3 -m venv target/venv &&
+ target/venv/bin/python -m pip install -r ../Docs/requirements.txt &&
+ target/venv/bin/sphinx-build -M html ../Docs target/sphinx
+ ) || echo "Python3 not found. Skipping documentation generation."
+
+
+
+
+ windows-config
+
+ windows
+
+
+ cmd
+ /c
+
+ where python >nul 2>nul && ( python -m venv target/venv && target\venv\Scripts\activate.bat && pip install -r ..\Docs\requirements.txt && sphinx-build -M html ..\Docs target\sphinx ) || echo Python not found. Skipping documentation generation.
+
+
+
+
+ macos-bundler
+
+ mac
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 3.5.0
+
+
+ regex-property
+
+ regex-property
+
+
+ macOS.version
+ ${project.version}
+
+ ^([0-9]+(\.[0-9]+)*).*
+ $1
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.6.0
+
+
+ copy-dependencies
+ prepare-package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/macos-stage/lib
+
+
+
+
+
+
+ maven-resources-plugin
+ 3.3.1
+
+
+ copy-app-files
+ package
+
+ copy-resources
+
+
+ ${project.build.directory}/macos-stage/lib
+
+
+
+ ${project.build.directory}
+
+ ${project.build.finalName}.jar
+ help/**
+
+
+
+
+
+
+ copy-help-files
+ package
+
+ copy-resources
+
+
+ ${project.build.directory}/macos-stage/help
+
+
+
+ ${project.build.directory}/sphinx/html/
+
+ **
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.1.0
+
+
+ create-macos-app
+ package
+
+ exec
+
+
+ jpackage
+
+ --input
+ ${project.build.directory}/macos-stage
+ --main-jar
+ lib/${project.build.finalName}.jar
+ --main-class
+ org.noise_planet.noisemodelling.webserver.NoiseModellingServer
+ --type
+ dmg
+ --dest
+ ${project.build.directory}
+ --name
+ NoiseModelling
+ --icon
+ ../installer/Logo_noisemodelling_square.icns
+ --vendor
+ ${parent.organization.name}
+ --app-version
+ ${macOS.version}
+ --mac-package-name
+ NoiseModelling
+ --arguments
+ -u -r ""
+
+
+
+
+
+
+
+
+
+
+
+ ${project.groupId}
+ noisemodelling-jdbc
+ ${project.version}
+
+
+ ${project.groupId}
+ noisemodelling-emission
+ ${project.version}
+
+
+ ${project.groupId}
+ noisemodelling-pathfinder
+ ${project.version}
+
+
+ ${project.groupId}
+ noisemodelling-propagation
+ ${project.version}
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.apache.groovy
+ groovy-templates
+
+
+ org.geotools.ogc
+ net.opengis.wps
+
+
+ org.geotools.xsd
+ gt-xsd-wps
+
+
+ org.orbisgis
+ h2gis-utilities
+
+
+ org.orbisgis
+ h2gis
+
+
+ org.orbisgis
+ postgis-jts
+
+
+ com.h2database
+ h2
+
+
+ org.postgresql
+ postgresql
+
+
+ commons-cli
+ commons-cli
+
+
+ org.osgi
+ org.osgi.service.jdbc
+
+
+ com.opencsv
+ opencsv
+
+
+ io.javalin
+ javalin-bundle
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+ org.apache.groovy
+ groovy-sql
+
+
+ org.apache.groovy
+ groovy-json
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.groovy
+ groovy
+
+
+ org.slf4j
+ slf4j-reload4j
+
+
+ com.zaxxer
+ HikariCP
+
+
+ com.auth0
+ java-jwt
+
+
+ io.javalin
+ javalin-rendering
+
+
+ org.thymeleaf
+ thymeleaf
+
+
+ com.atlassian
+ onetime
+
+
+ com.google.zxing
+ core
+
+
+ com.google.zxing
+ javase
+
+
+ org.openstreetmap.osmosis
+ osmosis-core
+
+
+ org.openstreetmap.osmosis
+ osmosis-pbf
+
+
+ org.openstreetmap.osmosis
+ osmosis-xml
+
+
+ org.jfree
+ jfreechart
+
+
+ org.matsim
+ matsim
+
+
+ org.geotools
+ *
+
+
+
+ org.apache.logging.log4j
+ log4j-1.2-api
+
+
+
+
+
+
+
+
+ ${project.basedir}/src/main/resources
+
+ org/noise_planet/noisemodelling/version.properties
+
+ true
+
+
+
+ ${project.basedir}/src/main/resources
+
+ org/noise_planet/noisemodelling/version.properties
+
+ false
+
+
+
+ ${project.basedir}/src/main/groovy/org/noise_planet/noisemodelling/scripts
+
+ **/*.groovy
+
+ scripts
+ false
+
+
+
+
+
+ org.codehaus.mojo
+ exec-maven-plugin
+ 3.5.1
+
+
+ generate-functions-docs
+ package
+
+ exec
+
+
+ ${java.home}/bin/java
+
+ -classpath
+
+ org.noise_planet.noisemodelling.autodoc.GenerateFunctionsDocs
+ ${project.parent.basedir}/Docs
+ ${project.basedir}/src/main/groovy/org/noise_planet/noisemodelling/scripts
+
+
+
+
+ generate-cnossos-report
+ package
+
+ exec
+
+
+ ${java.home}/bin/java
+
+ -classpath
+
+ org.noise_planet.noisemodelling.autodoc.GenerateReferenceDeviation
+ ${project.parent.basedir}/Docs
+
+
+
+
+ generate-documentation
+ package
+
+ exec
+
+
+
+ ${shell.executable}
+
+ ${shell.arg}
+ ${sphinx.command}
+
+ true
+
+
+
+
+
+
+ org.codehaus.gmavenplus
+ gmavenplus-plugin
+ 4.2.1
+
+
+
+ addSources
+ addTestSources
+ compile
+ compileTests
+
+
+
+
+
+
+ ${project.basedir}/src/main/groovy
+
+ **/*.groovy
+
+
+
+
+
+ ${project.basedir}/src/test/groovy
+
+ **/*.groovy
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ appassembler-maven-plugin
+ 2.1.0
+
+
+ assemble
+ package
+
+ assemble
+
+
+
+
+
+
+ org.noise_planet.noisemodelling.webserver.NoiseModellingServer
+ WebServer
+
+
+ org.noise_planet.noisemodelling.runner.Main
+ ScriptRunner
+
+
+
+ flat
+ true
+ lib
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ create-distribution
+ package
+
+ single
+
+
+ NoiseModelling_${project.parent.version}
+ false
+
+ src/assembly/distribution.xml
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 3.2.0
+
+
+
+ ${project.build.outputDirectory}/META-INF/MANIFEST.MF
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 3.0.1
+
+
+ attach-sources
+
+ jar
+
+ package
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 11
+ 11
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ 5.1.1
+ true
+
+
+ bundle-manifest
+ process-classes
+
+ manifest
+
+
+
+
+
+ jar
+ bundle
+ war
+
+
+ org.noise_planet.noisemodelling.*
+ UMRAE team (Eiffel University), DECIDE team (Lab-STICC)
+ org.slf4j;version="[1.6.0,2)",!org.h2.*,*
+ ${buildNumber}
+
+
+
+
+
+
+
diff --git a/noisemodelling-scripts/src/assembly/distribution.xml b/noisemodelling-scripts/src/assembly/distribution.xml
new file mode 100644
index 000000000..63a9695d4
--- /dev/null
+++ b/noisemodelling-scripts/src/assembly/distribution.xml
@@ -0,0 +1,65 @@
+
+
+ dist
+
+ zip
+
+
+ true
+
+
+
+
+ ${project.build.directory}/appassembler
+ /
+
+ **/*
+
+ 0755
+
+
+
+ ${project.basedir}/src/assembly
+ /
+
+ start_linux_macos.sh
+ start_windows.bat
+
+ 0755
+
+
+
+ ${project.basedir}/src/test/resources/org/noise_planet/noisemodelling/scripts
+ /resources
+
+
+
+ ${project.basedir}/target/sphinx/html
+ /help
+
+
+
+ ${project.basedir}/src/main/groovy/org/noise_planet/noisemodelling/scripts
+ scripts
+
+
+ ${project.parent.basedir}
+ /
+
+ LICENSE
+ README.md
+
+
+
+ ${project.basedir}/src/test/groovy/org/noise_planet/noisemodelling/scripts
+ /
+
+ get_started_tutorial_complex.groovy
+
+ 0755
+
+
+
\ No newline at end of file
diff --git a/noisemodelling-scripts/src/assembly/start_linux_macos.sh b/noisemodelling-scripts/src/assembly/start_linux_macos.sh
new file mode 100644
index 000000000..707f087ee
--- /dev/null
+++ b/noisemodelling-scripts/src/assembly/start_linux_macos.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+bash bin/WebServer -u -s scripts/ -r ""
diff --git a/noisemodelling-scripts/src/assembly/start_windows.bat b/noisemodelling-scripts/src/assembly/start_windows.bat
new file mode 100644
index 000000000..6044418ca
--- /dev/null
+++ b/noisemodelling-scripts/src/assembly/start_windows.bat
@@ -0,0 +1 @@
+call bin\WebServer.bat -u -s scripts/ -r ""
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Add_Laeq_Leq_columns.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Add_Laeq_Leq_columns.groovy
similarity index 75%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Add_Laeq_Leq_columns.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Add_Laeq_Leq_columns.groovy
index 910b92a91..4e830f47a 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Add_Laeq_Leq_columns.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Add_Laeq_Leq_columns.groovy
@@ -15,20 +15,17 @@
* @Author Arnaud Can, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Acoustic_Tools
+package org.noise_planet.noisemodelling.scripts.Acoustic_Tools
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.sql.Connection
-title = 'Add Leq and LAeq columns'
-description = '➡️ Add the columns Leq and LAeq to a table with octave band values from 63 Hz to 8000 Hz.'+
+title = 'Add LAeq and Leq columns'
+description = '➡️ Add the columns LAeq and Leq to a table with octave band values from 63 Hz to 8000 Hz.'+
' ' +
'The columns of the table should be named HZ63, HZ125,..., HZ8000 with an HZ prefix that can be changed.'
@@ -43,7 +40,7 @@ inputs = [
tableName: [
title : 'Name of the table',
name : 'Name of the table',
- description: 'Name of the table on which Leq and LAeq columns will be added.',
+ description: 'Name of the table on which LAeq and Leq columns will be added.',
type : String.class
]
]
@@ -58,15 +55,6 @@ outputs = [
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
def exec(Connection connection, input) {
// output string, the information given back to the user
@@ -114,17 +102,3 @@ def exec(Connection connection, input) {
return resultString
}
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isolines.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isolines.groovy
similarity index 89%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isolines.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isolines.groovy
index 987146d06..56220a375 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isolines.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isolines.groovy
@@ -1,387 +1,358 @@
-/**
- * NoiseModelling is an open-source tool designed to produce environmental noise maps on very large urban areas. It can be used as a Java library or be controlled through a user friendly web interface.
- *
- * This version is developed by the DECIDE team from the Lab-STICC (CNRS) and by the Mixt Research Unit in Environmental Acoustics (Université Gustave Eiffel).
- *
- *
- * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
- *
- * Contact: contact@noise-planet.org
- *
- */
-
-/**
- * @Author Adapted from NM WPS scripts
- * @Author Ignacio Soto Molina, Ministry for Ecological Transition (MITECO), Spain
- */
-
-package org.noise_planet.noisemodelling.wps.Acoustic_Tools
-
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
-import org.h2gis.utilities.GeometryTableUtilities
-import org.h2gis.utilities.TableLocation
-import org.h2gis.utilities.wrapper.ConnectionWrapper
-import org.locationtech.jts.geom.*
-import org.slf4j.Logger
-import org.slf4j.LoggerFactory
-
-import java.sql.Connection
-import java.sql.SQLException
-
-// ----------------
-// Create Isolines (Isophones) from TRIANGLES + RECEIVERS_LEVEL
-// ----------------
-
-title = 'Create Isolines (Isophones)'
-
-description = 'Generate isolines (isophones) by linear interpolation on triangle edges (marching-triangles).' +
- ' One multilines map per PERIOD and per LEVEL is created.' +
- ' Main output table : ISOLINES_NOISE_MAP ' +
- 'with : ' +
- '- PERIOD : receivers period label (VARCHAR). ' +
- '- LEVEL : isoline value (DOUBLE). ' +
- '- THE_GEOM : MULTILINESTRING/LINESTRING geometry.' +
- ' Additional output tables : one table per PERIOD, ' +
- 'named L<PERIOD>_ISOLINES_NOISE_MAP, ' +
- 'containing only the isolines of that PERIOD (same structure as above).'
-
-inputs = [
- trianglesTable : [
- name : 'Triangles table',
- title : 'Triangles table',
- description: 'Name of the triangles table. ' +
- 'Shall contain : PK, THE_GEOM, PK_1, PK_2, PK_3, CELL_ID.',
- min : 1, max: 1,
- type : String.class
- ],
- receiversTable : [
- name : 'Receivers level table',
- title : 'Receivers level table',
- description: 'Name of the receivers level table. ' +
- 'Shall contain : IDRECEIVER, PERIOD, THE_GEOM, LAEQ (or any field to contour).',
- min : 1, max: 1,
- type : String.class
- ],
- fieldName : [
- name : 'Field to contour',
- title : 'Field to contour',
- description: 'Receivers numeric field to contour (e.g. LAEQ). Default: LAEQ.',
- min : 0, max: 1,
- type : String.class
- ],
- isoClasses : [
- name : 'Iso levels (dB)',
- title : 'Iso levels (dB)',
- description: 'Comma-separated levels. Default: 35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0',
- min : 0, max: 1,
- type : String.class
- ]
-]
-
-outputs = [
- result: [
- name: 'Result output string',
- title: 'Result output string',
- description: 'This type of result does not allow the blocks to be linked together.',
- type: String.class
- ]
-]
-
-// -------------------
-// Open Connection to Geoserver (same pattern as Template)
-// -------------------
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// -------------------
-// run() wrapper (same pattern as Template)
-// -------------------
-def run(input) {
- String dbName = "h2gisdb"
- openGeoserverDataStoreConnection(dbName).withCloseable { Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-// -------------------
-// Main function (same structure as Template.exec)
-// -------------------
-def exec(Connection connection, input) {
-
- // Need to change the ConnectionWrapper to WpsConnectionWrapper to work under postGIS database
- connection = new ConnectionWrapper(connection)
-
- // Create a sql connection to interact with the database in SQL
- Sql sql = new Sql(connection)
-
- // Logger
- Logger logger = LoggerFactory.getLogger("org.noise_planet.noisemodelling")
-
- // print to command window
- logger.info('Start : Create Isolines')
- logger.info("inputs {}", input)
-
- // -------------------
- // Get inputs
- // -------------------
- String trianglesTable = (input['trianglesTable'] ?: 'TRIANGLES') as String
- String receiversTable = (input['receiversTable'] ?: 'RECEIVERS_LEVEL') as String
- String fieldName = (input['fieldName'] ?: 'LAEQ') as String
- String isoClassesStr = (input['isoClasses'] ?: '35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0') as String
-
- // do it case-insensitive for table names
- trianglesTable = trianglesTable.toUpperCase()
- receiversTable = receiversTable.toUpperCase()
-
- // Parse levels (sorted)
- List levels = isoClassesStr.split(',')
- .collect { it.trim() }
- .findAll { it }
- .collect { it as Double }
- .sort()
-
- // -------------------
- // Checks
- // -------------------
- def triExists = org.h2gis.utilities.JDBCUtilities.tableExists(connection, trianglesTable)
- def recExists = org.h2gis.utilities.JDBCUtilities.tableExists(connection, receiversTable)
- if (!triExists) throw new SQLException(String.format("The table %s does not exist.", trianglesTable))
- if (!recExists) throw new SQLException(String.format("The table %s does not exist.", receiversTable))
-
- // SRID (receivers first, then triangles)
- int srid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(receiversTable))
- if (srid == 0) srid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(trianglesTable))
- if (srid == 0) throw new SQLException("Unable to determine SRID of geometries (receivers/triangles).")
-
- // Validate field existence in receivers (case-insensitive)
- def cntRow = sql.firstRow(
- "SELECT COUNT(*) AS CNT FROM INFORMATION_SCHEMA.COLUMNS WHERE UPPER(TABLE_NAME)=? AND UPPER(COLUMN_NAME)=?",
- [receiversTable.toUpperCase(), fieldName.toUpperCase()]
- )
- if ((cntRow?.CNT ?: 0) == 0) {
- throw new SQLException(String.format("Field '%s' does not exist in %s", fieldName, receiversTable))
- }
-
- // -------------------
- // Create output & temporaries (NO placeholders in DDL)
- // -------------------
- String outTable = "ISOLINES_NOISE_MAP"
- String segTable = "TMP_ISO_SEGMENTS"
- String resultString
-
- sql.execute("DROP TABLE IF EXISTS \"" + outTable + "\"")
- sql.execute("""CREATE TABLE "${outTable}"(
- "PERIOD" VARCHAR,
- "LEVEL" DOUBLE,
- "THE_GEOM" GEOMETRY
- )""")
- sql.execute("CREATE SPATIAL INDEX ON \"" + outTable + "\"(\"THE_GEOM\")")
-
- sql.execute("DROP TABLE IF EXISTS \"" + segTable + "\"")
- sql.execute("""CREATE TABLE "${segTable}"(
- "PERIOD" VARCHAR,
- "LEVEL" DOUBLE,
- "THE_GEOM" GEOMETRY
- )""")
- sql.execute("CREATE SPATIAL INDEX ON \"" + segTable + "\"(\"THE_GEOM\")")
-
- // -------------------
- // Periods list
- // -------------------
- List periods = []
- sql.eachRow("SELECT DISTINCT \"PERIOD\" FROM \"" + receiversTable + "\" WHERE \"PERIOD\" IS NOT NULL") { r ->
- periods.add(r.PERIOD?.toString())
- }
- if (periods.isEmpty()) periods = [null]
-
- // -------------------
- // Geometry machinery
- // -------------------
- GeometryFactory gf = new GeometryFactory(new PrecisionModel(), srid)
- double eps = 1e-9
-
- // Interpolate point on edge
- def interpPoint = { Coordinate a, Coordinate b, double va, double vb, double cLevel ->
- double dv = (vb - va)
- if (Math.abs(dv) < eps) return null
- double t = (cLevel - va) / dv
- if (t < -eps || t > 1 + eps) return null
- if (t < 0) t = 0
- if (t > 1) t = 1
- new Coordinate(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y))
- }
-
- // Load triangles to memory
- List triRows = []
- sql.eachRow("SELECT \"PK\", \"PK_1\", \"PK_2\", \"PK_3\" FROM \"" + trianglesTable + "\"") { r ->
- triRows.add([PK: r.PK as Integer, A: r.PK_1 as Integer, B: r.PK_2 as Integer, C: r.PK_3 as Integer])
- }
-
- // Insert batch for segments
- String insSegSQL = "INSERT INTO " + segTable + "(PERIOD, LEVEL, THE_GEOM) VALUES (?, ?, ?)"
-
- // -------------------
- // Build segments by PERIOD & LEVEL
- // -------------------
- periods.each { periodVal ->
- // Map: receiver id -> (coord, val)
- Map recMap = [:]
- String sqlRec = (periodVal == null) ?
- "SELECT IDRECEIVER, THE_GEOM, " + fieldName + " AS VAL FROM " + receiversTable :
- "SELECT IDRECEIVER, THE_GEOM, " + fieldName + " AS VAL FROM " + receiversTable + " WHERE PERIOD = ?"
-
- if (periodVal == null) {
- sql.eachRow(sqlRec) { r ->
- Point pt = r.THE_GEOM as Point
- if (pt != null) recMap[r.IDRECEIVER as Integer] = [coord: pt.coordinate, val: (r.VAL as Double)]
- }
- } else {
- sql.eachRow(sqlRec, [periodVal]) { r ->
- Point pt = r.THE_GEOM as Point
- if (pt != null) recMap[r.IDRECEIVER as Integer] = [coord: pt.coordinate, val: (r.VAL as Double)]
- }
- }
-
- if (recMap.isEmpty()) return
-
- sql.withBatch(5000, insSegSQL) { ps ->
- triRows.each { trow ->
- def rA = recMap[trow.A]; def rB = recMap[trow.B]; def rC = recMap[trow.C]
- if (rA == null || rB == null || rC == null) return
-
- Coordinate A = rA.coord as Coordinate
- Coordinate B = rB.coord as Coordinate
- Coordinate C = rC.coord as Coordinate
- double vA = (rA.val as Double)
- double vB = (rB.val as Double)
- double vC = (rC.val as Double)
-
- levels.each { L ->
- List hits = []
-
- // AB
- if ((L > Math.min(vA, vB) - eps) && (L < Math.max(vA, vB) + eps) && Math.abs(vA - vB) > eps) {
- def p = interpPoint(A, B, vA, vB, L); if (p != null) hits << p
- } else if (Math.abs(vA - L) <= eps && Math.abs(vA - vB) > eps) {
- hits << A
- } else if (Math.abs(vB - L) <= eps && Math.abs(vA - vB) > eps) {
- hits << B
- }
- // BC
- if ((L > Math.min(vB, vC) - eps) && (L < Math.max(vB, vC) + eps) && Math.abs(vB - vC) > eps) {
- def p = interpPoint(B, C, vB, vC, L); if (p != null) hits << p
- } else if (Math.abs(vB - L) <= eps && Math.abs(vB - vC) > eps) {
- hits << B
- } else if (Math.abs(vC - L) <= eps && Math.abs(vB - vC) > eps) {
- hits << C
- }
- // CA
- if ((L > Math.min(vC, vA) - eps) && (L < Math.max(vC, vA) + eps) && Math.abs(vC - vA) > eps) {
- def p = interpPoint(C, A, vC, vA, L); if (p != null) hits << p
- } else if (Math.abs(vC - L) <= eps && Math.abs(vC - vA) > eps) {
- hits << C
- } else if (Math.abs(vA - L) <= eps && Math.abs(vC - vA) > eps) {
- hits << A
- }
-
- // de-dup small numeric jitter
- def uniq = []
- hits.each { h ->
- if (h == null) return
- def kx = Math.rint(h.x * 1e6) / 1e6
- def ky = Math.rint(h.y * 1e6) / 1e6
- if (!uniq.any { Math.abs(it.x - kx) < 1e-9 && Math.abs(it.y - ky) < 1e-9 }) {
- uniq << new Coordinate(kx, ky)
- }
- }
-
- if (uniq.size() == 2) {
- LineString ls = gf.createLineString([uniq[0], uniq[1]] as Coordinate[])
- ps.addBatch(periodVal, (L as Double), ls)
- }
- } // levels
- } // triangles
- } // batch
- } // periods
-
- // -------------------
- // Stitch segments per (PERIOD, LEVEL) and insert to output
- // (without using GROUP BY directly to avoid dialect quirks)
- // -------------------
- List groups = []
- sql.eachRow("SELECT DISTINCT \"PERIOD\", \"LEVEL\" FROM \"" + segTable + "\"") { g ->
- groups.add([p: g.PERIOD, l: (g.LEVEL as Double)])
- }
-
- String insMerged =
- "INSERT INTO \"" + outTable + "\"(\"PERIOD\", \"LEVEL\", \"THE_GEOM\") " +
- "SELECT ?, ?, ST_LineMerge(ST_Union(\"THE_GEOM\")) " +
- "FROM \"" + segTable + "\" " +
- "WHERE ((\"PERIOD\" IS NULL AND ? IS NULL) OR \"PERIOD\" = ?) " +
- "AND \"LEVEL\" = ?"
-
- groups.each { g ->
- sql.execute(insMerged, [g.p, g.l, g.p, g.p, g.l])
- }
-
- // -------------------
- // Cleanup
- // -------------------
- sql.execute("DROP TABLE IF EXISTS \"" + segTable + "\"")
-
- // -------------------
- // Create a layer for each PERIOD from the output
- // -------------------
- periods.findAll { it != null }.each { p ->
- // Normalize table name: uppercase and only [A-Z0-9_]
- String periodSafe = p.toString().toUpperCase().replaceAll('[^A-Z0-9_]', '_')
- String perTable = "L" + periodSafe + "_ISOLINES_NOISE_MAP"
-
- // Drop + create destination table (without placeholders in DDL)
- sql.execute("DROP TABLE IF EXISTS \"" + perTable + "\"")
- sql.execute("""CREATE TABLE "${perTable}"(
- "PERIOD" VARCHAR,
- "LEVEL" DOUBLE,
- "THE_GEOM" GEOMETRY
- )""")
-
- // Insert only the rows of that PERIOD (here we can build the WHERE as a string)
- String val = p.replace("'", "''") // escapar comillas simples
- sql.execute("""INSERT INTO "${perTable}"("PERIOD","LEVEL","THE_GEOM")
- SELECT "PERIOD","LEVEL","THE_GEOM"
- FROM "${outTable}"
- WHERE "PERIOD" = '${val}'""")
-
- // Spatial index
- sql.execute("CREATE SPATIAL INDEX ON \"" + perTable + "\"(\"THE_GEOM\")")
- }
-
- // -------------------
- // Print results
- // -------------------
- List periodTables = periods.findAll { it != null }
- .collect { "L" + it.toString().toUpperCase().replaceAll("[^A-Z0-9_]", "_") + "_ISOLINES_NOISE_MAP" }
-
- if (periodTables.isEmpty()) {
- resultString = "Isolines created in " + outTable + " for " + levels.size() + " levels. No PERIOD values found, so no per-period tables were created."
- } else {
- resultString = "Isolines created in " + outTable + " for " + levels.size() + " levels and " +
- periodTables.size() + " periods: " + periodTables.join(", ")
- }
-
- logger.info('Result : ' + resultString)
- logger.info('End : Create Isolines')
-
- // send resultString to WPS Builder
- return resultString
-}
-
+/**
+ * NoiseModelling is an open-source tool designed to produce environmental noise maps on very large urban areas. It can be used as a Java library or be controlled through a user friendly web interface.
+ *
+ * This version is developed by the DECIDE team from the Lab-STICC (CNRS) and by the Mixt Research Unit in Environmental Acoustics (Université Gustave Eiffel).
+ *
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Contact: contact@noise-planet.org
+ *
+ */
+
+/**
+ * @Author Adapted from NM WPS scripts
+ * @Author Ignacio Soto Molina, Ministry for Ecological Transition (MITECO), Spain
+ */
+
+package org.noise_planet.noisemodelling.scripts.Acoustic_Tools
+
+import groovy.sql.Sql
+import org.h2gis.utilities.GeometryTableUtilities
+import org.h2gis.utilities.TableLocation
+import org.h2gis.utilities.wrapper.ConnectionWrapper
+import org.locationtech.jts.geom.*
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.sql.Connection
+import java.sql.SQLException
+
+// ----------------
+// Create Isolines (Isophones) from TRIANGLES + RECEIVERS_LEVEL
+// ----------------
+
+title = 'Create Isolines'
+
+description = 'Generate isolines (isophones) by linear interpolation on triangle edges (marching-triangles).' +
+ ' One multilines map per PERIOD and per LEVEL is created.' +
+ ' Main output table : ISOLINES_NOISE_MAP ' +
+ 'with : ' +
+ '- PERIOD : receivers period label (VARCHAR). ' +
+ '- LEVEL : isoline value (DOUBLE). ' +
+ '- THE_GEOM : MULTILINESTRING/LINESTRING geometry.' +
+ ' Additional output tables : one table per PERIOD, ' +
+ 'named L<PERIOD>_ISOLINES_NOISE_MAP, ' +
+ 'containing only the isolines of that PERIOD (same structure as above).'
+
+inputs = [
+ trianglesTable : [
+ name : 'Triangles table',
+ title : 'Triangles table',
+ description: 'Name of the triangles table. ' +
+ 'Shall contain : PK, THE_GEOM, PK_1, PK_2, PK_3, CELL_ID.',
+ type : String.class
+ ],
+ receiversTable : [
+ name : 'Receivers level table',
+ title : 'Receivers level table',
+ description: 'Name of the receivers level table. ' +
+ 'Shall contain : IDRECEIVER, PERIOD, THE_GEOM, LAEQ (or any field to contour).',
+ type : String.class
+ ],
+ fieldName : [
+ name : 'Field to contour',
+ title : 'Field to contour',
+ description: 'Receivers numeric field to contour (e.g. LAEQ).',
+ default : 'LAEQ',
+ type : String.class
+ ],
+ isoClasses : [
+ name : 'Iso levels (dB)',
+ title : 'Iso levels (dB)',
+ description: 'Comma-separated levels.',
+ default : '35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0',
+ type : String.class
+ ]
+]
+
+outputs = [
+ result: [
+ name: 'Result output string',
+ title: 'Result output string',
+ description: 'This type of result does not allow the blocks to be linked together.',
+ type: String.class
+ ]
+]
+
+// -------------------
+// Main function (same structure as Template.exec)
+// -------------------
+def exec(Connection connection, Map input) {
+
+ // Need to change the ConnectionWrapper to WpsConnectionWrapper to work under postGIS database
+ connection = new ConnectionWrapper(connection)
+
+ // Create a sql connection to interact with the database in SQL
+ Sql sql = new Sql(connection)
+
+ // Logger
+ Logger logger = LoggerFactory.getLogger("org.noise_planet.noisemodelling")
+
+ // print to command window
+ logger.info('Start : Create Isolines')
+ logger.info("inputs {}", input)
+
+ // -------------------
+ // Get inputs
+ // -------------------
+ String trianglesTable = (input['trianglesTable'] ?: 'TRIANGLES') as String
+ String receiversTable = (input['receiversTable'] ?: 'RECEIVERS_LEVEL') as String
+ String fieldName = (input['fieldName'] ?: 'LAEQ') as String
+ String isoClassesStr = (input['isoClasses'] ?: '35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0') as String
+
+ // do it case-insensitive for table names
+ trianglesTable = trianglesTable.toUpperCase()
+ receiversTable = receiversTable.toUpperCase()
+
+ // Parse levels (sorted)
+ List levels = isoClassesStr.split(',')
+ .collect { it.trim() }
+ .findAll { it }
+ .collect { it as Double }
+ .sort()
+
+ // -------------------
+ // Checks
+ // -------------------
+ def triExists = org.h2gis.utilities.JDBCUtilities.tableExists(connection, trianglesTable)
+ def recExists = org.h2gis.utilities.JDBCUtilities.tableExists(connection, receiversTable)
+ if (!triExists) throw new SQLException(String.format("The table %s does not exist.", trianglesTable))
+ if (!recExists) throw new SQLException(String.format("The table %s does not exist.", receiversTable))
+
+ // SRID (receivers first, then triangles)
+ int srid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(receiversTable))
+ if (srid == 0) srid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(trianglesTable))
+ if (srid == 0) throw new SQLException("Unable to determine SRID of geometries (receivers/triangles).")
+
+ // Validate field existence in receivers (case-insensitive)
+ def cntRow = sql.firstRow(
+ "SELECT COUNT(*) AS CNT FROM INFORMATION_SCHEMA.COLUMNS WHERE UPPER(TABLE_NAME)=? AND UPPER(COLUMN_NAME)=?",
+ [receiversTable.toUpperCase(), fieldName.toUpperCase()]
+ )
+ if ((cntRow?.CNT ?: 0) == 0) {
+ throw new SQLException(String.format("Field '%s' does not exist in %s", fieldName, receiversTable))
+ }
+
+ // -------------------
+ // Create output & temporaries (NO placeholders in DDL)
+ // -------------------
+ String outTable = "ISOLINES_NOISE_MAP"
+ String segTable = "TMP_ISO_SEGMENTS"
+ String resultString
+
+ sql.execute("DROP TABLE IF EXISTS \"" + outTable + "\"")
+ sql.execute("""CREATE TABLE "${outTable}"(
+ "PERIOD" VARCHAR,
+ "LEVEL" DOUBLE,
+ "THE_GEOM" GEOMETRY
+ )""")
+ sql.execute("CREATE SPATIAL INDEX ON \"" + outTable + "\"(\"THE_GEOM\")")
+
+ sql.execute("DROP TABLE IF EXISTS \"" + segTable + "\"")
+ sql.execute("""CREATE TABLE "${segTable}"(
+ "PERIOD" VARCHAR,
+ "LEVEL" DOUBLE,
+ "THE_GEOM" GEOMETRY
+ )""")
+ sql.execute("CREATE SPATIAL INDEX ON \"" + segTable + "\"(\"THE_GEOM\")")
+
+ // -------------------
+ // Periods list
+ // -------------------
+ List periods = []
+ sql.eachRow("SELECT DISTINCT \"PERIOD\" FROM \"" + receiversTable + "\" WHERE \"PERIOD\" IS NOT NULL") { r ->
+ periods.add(r.PERIOD?.toString())
+ }
+ if (periods.isEmpty()) periods = [null]
+
+ // -------------------
+ // Geometry machinery
+ // -------------------
+ GeometryFactory gf = new GeometryFactory(new PrecisionModel(), srid)
+ double eps = 1e-9
+
+ // Interpolate point on edge
+ def interpPoint = { Coordinate a, Coordinate b, double va, double vb, double cLevel ->
+ double dv = (vb - va)
+ if (Math.abs(dv) < eps) return null
+ double t = (cLevel - va) / dv
+ if (t < -eps || t > 1 + eps) return null
+ if (t < 0) t = 0
+ if (t > 1) t = 1
+ new Coordinate(a.x + t * (b.x - a.x), a.y + t * (b.y - a.y))
+ }
+
+ // Load triangles to memory
+ List triRows = []
+ sql.eachRow("SELECT \"PK\", \"PK_1\", \"PK_2\", \"PK_3\" FROM \"" + trianglesTable + "\"") { r ->
+ triRows.add([PK: r.PK as Integer, A: r.PK_1 as Integer, B: r.PK_2 as Integer, C: r.PK_3 as Integer])
+ }
+
+ // Insert batch for segments
+ String insSegSQL = "INSERT INTO " + segTable + "(PERIOD, LEVEL, THE_GEOM) VALUES (?, ?, ?)"
+
+ // -------------------
+ // Build segments by PERIOD & LEVEL
+ // -------------------
+ periods.each { periodVal ->
+ // Map: receiver id -> (coord, val)
+ Map recMap = [:]
+ String sqlRec = (periodVal == null) ?
+ "SELECT IDRECEIVER, THE_GEOM, " + fieldName + " AS VAL FROM " + receiversTable :
+ "SELECT IDRECEIVER, THE_GEOM, " + fieldName + " AS VAL FROM " + receiversTable + " WHERE PERIOD = ?"
+
+ if (periodVal == null) {
+ sql.eachRow(sqlRec) { r ->
+ Point pt = r.THE_GEOM as Point
+ if (pt != null) recMap[r.IDRECEIVER as Integer] = [coord: pt.coordinate, val: (r.VAL as Double)]
+ }
+ } else {
+ sql.eachRow(sqlRec, [periodVal]) { r ->
+ Point pt = r.THE_GEOM as Point
+ if (pt != null) recMap[r.IDRECEIVER as Integer] = [coord: pt.coordinate, val: (r.VAL as Double)]
+ }
+ }
+
+ if (recMap.isEmpty()) return
+
+ sql.withBatch(5000, insSegSQL) { ps ->
+ triRows.each { trow ->
+ def rA = recMap[trow.A]; def rB = recMap[trow.B]; def rC = recMap[trow.C]
+ if (rA == null || rB == null || rC == null) return
+
+ Coordinate A = rA.coord as Coordinate
+ Coordinate B = rB.coord as Coordinate
+ Coordinate C = rC.coord as Coordinate
+ double vA = (rA.val as Double)
+ double vB = (rB.val as Double)
+ double vC = (rC.val as Double)
+
+ levels.each { L ->
+ List hits = []
+
+ // AB
+ if ((L > Math.min(vA, vB) - eps) && (L < Math.max(vA, vB) + eps) && Math.abs(vA - vB) > eps) {
+ def p = interpPoint(A, B, vA, vB, L); if (p != null) hits << p
+ } else if (Math.abs(vA - L) <= eps && Math.abs(vA - vB) > eps) {
+ hits << A
+ } else if (Math.abs(vB - L) <= eps && Math.abs(vA - vB) > eps) {
+ hits << B
+ }
+ // BC
+ if ((L > Math.min(vB, vC) - eps) && (L < Math.max(vB, vC) + eps) && Math.abs(vB - vC) > eps) {
+ def p = interpPoint(B, C, vB, vC, L); if (p != null) hits << p
+ } else if (Math.abs(vB - L) <= eps && Math.abs(vB - vC) > eps) {
+ hits << B
+ } else if (Math.abs(vC - L) <= eps && Math.abs(vB - vC) > eps) {
+ hits << C
+ }
+ // CA
+ if ((L > Math.min(vC, vA) - eps) && (L < Math.max(vC, vA) + eps) && Math.abs(vC - vA) > eps) {
+ def p = interpPoint(C, A, vC, vA, L); if (p != null) hits << p
+ } else if (Math.abs(vC - L) <= eps && Math.abs(vC - vA) > eps) {
+ hits << C
+ } else if (Math.abs(vA - L) <= eps && Math.abs(vC - vA) > eps) {
+ hits << A
+ }
+
+ // de-dup small numeric jitter
+ def uniq = []
+ hits.each { h ->
+ if (h == null) return
+ def kx = Math.rint(h.x * 1e6) / 1e6
+ def ky = Math.rint(h.y * 1e6) / 1e6
+ if (!uniq.any { Math.abs(it.x - kx) < 1e-9 && Math.abs(it.y - ky) < 1e-9 }) {
+ uniq << new Coordinate(kx, ky)
+ }
+ }
+
+ if (uniq.size() == 2) {
+ LineString ls = gf.createLineString([uniq[0], uniq[1]] as Coordinate[])
+ ps.addBatch(periodVal, (L as Double), ls)
+ }
+ } // levels
+ } // triangles
+ } // batch
+ } // periods
+
+ // -------------------
+ // Stitch segments per (PERIOD, LEVEL) and insert to output
+ // (without using GROUP BY directly to avoid dialect quirks)
+ // -------------------
+ List groups = []
+ sql.eachRow("SELECT DISTINCT \"PERIOD\", \"LEVEL\" FROM \"" + segTable + "\"") { g ->
+ groups.add([p: g.PERIOD, l: (g.LEVEL as Double)])
+ }
+
+ String insMerged =
+ "INSERT INTO \"" + outTable + "\"(\"PERIOD\", \"LEVEL\", \"THE_GEOM\") " +
+ "SELECT ?, ?, ST_LineMerge(ST_Union(\"THE_GEOM\")) " +
+ "FROM \"" + segTable + "\" " +
+ "WHERE ((\"PERIOD\" IS NULL AND ? IS NULL) OR \"PERIOD\" = ?) " +
+ "AND \"LEVEL\" = ?"
+
+ groups.each { g ->
+ sql.execute(insMerged, [g.p, g.l, g.p, g.p, g.l])
+ }
+
+ // -------------------
+ // Cleanup
+ // -------------------
+ sql.execute("DROP TABLE IF EXISTS \"" + segTable + "\"")
+
+ // -------------------
+ // Create a layer for each PERIOD from the output
+ // -------------------
+ periods.findAll { it != null }.each { p ->
+ // Normalize table name: uppercase and only [A-Z0-9_]
+ String periodSafe = p.toString().toUpperCase().replaceAll('[^A-Z0-9_]', '_')
+ String perTable = "L" + periodSafe + "_ISOLINES_NOISE_MAP"
+
+ // Drop + create destination table (without placeholders in DDL)
+ sql.execute("DROP TABLE IF EXISTS \"" + perTable + "\"")
+ sql.execute("""CREATE TABLE "${perTable}"(
+ "PERIOD" VARCHAR,
+ "LEVEL" DOUBLE,
+ "THE_GEOM" GEOMETRY
+ )""")
+
+ // Insert only the rows of that PERIOD (here we can build the WHERE as a string)
+ String val = p.replace("'", "''") // escapar comillas simples
+ sql.execute("""INSERT INTO "${perTable}"("PERIOD","LEVEL","THE_GEOM")
+ SELECT "PERIOD","LEVEL","THE_GEOM"
+ FROM "${outTable}"
+ WHERE "PERIOD" = '${val}'""")
+
+ // Spatial index
+ sql.execute("CREATE SPATIAL INDEX ON \"" + perTable + "\"(\"THE_GEOM\")")
+ }
+
+ // -------------------
+ // Print results
+ // -------------------
+ List periodTables = periods.findAll { it != null }
+ .collect { "L" + it.toString().toUpperCase().replaceAll("[^A-Z0-9_]", "_") + "_ISOLINES_NOISE_MAP" }
+
+ if (periodTables.isEmpty()) {
+ resultString = "Isolines created in " + outTable + " for " + levels.size() + " levels. No PERIOD values found, so no per-period tables were created."
+ } else {
+ resultString = "Isolines created in " + outTable + " for " + levels.size() + " levels and " +
+ periodTables.size() + " periods: " + periodTables.join(", ")
+ }
+
+ logger.info('Result : ' + resultString)
+ logger.info('End : Create Isolines')
+
+ // send resultString to WPS Builder
+ return resultString
+}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isosurface.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isosurface.groovy
similarity index 62%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isosurface.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isosurface.groovy
index bb96ddb57..b14da1598 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/Create_Isosurface.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/Create_Isosurface.groovy
@@ -15,92 +15,77 @@
* @Author Nicolas Fortin, Université Gustave Eiffel
*/
+package org.noise_planet.noisemodelling.scripts.Acoustic_Tools
-package org.noise_planet.noisemodelling.wps.Acoustic_Tools
-
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
+import org.h2gis.api.ProgressVisitor
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.TableLocation
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.noise_planet.noisemodelling.jdbc.utils.IsoSurface
+import org.noise_planet.noisemodelling.pathfinder.utils.profiler.RootProgressVisitor
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
title = 'Create isosurfaces from a NoiseModelling resulting table and its associated TRIANGLES table.'
description = '➡️ Create isosurfaces from a NoiseModelling resulting table and its associated TRIANGLES table.'+
' ' +
- '🚨 The triangle table must have been created using the WPS block "Receivers/Delaunay_Grid ". ' +
+ '🚨 The triangle table must have been created using the "Receivers/Delaunay_Grid " WPS block. ' +
'✅ The output table is called CONTOURING_NOISE_MAP ' +
- ' '
+ ' '
inputs = [
resultTable : [
name : 'Sound levels table',
title : 'Sound levels table',
- description: 'Name of the sound levels table, generated from "Noise_level_from_source". (STRING) ' +
- 'Example : RECEIVERS_LEVEL.',
+ description: 'Name of the sound levels table, generated from the "Noise_level_from_source " WPS block. (STRING) ' +
+ 'Example : RECEIVERS_LEVEL',
type : String.class
],
isoClass : [
name : 'Iso levels in dB',
title : 'Iso levels in dB',
- description: 'Separation of sound levels for isosurfaces. First range is from -∞ to first value excluded. The first value included to next value excluded.. ' +
- 'Read this documentation for more information about sound levels classes. ' +
- '🛠 Default value: 35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0 ',
- min : 0, max: 1,
+ description: 'Separation of sound levels for isosurfaces. The first range is from -∞ to the first value (excluded). The next range is from the first value (included) to the next value (excluded). ' +
+ 'Read this documentation for more information about sound levels classes.',
+ default : "35.0,40.0,45.0,50.0,55.0,60.0,65.0,70.0,75.0,80.0,200.0",
type : String.class
],
resultTableField : [
name : 'Field of result table',
title : 'Field of result table',
- description: 'Field to read in the result table to make the iso surface; Default value: LAEQ',
- min : 0, max: 1,
+ description: 'Field to read in the result table to make the iso surface.',
+ default : 'LAEQ',
type : String.class
],
keepTriangles: [
name : 'Keep triangles',
title : 'Keep triangles',
description: 'Point inside areas with the same iso levels are kept so elevation variation into ' +
- 'same iso level areas will be preserved but the output data size will be higher.',
- min : 0, max: 1,
+ 'same iso level areas will be preserved but the output data size will be higher. Keeping triangles will reduce significantly the computation time.',
+ default : false,
type : Boolean.class
],
smoothCoefficient: [
name : 'Polygon smoothing coefficient',
title : 'Polygon smoothing coefficient',
- description: 'This coefficient (Bezier curve coefficient) will smooth the generated isosurfaces. '+
- 'If equal to 0, it disables the smoothing step and will keep the altitude of receivers (3D geojson can be viewed on https://kepler.gl). ' +
- '🛠 Default value: 0.5 ',
- min : 0, max: 1,
- type : Double.class
- ]
+ description: 'This coefficient (Bezier curve coefficient) will smooth the generated isosurfaces. ' +
+ 'If equal to 0, it disables the smoothing step and will keep the altitude of final polygons (3D geojson can be viewed on https://kepler.gl).' +
+ 'Use this option with keepTriangles to keep the altitude variation into same iso level areas.',
+ default : 0,
+ type : Double.class]
]
outputs = [
result: [
- name : 'Result output string',
- title : 'Result output string',
- description: 'This type of result does not allow the blocks to be linked together.',
+ name : 'Output table',
+ title : 'Output table',
+ description: 'Name of the output table containing the isosurfaces. The table is created in the same schema as the input result table. (STRING)',
type : String.class
]
]
-
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-def exec(Connection connection, Map input) {
+def exec(Connection connection, Map input, ProgressVisitor progressVisitor) {
//Need to change the ConnectionWrapper to WpsConnectionWrapper to work under postGIS database
connection = new ConnectionWrapper(connection)
@@ -121,7 +106,7 @@ def exec(Connection connection, Map input) {
isoLevels = new ArrayList<>()
StringTokenizer st = new StringTokenizer(input['isoClass'] as String, ",")
while (st.hasMoreTokens()) {
- isoLevels.add(Double.parseDouble(st.nextToken()))
+ isoLevels.add(Double.parseDouble(st.nextToken().trim()))
}
}
@@ -138,22 +123,17 @@ def exec(Connection connection, Map input) {
if(input.containsKey("keepTriangles")) {
isoSurface.setMergeTriangles(!(input['keepTriangles'] as Boolean))
- }
-
- if (input.containsKey("smoothCoefficient")) {
- double coefficient = input['smoothCoefficient'] as Double
- if (coefficient < 0.01) {
- isoSurface.setSmooth(false)
- } else {
- isoSurface.setSmooth(true)
- isoSurface.setSmoothCoefficient(coefficient)
- }
}
- else {
+ double coefficient = input.getOrDefault("smoothCoefficient", 0.0) as Double
+ if(coefficient < 0.01) {
+ isoSurface.setSmooth(false)
+ } else {
isoSurface.setSmooth(true)
- isoSurface.setSmoothCoefficient(0.5)
+ isoSurface.setSmoothCoefficient(coefficient)
}
+ isoSurface.setProgressVisitor(progressVisitor)
+
isoSurface.createTable(connection, "IDRECEIVER")
resultString = "Table " + isoSurface.getOutputTable() + " created"
@@ -162,20 +142,9 @@ def exec(Connection connection, Map input) {
logger.info(resultString)
// print to WPS Builder
- return resultString
+ return [result: isoSurface.getOutputTable()]
}
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
+def exec(Connection connection, Map input) {
+ return exec(connection, input, new RootProgressVisitor(1, true, 5))
}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/DynamicIndicators.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/DynamicIndicators.groovy
similarity index 71%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/DynamicIndicators.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/DynamicIndicators.groovy
index 4e203d79d..84c495e42 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Acoustic_Tools/DynamicIndicators.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Acoustic_Tools/DynamicIndicators.groovy
@@ -14,38 +14,38 @@
* @Author Pierre Aumond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Acoustic_Tools
+package org.noise_planet.noisemodelling.scripts.Acoustic_Tools
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.sql.Connection
title = 'Compute dynamic indicators'
-description = 'Compute dynamic indicators as L10, L90 The columns of the table should be named HZ63, HZ125,..., HZ8000 with an HZ prefix that can be changed.'
+description = 'Computes dynamic percentile indicators (L10, L50, L90) for each row in the table'
inputs = [
columnName : [
name : 'Column name',
title : 'Column name',
- description: 'Column name on which to perform the calculation. (STRING) For example : LEQA',
+ description: 'Column name on which to perform the calculation. (STRING) For example : LEAQ',
type : String.class
],
tableName: [
title : 'Name of the table',
name : 'Name of the table',
- description: 'Name of the table on which to perform the calculation. The table must contain multiple sound level values for a single receiver. (STRING) For example : RECEIVERS_LEVEL',
+ description: 'Name of the table on which to perform the calculation. The table must contain multiple sound level values for a single receiver. The columns of the table should be named HZ63, HZ125,..., HZ8000 with an HZ prefix that can be changed. (STRING) For example : RECEIVERS_LEVEL',
type : String.class
],
outputTableName: [
title : 'Name of the output table',
name : 'Name of the output table',
- description: 'Name of the output table default to tableName+_DYN_IND',
- min : 0, max: 1,
+ description: 'Name of the output table',
+ default : 'tableName_DYN_IND',
type : String.class,
]
]
@@ -60,15 +60,6 @@ outputs = [
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
def exec(Connection connection, Map input) {
// output string, the information given back to the user
@@ -117,17 +108,3 @@ def exec(Connection connection, Map input) {
return resultString
}
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/All_Possible_Configuration.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/All_Possible_Configuration.groovy
similarity index 77%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/All_Possible_Configuration.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/All_Possible_Configuration.groovy
index 7c30441fb..f0fd1f419 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/All_Possible_Configuration.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/All_Possible_Configuration.groovy
@@ -1,26 +1,38 @@
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * NoiseModelling is an open-source tool designed to produce environmental noise maps on very large urban areas. It can be used as a Java library or be controlled through a user friendly web interface.
+ *
+ * This version is developed by the DECIDE team from the Lab-STICC (CNRS) and by the Mixt Research Unit in Environmental Acoustics (Université Gustave Eiffel).
+ *
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Contact: contact@noise-planet.org
+ *
+ */
+
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.BatchingPreparedStatementWrapper
import groovy.sql.BatchingStatementWrapper
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
-// ----------------- WPS Metadata ------------------
-title = 'all configurations '
-description = 'process to generate all configurations.'
+
+title = 'All configurations'
+description = 'Process to generate all configurations.'
inputs = [
trafficValues: [
name: 'Traffic values',
title: 'Traffic values',
- description: 'list of variation values in % for traffic like [0.01,1.0, 2.0,3,4]',
+ description: 'List of variation values in % for traffic like [0.01,1.0, 2.0,3,4]',
type: String.class
],
temperatureValues : [
@@ -41,30 +53,6 @@ outputs = [
]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
@CompileStatic
def exec(Connection connection,input) {
@@ -98,7 +86,7 @@ def exec(Connection connection,input) {
* The generated combinations include values for type of roads primary, secondary, tertiary, others, and temperature.
*
* The total number of combinations is calculated as:
- * (number of `vals` elements) ^ (number of paramèters) * (number of `temps` elements).
+ * (number of `vals` elements) ^ (number of parameters) * (number of `temps` elements).
*
* The sql table follows the structure:
* IT, PRIMARY, SECONDARY, TERTIARY, OTHERS, TEMP.
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Create_Assimilated_Maps.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Create_Assimilated_Maps.groovy
similarity index 65%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Create_Assimilated_Maps.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Create_Assimilated_Maps.groovy
index de6b6e52d..90235bcec 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Create_Assimilated_Maps.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Create_Assimilated_Maps.groovy
@@ -10,22 +10,20 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
-import geoserver.GeoServer
-import geoserver.catalog.Store
import org.h2gis.utilities.wrapper.ConnectionWrapper
-
import java.sql.Connection
-
title = 'Creation of the result table'
-description = 'Creation of the result table.'
-
+description = 'Creates the ASSIMILATED_MAPS table by joining the best configuration table with the receivers noise levels.'
inputs = [
bestConfigTable: [
name: 'The best configuration table',
@@ -40,9 +38,9 @@ inputs = [
type: String.class
],
outputTable: [
- name: 'The output table name',
- title: 'The output table name',
- description: 'The output table name',
+ name: 'The output table name',
+ title: 'The output table name',
+ description: 'The output table name',
type: String.class
]
]
@@ -55,16 +53,6 @@ outputs = [
]
]
-// Open Connection to Geoserver
-Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
@CompileStatic
def exec(Connection connection,inputs) {
connection = new ConnectionWrapper(connection)
@@ -75,8 +63,8 @@ def exec(Connection connection,inputs) {
Sql sql = new Sql(connection)
// Add Timestamp to the NMs
sql.execute("DROP TABLE "+outputTable+" IF EXISTS;")
- sql.execute("CREATE TABLE "+outputTable+" AS SELECT b.EPOCH TIMESTAMP, a.LAEQ, a.THE_GEOM, a.IDRECEIVER FROM "+bestConfigTable+" b LEFT JOIN "+receiverLevel+" a ON a.PERIOD = b.IT ; ")
- sql.execute("ALTER TABLE "+outputTable+" ALTER COLUMN TIMESTAMP SET DATA TYPE INTEGER")
+ sql.execute("CREATE TABLE "+outputTable+" AS SELECT b.EPOCH TIMESTAMP, a.LAEQ, a.THE_GEOM, a.IDRECEIVER FROM "+bestConfigTable+" b LEFT JOIN "+receiverLevel+" a ON a.PERIOD = b.IT;")
+ sql.execute("ALTER TABLE "+outputTable+" ALTER COLUMN TIMESTAMP SET DATA TYPE INTEGER;")
def columnNames = JDBCUtilities.getColumnNames(connection, outputTable)
@@ -85,18 +73,3 @@ def exec(Connection connection,inputs) {
return "Calculation Done ! The table "+outputTable+" has been created."
}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Data_Simulation.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Data_Simulation.groovy
similarity index 86%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Data_Simulation.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Data_Simulation.groovy
index 261e78128..171d711a2 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Data_Simulation.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Data_Simulation.groovy
@@ -10,12 +10,13 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.SpatialResultSet
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.noise_planet.noisemodelling.emission.road.cnossos.RoadCnossos
@@ -24,19 +25,18 @@ import org.noise_planet.noisemodelling.pathfinder.profilebuilder.ProfileBuilder
import org.noise_planet.noisemodelling.pathfinder.utils.AcousticIndicatorsFunctions
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.PreparedStatement
import java.sql.SQLException
title = 'Data Simulation'
-description = 'Method to execute a series of operations for generate noise maps'
-
+description = 'Method for performing a series of operations to generate noise maps'
inputs = [
noiseMapLimit: [
- name: 'Number of map ',
+ name: 'Number of map',
title: 'Number of map',
- description: 'The optional parameter limits the number of maps to be generated',
+ description: 'The optional parameter between 1 and 100 corresponding to the percentage of number of maps relative to the maximal number of combinations',
+ default: '100',
type: Integer.class
]
]
@@ -45,7 +45,7 @@ outputs = [
result: [
name: 'Noise map table ',
title: 'Noise map table',
- description: 'NOISE_MAPS table input',
+ description: 'LW_ROADS and ROADS_GEOM tables output',
type: String.class
]
]
@@ -80,7 +80,8 @@ def exec(Connection connection,input) {
"HZ63 double precision, HZ125 double precision, HZ250 double precision, HZ500 double precision, HZ1000 double precision, HZ2000 double precision, HZ4000 double precision, HZ8000 double precision);")
int size
if (limit != null){
- size = limit
+
+ size = (int) ((limit * allCombinations.size()) / 100)
}
else{
size = allCombinations.size()
@@ -205,32 +206,6 @@ def exec(Connection connection,input) {
}
logger.info('End data simulation ')
- return "Calculation Done ! The table LW_ROADS has been created."
-
-}
+ return "Calculation Done ! The table LW_ROADS and ROADS_GEOM has been created."
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection,input)]
- }
-}
-
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Extract_Best_Configuration.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Extract_Best_Configuration.groovy
similarity index 84%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Extract_Best_Configuration.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Extract_Best_Configuration.groovy
index c1e938fc3..c78388c6b 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Extract_Best_Configuration.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Extract_Best_Configuration.groovy
@@ -10,42 +10,41 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.SpatialResultSet
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.PreparedStatement
title = 'Extraction of the best configurations'
description = 'Extraction of the best maps, i.e. those that minimise the difference between the measured and simulated values, by calculating the minimum median values. '
-
inputs = [
observationTable: [
name: 'Sensors measurement training table',
title: 'Measurement table',
- description: 'table of observationSensor containing the training data Set',
+ description: 'Table of "observationSensor" containing the training data Set',
type: String.class
],
noiseMapTable: [
name: 'Noise map table',
title: 'Noise map table',
- description: 'table of noiseMapTable containing the noise maps after simulation',
+ description: 'Table of "noiseMapTable" containing the noise maps after simulation',
type: String.class
],
tempToleranceThreshold: [
- name: 'temperature tolerance threshold ',
- title: 'temperature tolerance threshold ',
- description: 'temperature tolerance threshold pour extraire la best configuration',
+ name: 'Temperature tolerance threshold',
+ title: 'Temperature tolerance threshold',
+ description: 'Temperature tolerance threshold used to filter and extract the best configurations',
type: Double.class
]
]
@@ -202,27 +201,3 @@ static def exec(Connection connection, input){
return "Calculation Done ! The table BEST_CONFIGURATION_FULL has been created."
}
-// run the script
-static def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Merged_Sensors_Receivers.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Merged_Sensors_Receivers.groovy
similarity index 64%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Merged_Sensors_Receivers.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Merged_Sensors_Receivers.groovy
index df5c6520c..5641771a2 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Merged_Sensors_Receivers.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Merged_Sensors_Receivers.groovy
@@ -10,21 +10,19 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
-
import java.sql.Connection
-
title = 'Merged Sensors and Receivers'
-description = 'Adding the sensors into the RECEIVERS after creating a regular grid of receivers.'
-
+description = 'Merges sensor locations into an existing RECEIVERS table previously created with a regular grid.'
inputs = [
tableReceivers: [
name: 'The receiver table',
@@ -35,7 +33,7 @@ inputs = [
tableSensors: [
name: 'The Sensors table',
title: 'The Sensors table',
- description: 'The Sensors table ',
+ description: 'The Sensors table',
type: String.class
]
]
@@ -65,27 +63,3 @@ static def exec(Connection connection,inputs) {
return "Calculation Done ! The tables " + receiverTable + " and "+tableSensors +" have been merged."
}
-// run the script
-static def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/NMs_4_BestConfigs.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/NMs_4_BestConfigs.groovy
similarity index 72%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/NMs_4_BestConfigs.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/NMs_4_BestConfigs.groovy
index edc2293a8..bca4af70b 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/NMs_4_BestConfigs.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/NMs_4_BestConfigs.groovy
@@ -10,17 +10,17 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
title = 'Dynamic Road Traffic Emission'
@@ -68,28 +68,3 @@ static def exec(Connection connection,inputs){
logger.info('End Traffic calibration')
return "Calculation Done ! The table LW_ROADS_best has been created."
}
-
-// run the script
-static def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Prepare_Sensors.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Prepare_Sensors.groovy
similarity index 86%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Prepare_Sensors.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Prepare_Sensors.groovy
index 0a2036a7d..02708a66f 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Data_Assimilation/Prepare_Sensors.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Data_Assimilation/Prepare_Sensors.groovy
@@ -1,11 +1,24 @@
-package org.noise_planet.noisemodelling.wps.Data_Assimilation
+/**
+ * NoiseModelling is an open-source tool designed to produce environmental noise maps on very large urban areas. It can be used as a Java library or be controlled through a user friendly web interface.
+ *
+ * This version is developed by the DECIDE team from the Lab-STICC (CNRS) and by the Mixt Research Unit in Environmental Acoustics (Université Gustave Eiffel).
+ *
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Contact: contact@noise-planet.org
+ *
+ */
+
+/**
+ * @Author DIAGNE Ndeye-Maguette, Université Gustave Eiffel
+ */
+
+package org.noise_planet.noisemodelling.scripts.Data_Assimilation
import com.opencsv.CSVReader
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -20,39 +33,38 @@ import java.time.format.DateTimeParseException
import java.util.concurrent.atomic.AtomicInteger
title = 'Preparation of Sensor data'
-description = 'Extraction of sensor data for a given period and creation of sql tables '
-
+description = 'Extracts sensor data for a given period and creates SQL tables'
inputs = [
startDate: [
name: 'Start Time Stamp',
title: 'Start Time Stamp',
- description: 'the start timestamp to extract the dataset in format "%Y-%m-%d %H:%M:%S" ',
+ description: 'The start timestamp to extract the dataset in format "%Y-%m-%d %H:%M:%S"',
type: String.class
],
endDate: [
name: 'End Time Stamp',
title: 'End Time Stamp',
- description: 'the end timestamp to extract the dataset in format "%Y-%m-%d %H:%M:%S" ',
+ description: 'The end timestamp to extract the dataset in format "%Y-%m-%d %H:%M:%S"',
type: String.class
],
trainingRatio: [
name: 'Training data percentage',
title: 'Training data percentage',
- description: 'Training data as a percentage of total data ',
+ description: 'Training data as a percentage of total data',
type: Float.class
],
workingFolder: [
name: 'Input folder path',
title: 'Working directory path with input files',
- description: 'Folder containing csv files "device_mapping_sf", the osm file and the folder "devices_data"',
+ description: 'Folder containing .csv files "device_mapping_sf", the .osm file and the folder "devices_data"',
type: String.class
],
targetSRID : [
name : 'Target projection identifier',
title : 'Target projection identifier',
description: '🌍 Target projection identifier (also called SRID) of your table. ' +
- 'It should be an EPSG code, an integer with 4 or 5 digits (ex: 3857 is Web Mercator projection). ' +
- '🚨 The target SRID must be in metric coordinates example 2056 for Geneva.',
+ 'It should be an EPSG code, an integer with 4 or 5 digits (ex: 3857 is Web Mercator projection). ' +
+ '🚨 The target SRID must be in metric coordinates (e.g 2056 for Geneva).',
type : Integer.class
]
]
@@ -136,31 +148,6 @@ static def exec(Connection connection,input){
return "Calculation Done ! The tables SENSORS_MEASUREMENTS, SENSORS_LOCATION and SENSORS_MEASUREMENTS_TRAINING have been created."
-
-}
-
-static def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
}
/**
@@ -210,7 +197,6 @@ static def allMeasurements(LocalDateTime dayStart, LocalDateTime dayEnd, String
}
-
/**
* Extracts sensor data from the sensor measurements files for a given period dayStart -> dayEnd
*
@@ -338,6 +324,4 @@ static def extractObservationData(Connection connection,Float ratio) {
}
}
-
-
}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Add_Primary_Key.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Add_Primary_Key.groovy
similarity index 79%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Add_Primary_Key.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Add_Primary_Key.groovy
index 262cbe259..a6818642f 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Add_Primary_Key.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Add_Primary_Key.groovy
@@ -14,22 +14,18 @@
* @Author Pierre Aumond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Database_Manager
+package org.noise_planet.noisemodelling.scripts.Database_Manager
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.ResultSet
import java.sql.Statement
title = 'Add primary key column or constraint'
-description = '➡️ Add a Primary Key (🔑) column or add a Primary Key constraint to a column of a table. ' +
+description = '➡️ Adds a Primary Key (🔑) column, or adds a Primary Key constraint to an existing column. ' +
' ' +
'It is necessary to add a Primary Key on one of the columns for the source and receiver tables before doing a calculation. ' +
'💡 If the table already has a Primary Key, it will remove the constraint before the operation.'
@@ -38,7 +34,7 @@ inputs = [
pkName: [
name: 'Name of the column',
title: 'Name of the column',
- description: 'Name of the column to be added, or for which the main key constraint will be added. '+
+ description: 'Name of the column to be added, or for which the main key constraint will be added. '+
'💡 Primary keys must contain UNIQUE values, and cannot contain NULL values',
type: String.class
],
@@ -59,16 +55,6 @@ outputs = [
]
]
-
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
def exec(Connection connection, input) {
// output string, the information given back to the user
@@ -124,17 +110,3 @@ def exec(Connection connection, input) {
return resultString
}
-
-// run the script
-def run(input) {
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Clean_Database.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Clean_Database.groovy
similarity index 76%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Clean_Database.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Clean_Database.groovy
index acb154f72..acfc11493 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Clean_Database.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Clean_Database.groovy
@@ -15,26 +15,22 @@
* @Author Nicolas Fortin, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Database_Manager
+package org.noise_planet.noisemodelling.scripts.Database_Manager
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.Statement
title = 'Delete all database tables'
-description = '➡️ Delete all non-system tables of the database. '+
+description = '➡️ Delete all non-system tables of the database. '+
'🚨 Use with caution'
inputs = [
areYouSure: [
- name: 'Are you sure ?',
+ name: 'Are you sure?',
title: 'Are you sure?',
description: 'Are you sure you want to delete all the tables in the database?',
type: Boolean.class
@@ -50,15 +46,6 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
def exec(Connection connection, input) {
// output string, the information given back to the user
@@ -112,17 +99,3 @@ def exec(Connection connection, input) {
return resultString
}
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Display_Database.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Display_Database.groovy
similarity index 62%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Display_Database.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Display_Database.groovy
index 0cf4f77c6..6801e07db 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Display_Database.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Display_Database.groovy
@@ -14,33 +14,32 @@
* @Author Nicolas Fortin, Université Gustave Eiffel
*/
+package org.noise_planet.noisemodelling.scripts.Database_Manager
-package org.noise_planet.noisemodelling.wps.Database_Manager
-
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
+import org.h2gis.api.ProgressVisitor
+import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
+import org.h2gis.utilities.dbtypes.DBTypes
+import org.h2gis.utilities.dbtypes.DBUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
title = 'Display the list of tables (and their attributes).'
description = '➡️ Displays the list of tables that are in the database. ' +
- ' ' +
- 'Optionally it is also possible to display their attributes ("showColumns" parameter). ' +
- '💡 To visualize the content of (a part of) a table, you can use "Table Visualization Data" script.'
+ ' ' +
+ 'Optionally it is also possible to display their attributes ("showColumns" parameter). ' +
+ '💡 To visualize the content of (a part of) a table, you can use "Table Visualization Data" script.'
inputs = [
showColumns: [
name : 'Display columns of the tables',
title : 'Display columns of the tables',
- description: 'Do you want to display also the column of the tables ? ' +
+ description: 'Would you also like to display the column name in the tables?' +
'💡 Note : A small yellow key symbol (🔑) will appear if the column as a Primary Key constraint.',
- type : Boolean.class,
- min : 0, max: 1
+ default : true,
+ type : Boolean.class
]
]
@@ -53,17 +52,7 @@ outputs = [
]
]
-
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-def exec(Connection connection, input) {
+def exec(Connection connection, Map input) {
// output string, the information given back to the user
String resultString = null
@@ -75,11 +64,7 @@ def exec(Connection connection, input) {
logger.info('Start : Display database')
logger.info("inputs {}", input) // log inputs of the run
- Boolean showColumnName = false
-
- if(input['showColumns']) {
- showColumnName = input['showColumns'].toBoolean()
- }
+ Boolean showColumnName = input['showColumns'].toBoolean()
// list of the system tables
List ignorelst = ["SPATIAL_REF_SYS", "GEOMETRY_COLUMNS"]
@@ -89,20 +74,31 @@ def exec(Connection connection, input) {
// Get every table names
List tables = JDBCUtilities.getTableNames(connection, null, "PUBLIC", "%", null)
+ DBTypes dbType = DBUtils.getDBType(connection)
+
// Loop over the tables
tables.each { t ->
- TableLocation tab = TableLocation.parse(t)
+ TableLocation tab = TableLocation.parse(t, dbType)
if (!ignorelst.contains(tab.getTable())) {
sb.append(tab.getTable())
sb.append("")
if (showColumnName) {
List fields = JDBCUtilities.getColumnNames(connection, t)
+ def geometryColumnNames = GeometryTableUtilities.getGeometryColumnNames(connection, tab)
Integer keyColumnIndex = JDBCUtilities.getIntegerPrimaryKey(connection, tab)
int columnIndex = 1;
fields.each {
f ->
if (columnIndex == keyColumnIndex) {
sb.append(String.format(" %s 🔑", f))
+ } else if(geometryColumnNames.contains(f)) {
+ int epsg = 0;
+ try {
+ epsg = GeometryTableUtilities.getSRID(connection, tab)
+ } catch (Exception ex) {
+ //ignore
+ }
+ sb.append(String.format(" %s 🌐 (srid: %d)", f, epsg))
} else {
sb.append(String.format(" %s", f))
}
@@ -113,23 +109,16 @@ def exec(Connection connection, input) {
}
}
+ if(sb.length() == 0) {
+ sb.append('')
+ sb.append('
🗄 Database is Empty ')
+ sb.append('
No tables found in the database.
')
+ sb.append('
Please import data using **Import_File** to get started.
')
+ sb.append('
')
+ }
// print to command window
logger.info('End : Display database')
// print to WPS Builder
return sb.toString()
}
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Drop_a_Table.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Drop_a_Table.groovy
similarity index 78%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Drop_a_Table.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Drop_a_Table.groovy
index b469c98ee..611ab0ff0 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Drop_a_Table.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Drop_a_Table.groovy
@@ -15,16 +15,12 @@
* @Author Nicolas Fortin, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Database_Manager
+package org.noise_planet.noisemodelling.scripts.Database_Manager
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.Statement
@@ -50,14 +46,7 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
+
def exec(Connection connection, input) {
// output string, the information given back to the user
@@ -112,18 +101,3 @@ def exec(Connection connection, input) {
// print to WPS Builder
return resultString
}
-
-
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
\ No newline at end of file
diff --git a/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Data.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Data.groovy
new file mode 100644
index 000000000..4ea59e16d
--- /dev/null
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Data.groovy
@@ -0,0 +1,217 @@
+/**
+ * NoiseModelling is an open-source tool designed to produce environmental noise maps on very large urban areas. It can be used as a Java library or be controlled through a user friendly web interface.
+ *
+ * This version is developed by the DECIDE team from the Lab-STICC (CNRS) and by the Mixt Research Unit in Environmental Acoustics (Université Gustave Eiffel).
+ *
+ *
+ * NoiseModelling is distributed under GPL 3 license. You can read a copy of this License in the file LICENCE provided with this software.
+ *
+ * Contact: contact@noise-planet.org
+ *
+ */
+
+/**
+ * @Author Pierre Aumond, Université Gustave Eiffel
+ * @Author Nicolas Fortin, Université Gustave Eiffel
+ */
+
+
+package org.noise_planet.noisemodelling.scripts.Database_Manager
+
+import groovy.sql.Sql
+import org.h2gis.utilities.JDBCUtilities
+import org.h2gis.utilities.GeometryTableUtilities
+import org.h2gis.utilities.TableLocation
+import org.locationtech.jts.geom.Geometry
+import org.locationtech.jts.io.WKTWriter
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import java.sql.Connection
+import java.sql.Statement
+
+
+title = 'Display first rows of a query result.'
+description = '➡️ Display the content of a SQL query result. ' +
+ ' ' +
+ 'You can provide either a table name or a complete SELECT SQL query. ' +
+ 'Using "linesNumber" parameter, you can choose the number of lines to display ' +
+ '🚨 Be careful, this treatment can be very long if the query returns many rows.'
+
+inputs = [linesNumber: [name : 'Number of rows',
+ title : 'Number of rows',
+ description: 'Number of rows you want to display. This parameter is ignored if your SQL query already contains a LIMIT clause.',
+ default : 10,
+ type : Integer.class],
+ tableName : [name : 'Table name',
+ title : 'Table name',
+ description: 'Table name or SQL SELECT query (e.g., mytable or SELECT * FROM mytable)',
+ type : String.class]]
+
+outputs = [
+ result: [
+ name : 'Result output string',
+ title : 'Result output string',
+ description: 'This type of result does not allow the blocks to be linked together.',
+ type : String.class
+ ]
+]
+
+def exec(Connection connection, input) {
+
+ // Create a logger to display messages in the geoserver logs and in the command prompt.
+ Logger logger = LoggerFactory.getLogger("org.noise_planet.noisemodelling")
+
+ // print to command window
+ logger.info('Start : Display first rows of a query result')
+ logger.info("inputs {}", input) // log inputs of the run
+
+ // Get the number of rows the user want to display
+ int linesNumber = 10
+ if (input['linesNumber']) {
+ linesNumber = input['linesNumber'] as Integer
+ }
+
+ // Get SQL query or table name
+ String sqlQuery = input["tableName"] as String
+
+ // Create a connection statement to interact with the database in SQL
+ Sql sql = new Sql(connection)
+
+ List output
+ String finalQuery
+ boolean isTableName = !sqlQuery.toUpperCase().trim().startsWith("SELECT ")
+
+ if (isTableName) {
+ // If the input is a table name, create a SELECT query
+ Statement statement = connection.createStatement()
+ finalQuery = String.format("SELECT * FROM %s", statement.enquoteIdentifier(sqlQuery, false))
+ } else {
+ // If the input is already a SQL query, use it as is
+ // Additional validation: prevent common SQL injection patterns
+ def upperQuery = sqlQuery.toUpperCase()
+ if (upperQuery.contains(" DROP ") || upperQuery.contains(" DELETE ") ||
+ upperQuery.contains(" UPDATE ") || upperQuery.contains(" INSERT ") ||
+ upperQuery.contains(" ALTER ") || upperQuery.contains(" CREATE ") ||
+ upperQuery.contains(" TRUNCATE ")) {
+ throw new IllegalArgumentException("Query contains forbidden SQL keywords")
+ }
+ finalQuery = sqlQuery
+ }
+
+ // Add LIMIT clause if not already present
+ if (!finalQuery.toUpperCase().contains("LIMIT")) {
+ finalQuery += String.format(" LIMIT %s", linesNumber.toString())
+ }
+
+ output = sql.rows(finalQuery)
+
+ // Check if the query returned any result
+ if (output.isEmpty()) {
+ logger.info("The query did not return any result.")
+ return "The query did not return any result."
+ }
+
+ logger.info('End : Display first rows of a query result')
+
+
+ // print to WPS Builder
+ return mapToTable(output, sql, sqlQuery, connection, isTableName)
+}
+
+
+/**
+ * Convert a list to HTML table
+ * @param list
+ * @param isTableName true if the query was a simple table name, false if it was a custom SQL query
+ * @return
+ */
+static String mapToTable(List list, Sql sql, String queryOrTableName, Connection connection, boolean isTableName) {
+
+ StringBuilder output = new StringBuilder()
+
+ Map first = list.first()
+
+ if (isTableName) {
+ // Only show total count and metadata for table names
+ try {
+ output.append("The total number of rows is " + sql.firstRow('SELECT COUNT(*) FROM ' + queryOrTableName.toUpperCase())[0])
+ } catch (Exception e) {
+ output.append("Unable to determine total row count for this query")
+ }
+
+ //get SRID of the table
+ try {
+ int srid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(queryOrTableName))
+
+ if (srid > 0) {
+ output.append("")
+ output.append("The srid of the table is " + srid)
+ } else {
+ output.append("")
+ output.append("This table doesn't have any srid")
+ }
+ } catch (Exception e) {
+ output.append("")
+ output.append("Unable to determine SRID information")
+ }
+
+ //get primary key of the table
+ try {
+ int pkIndex = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(queryOrTableName))
+
+ if (pkIndex > 0) {
+ output.append("")
+ output.append("The table has the following primary key : " + JDBCUtilities.getColumnName(connection, queryOrTableName, pkIndex))
+ } else {
+ output.append("")
+ output.append("This table does not have primary key.")
+ }
+ } catch (Exception e) {
+ output.append("")
+ output.append("Unable to determine primary key information")
+ }
+
+ output.append(" ")
+ } else {
+ output.append("SQL Query: " + queryOrTableName + "")
+ output.append("Showing first " + list.size() + " rows ")
+ }
+
+ // Add CSS styling for table cells with scroll support
+ output.append("")
+
+ output.append("")
+
+ first.each { key, val ->
+ output.append("${key} ")
+ }
+
+ output.append(" ")
+ WKTWriter wktWriter = new WKTWriter(3)
+ list.each { map ->
+ if (map.size() > 0) {
+
+ def values = map.values()
+
+ output.append("")
+
+ values.each {
+ def val = it
+ if (it instanceof Geometry) {
+ val = wktWriter.write(it)
+ }
+ output.append "${val}
"
+ }
+
+ output.append(" ")
+ }
+ }
+ output.append("
")
+
+ output.toString()
+}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Table_Visualization_Map.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Map.groovy
similarity index 65%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Table_Visualization_Map.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Map.groovy
index 3d40c9bbd..a377fb48d 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Database_Manager/Table_Visualization_Map.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Database_Manager/Table_Visualization_Map.groovy
@@ -16,30 +16,27 @@
*/
-package org.noise_planet.noisemodelling.wps.Database_Manager
+package org.noise_planet.noisemodelling.scripts.Database_Manager
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
-import org.h2gis.utilities.GeometryMetaData
import org.h2gis.utilities.GeometryTableUtilities
-import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
+import org.h2gis.utilities.dbtypes.DBTypes
import org.h2gis.utilities.dbtypes.DBUtils
import org.locationtech.jts.geom.Geometry
import org.locationtech.jts.io.WKTWriter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.ResultSet
import java.sql.Statement
-title = 'Diplay a table on a map.'
+title = 'Display a table on a map.'
description = '➡️ Display a table containing a geometric column on a map 🗺 '+
- ' ' +
- 'Technically, it groups all the geometries of a table and returns them in WKT OGC format. '+
- '🚨 Be careful, this treatment can be blocked if the table is too large.'
+ ' ' +
+ 'Technically, it groups all the geometries of a table and returns them in WKT OGC format. '+
+ '🚨 Be careful, this treatment can be blocked if the table is too large.'
+
+executionTimeout = 120 // For synchronous WPS, it will wait this time before returning a message, but it will still run the execution in the background
inputs = [
inputSRID: [
@@ -47,8 +44,7 @@ inputs = [
title : 'Projection identifier',
description: '🌍 Original projection identifier (also called SRID) of your table. It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection). (INTEGER) ' +
'All coordinates will be projected from the specified EPSG to WGS84 coordinates. ' +
- 'This entry is optional because many formats already include the projection and you can also import files without geometry attributes. ' +
- '🛠 Default value: 4326 ',
+ 'This entry is optional because many formats already include the projection and you can also import files without geometry attributes.',
type : Integer.class,
min : 0, max: 1
],
@@ -69,16 +65,7 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-def exec(Connection connection, input) {
+def exec(Connection connection, Map input) {
// output geometry, the information given back to the user
Geometry geom = null
@@ -104,9 +91,10 @@ def exec(Connection connection, input) {
if (input['inputSRID']) {
srid = input['inputSRID'] as Integer
}
+ DBTypes dbType = DBUtils.getDBType(connection)
// Read Geometry Index and type of the table
- List spatialFieldNames = GeometryTableUtilities.getGeometryColumnNames(connection, TableLocation.parse(tableName, DBUtils.getDBType(connection)))
+ List spatialFieldNames = GeometryTableUtilities.getGeometryColumnNames(connection, TableLocation.parse(tableName, dbType))
// If the table does not contain a geometry field
if (spatialFieldNames.isEmpty()) {
@@ -114,7 +102,7 @@ def exec(Connection connection, input) {
}
// Get the SRID of the table
- Integer tableSrid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(tableName))
+ Integer tableSrid = GeometryTableUtilities.getSRID(connection, TableLocation.parse(tableName, dbType))
if (tableSrid != 0 && tableSrid != srid && input['inputSRID']) throw new Exception("The table already has a different SRID than the one you gave.")
@@ -124,15 +112,8 @@ def exec(Connection connection, input) {
// Display the actual SRID in the command window
logger.info("The actual SRID of the table is " + srid)
- if (tableSrid == 0) {
- GeometryMetaData metaData = GeometryTableUtilities.getMetaData(connection, TableLocation.parse(tableName, DBUtils.getDBType(connection)), spatialFieldNames.get(0));
- metaData.setSRID(srid);
- connection.createStatement().execute(String.format("ALTER TABLE %s ALTER COLUMN %s %s USING ST_SetSRID(%s,%d)",
- TableLocation.parse(tableName, DBUtils.getDBType(connection)), spatialFieldNames.get(0), metaData.getSQL(), spatialFieldNames.get(0),spatialFieldNames.get(0) ,srid))
- }
-
// Project geometry in WGS84 (EPSG:4326) and groups all the geometries of the table
- String geomField = "ST_ACCUM(ST_TRANSFORM(" + spatialFieldNames.get(0) + " ,4326))"
+ String geomField = "ST_ACCUM(ST_TRANSFORM(" + TableLocation.quoteIdentifier(spatialFieldNames.get(0)) + " ,4326))"
ResultSet rs = stmt.executeQuery(String.format("select %s " + spatialFieldNames.get(0) + " from %s", geomField, tableName))
// Get the geometry field from the table
@@ -141,10 +122,11 @@ def exec(Connection connection, input) {
}
// print to command window
- if (asWKT(geom).size() > 100) {
- logger.info('Result (100 first characters) : ' + asWKT(geom).substring(0, 100) + '...')
+ String wkt = asWKT(geom)
+ if (wkt.size() > 100) {
+ logger.info('Result (100 first characters) : ' + wkt.substring(0, 100) + '...')
} else {
- logger.info('Result : ' + asWKT(geom))
+ logger.info('Result : ' + wkt)
}
logger.info('End : Display a table on a map')
@@ -154,20 +136,6 @@ def exec(Connection connection, input) {
}
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
/**
* Convert a Geometry value into a Well Known Text value.
* @param geometry Geometry instance
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/NoiseModelling/Noise_level_from_traffic.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/Noise_level_from_traffic.groovy
similarity index 76%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/NoiseModelling/Noise_level_from_traffic.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/Noise_level_from_traffic.groovy
index fb1a8d0bf..685b1bb67 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/NoiseModelling/Noise_level_from_traffic.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/Noise_level_from_traffic.groovy
@@ -14,12 +14,11 @@
* @Author Pierre Aumond, Université Gustave Eiffel
* @Author Nicolas Fortin, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.NoiseModelling
+package org.noise_planet.noisemodelling.scripts.Deprecated
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+import org.h2gis.api.EmptyProgressVisitor
+import org.h2gis.api.ProgressVisitor
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
@@ -29,7 +28,7 @@ import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.noise_planet.noisemodelling.jdbc.NoiseMapByReceiverMaker
import org.noise_planet.noisemodelling.jdbc.NoiseMapDatabaseParameters
import org.noise_planet.noisemodelling.jdbc.input.DefaultTableLoader
-import org.noise_planet.noisemodelling.pathfinder.utils.profiler.RootProgressVisitor
+import org.noise_planet.noisemodelling.jdbc.utils.DataBaseUtilities
import org.noise_planet.noisemodelling.propagation.AttenuationParameters
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -55,9 +54,9 @@ inputs = [
name : 'Buildings table name',
title : 'Buildings table name',
description: '🏠 Name of the Buildings table ' +
- 'The table must contain: ' +
- ' THE_GEOM : the 2D geometry of the building (POLYGON or MULTIPOLYGON) ' +
- ' HEIGHT : the height of the building (FLOAT) ',
+ 'The table must contain: ' +
+ ' THE_GEOM : the 2D geometry of the building (POLYGON or MULTIPOLYGON) ' +
+ ' HEIGHT : the height of the building (FLOAT) ',
type : String.class
],
tableRoads : [
@@ -73,7 +72,8 @@ inputs = [
' WBV_D WBV_E WBV_N : Hourly average motorcycles, tricycles or quads > 50 cc count (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
' LV_SPD_D LV_SPD_E LV_SPD_N : Hourly average light vehicle speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
' MV_SPD_D MV_SPD_E MV_SPD_N : Hourly average medium heavy vehicles speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
- ' HGV_SPD_D HGV_SPD_E HGV_SPD_N : Hourly average heavy duty vehicles speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
+ ' HGV_SPD_D HGV_SPD_E HGV_SPD_N : Hourly ave' +
+ 'rage heavy duty vehicles speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
' WAV_SPD_D WAV_SPD_E WAV_SPD_N : Hourly average mopeds, tricycles or quads ≤ 50 cc speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
' WBV_SPD_D WBV_SPD_E WBV_SPD_N : Hourly average motorcycles, tricycles or quads > 50 cc speed (6-18h)(18-22h)(22-6h) (DOUBLE) ' +
' PVMT : CNOSSOS road pavement identifier (ex: NL05)(default NL08) (VARCHAR) ' +
@@ -113,7 +113,8 @@ inputs = [
' SLOPE : Slope (in %) of the road section. If the field is not filled in, the LINESTRING z-values will be used to calculate the slope and the traffic direction (way field) will be force to 3 (bidirectional). (DOUBLE) ' +
' WAY : Define the way of the road section. 1 = one way road section and the traffic goes in the same way that the slope definition you have used, 2 = one way road section and the traffic goes in the inverse way that the slope definition you have used, 3 = bi-directional traffic flow, the flow is split into two components and correct half for uphill and half for downhill (INTEGER) ' +
'',
- min : 0, max: 1, type: String.class
+ min : 0, max: 1,
+ type: String.class
],
tableSourceDirectivity : [
name : 'Source directivity table name',
@@ -125,7 +126,8 @@ inputs = [
' THETA : [-90;90] Vertical angle in degree. 0° front 90° top -90° bottom (FLOAT) ' +
' PHI : [0;360] Horizontal angle in degree. 0° front 90° right (FLOAT) ' +
' LW63, LW125, LW250, LW500, LW1000, LW2000, LW4000, LW8000 : attenuation levels in dB for each octave or third octave (FLOAT) ' ,
- min : 0, max: 1, type: String.class
+ min : 0, max: 1,
+ type: String.class
],
tablePeriodAtmosphericSettings : [
name : 'Atmospheric settings table name for each time period',
@@ -140,7 +142,8 @@ inputs = [
' GDISC : choose between accept G discontinuity or not (BOOLEAN) default true ' +
' PRIME2520 : choose to use prime values to compute eq. 2.5.20 (BOOLEAN) default false ' +
'' ,
- min : 0, max: 1, type: String.class
+ min : 0, max: 1,
+ type: String.class
],
tableReceivers : [
name : 'Receivers table name',
@@ -175,163 +178,140 @@ inputs = [
paramWallAlpha : [
name : 'wallAlpha',
title : 'Wall absorption coefficient',
- description: 'Wall absorption coefficient (FLOAT) ' +
- 'This coefficient is going ' +
- ' from 0 : fully absorbent ' +
- ' to strictly less than 1 : fully reflective. ' +
+ description: 'Wall absorption coefficient [0,1] (between ``0`` : "fully reflective" and ``1`` : "fully absorbent")' +
'🛠 Default value: 0.1 ',
- min : 0, max: 1,
- type : String.class
+ min : 0, max: 1,
+ type: Double.class
],
confReflOrder : [
name : 'Order of reflexion',
title : 'Order of reflexion',
description: 'Maximum number of reflections to be taken into account (INTEGER). ' +
- '🚨 Adding 1 order of reflexion can significantly increase the processing time. ' +
- '🛠 Default value: 1 ',
- min : 0, max: 1,
- type : String.class
+ '🚨 Adding 1 order of reflexion can significantly increase the processing time',
+ default : 1,
+ type: Integer.class
],
confMaxSrcDist : [
name : 'Maximum source-receiver distance',
title : 'Maximum source-receiver distance',
description: 'Maximum distance between source and receiver (FLOAT, in meters). ' +
- '🛠 Default value: 150 ',
- min : 0, max: 1,
- type : String.class
+ ' ',
+ default : 150,
+ type: Double.class
],
confMaxReflDist : [
name : 'Maximum source-reflexion distance',
title : 'Maximum source-reflexion distance',
- description: 'Maximum reflection distance from the source (FLOAT, in meters). ' +
- '🛠 Default value: 50 ',
- min : 0, max: 1,
- type : String.class
+ description: 'Maximum search distance of walls / facades from the "Source-Receiver" segment, for the calculation of specular reflections (meters). ' +
+ ' ',
+ default : 50,
+ type: Double.class
+ ],
+ confMinWallReflDist: [
+ name : 'Ignore close reflections',
+ title : 'Ignore close reflections',
+ description: 'Optional maximum receiver-to-wall distance (meters) below which reflection cut profiles are ignored. With regard to the population’s exposure to noise, it is recommended that the contribution due to reflection off the façade wall of the building where the resident lives should be disregarded. If you have placed the receivers 0.1 m from the façades, you can set this parameter to 0.2 m. This offset is set to ensure that the contribution from the nearby wall is ignored. ' +
+ 'Use 0 to keep all reflections.',
+ default: 0,
+ type: Double.class
],
confThreadNumber : [
name : 'Thread number',
title : 'Thread number',
description: 'Number of thread to use on the computer (INTEGER). ' +
- 'To set this value, look at the number of cores you have. ' +
- 'If it is set to 0, use the maximum number of cores available. ' +
- '🛠 Default value: 0 ',
- min : 0, max: 1,
- type : String.class
+ '🛠 Default value: 0 = Automatic. Will check the number of cores and apply -1. (*e.g*: 8 cores = 7 cores will be used ',
+ default : 0,
+ type: Integer.class
],
confDiffVertical : [
name : 'Diffraction on vertical edges',
title : 'Diffraction on vertical edges',
- description: 'Compute or not the diffraction on vertical edges. Following Directive 2015/996, enable this option for rail and industrial sources only. ' +
- '🛠 Default value: false ',
- min : 0, max: 1,
+ description: 'Compute or not the diffraction on vertical edges. Following Directive 2015/996, enable this option for rail and industrial sources only',
+ default : false,
type : Boolean.class
],
confDiffHorizontal : [
name : 'Diffraction on horizontal edges',
title : 'Diffraction on horizontal edges',
- description: 'Compute or not the diffraction on horizontal edges. ' +
- '🛠 Default value: false ',
- min : 0, max: 1,
+ description: 'Compute or not the diffraction on horizontal edges',
+ default : false,
type : Boolean.class
],
confExportSourceId : [
- name : 'keep source id',
+ name : 'Keep source id',
title : 'Separate receiver level by source identifier',
- description: 'Keep source identifier in output in order to get noise contribution of each noise source. ' +
- '🛠 Default value: false ',
- min : 0, max: 1,
+ description: 'Keep source identifier in output in order to get noise contribution of each noise source. When only the source geometry is given, the attenuation between each pair of "source-receiver" points is specified (commonly referred to as the "attenuation matrix")',
+ default : false,
type: Boolean.class
],
confHumidity : [
name : 'Relative humidity',
title : 'Relative humidity',
- description: '🌧 Humidity for noise propagation. ' +
- '🛠 Default humidity value: 70 ',
- min : 0, max: 1,
- type: Double.class
+ description: '🌧 Humidity for noise propagation (%) [0,100]',
+ default : 70,
+ type : Double.class
],
confTemperature : [
name : 'Temperature',
title : 'Air temperature',
- description: '🌡 Default Air temperature in degree celsius. ' +
- '🛠 Default value: 15 ',
- min : 0, max: 1,
- type: Double.class
+ description: '🌡 Air temperature (°C)',
+ default : 15,
+ type : Double.class
],
confFavourableOccurrencesDefault: [
name : 'Probability of occurrences',
title : 'Probability of occurrences',
- description: 'Comma-delimited string containing the default probability of occurrences of favourable propagation conditions. ' +
- 'The north slice is the last array index not the first one ' +
- 'Slice width are 22.5°: (16 slices) ' +
- 'The first column 22.5° contain occurrences between 11.25 to 33.75 ° ' +
- 'The last column 360° contains occurrences between 348.75° to 360° and 0 to 11.25° ' +
- '🛠 Default value: 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ',
- min : 0, max: 1,
+ description: 'Comma-delimited string containing the probability ([0,1]) of occurrences of favourable propagation conditions. Follow the clockwise direction. The north slice is the last array index (n°16 in the schema below) not the first one ' +
+ ' ',
+ default : '0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5',
type : String.class
],
confRaysName : [
name : '',
title : 'Export scene',
description: 'Save each mnt, buildings and propagation rays into the specified table (ex:RAYS) or file URL (ex: file:///Z:/dir/map.kml) ' +
- 'You can set a table name here in order to save all the rays computed by NoiseModelling. ' +
- 'The number of rays has been limited in this script in order to avoid memory exception. ' +
- '🛠 Default value: empty (do not keep rays) ',
+ 'You can set a table name here in order to save all the rays computed by NoiseModelling. ' +
+ 'The number of rays has been limited in this script in order to avoid memory exception. ' +
+ '🛠 Default value: empty (do not keep rays) ',
min : 0, max: 1,
type: String.class
],
confMaxError : [
name : 'Max Error (dB)',
title : 'Max Error (dB)',
- description: 'Threshold for excluding negligible sound sources in calculations. Default value: 0.1 ',
- min : 0, max: 1,
+ description: 'Threshold for excluding negligible sound sources in calculations.' +
+ 'This parameter is ignored if no emission level is specified or if you set it to 0 dB. This parameter have a great impact on computation time. ',
+ default : 0.1,
type : Double.class
],
frequencyFieldPrepend : [
name : 'Frequency field name',
title : 'Frequency field name',
- description: 'Frequency field name prepend. Ex. for 1000 Hz frequency the default column name is HZ1000.' +
- '🛠 Default value: HZ ',
- min : 0, max: 1, type: String.class
+ description: 'Frequency field name prepend. Ex. for 1000 Hz frequency the default column name is HZ1000',
+ default : 'HZ',
+ type: String.class
+ ],
+ coefficientVersion : [
+ name : 'Coefficient version',
+ title : 'Coefficient version',
+ description: '🌧 Cnossos coefficient version (1 = 2015, 2 = 2020)',
+ default : 2,
+ type : Integer.class
]
]
outputs = [
result: [
- name : 'Result output string',
- title : 'Result output string',
- description: 'This type of result does not allow the blocks to be linked together.',
+ name : 'Created table',
+ title : 'Created table',
+ description: 'Name of the table containing the results of the computation. Can be used as input for another process.',
type : String.class
]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
-def exec(Connection connection, Map input) {
+def exec(Connection connection, Map input, ProgressVisitor progress) {
int maximumRaysToExport = 5000
DBTypes dbType = DBUtils.getDBType(connection.unwrap(Connection.class))
@@ -354,11 +334,10 @@ def exec(Connection connection, Map input) {
// -------------------
String sources_table_name = input['tableRoads']
- // do it case-insensitive
- sources_table_name = sources_table_name.toUpperCase()
+
// Check if srid are in metric projection.
int sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(sources_table_name))
- if (sridSources == 3785 || sridSources == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+sources_table_name+".")
+ if (!DataBaseUtilities.isSridMetric(connection, sridSources)) throw new IllegalArgumentException("Error : Please use a metric projection for "+sources_table_name+".")
if (sridSources == 0) throw new IllegalArgumentException("Error : The table "+sources_table_name+" does not have an associated SRID.")
//Get the geometry field of the source table
@@ -369,14 +348,13 @@ def exec(Connection connection, Map input) {
}
//Get the primary key field of the source table
- int pkIndex = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(sources_table_name))
+ int pkIndex = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(sources_table_name, dbType))
if (pkIndex < 1) {
throw new IllegalArgumentException(String.format("Source table %s does not contain a primary key", sourceTableIdentifier))
}
String receivers_table_name = input['tableReceivers']
- // do it case-insensitive
- receivers_table_name = receivers_table_name.toUpperCase()
+
//Get the geometry field of the receiver table
TableLocation receiverTableIdentifier = TableLocation.parse(receivers_table_name)
List geomFieldsRcv = GeometryTableUtilities.getGeometryColumnNames(connection, receiverTableIdentifier)
@@ -384,35 +362,33 @@ def exec(Connection connection, Map input) {
throw new SQLException(String.format("The table %s does not exists or does not contain a geometry field", receiverTableIdentifier))
}
// Check if srid are in metric projection and are all the same.
- int sridReceivers = GeometryTableUtilities.getSRID(connection, TableLocation.parse(receivers_table_name))
- if (sridReceivers == 3785 || sridReceivers == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+receivers_table_name+".")
+ int sridReceivers = GeometryTableUtilities.getSRID(connection, TableLocation.parse(receivers_table_name, dbType))
+ if (!DataBaseUtilities.isSridMetric(connection, sridReceivers)) throw new IllegalArgumentException("Error : Please use a metric projection for "+receivers_table_name+".")
if (sridReceivers == 0) throw new IllegalArgumentException("Error : The table "+receivers_table_name+" does not have an associated SRID.")
if (sridReceivers != sridSources) throw new IllegalArgumentException("Error : The SRID of table "+sources_table_name+" and "+receivers_table_name+" are not the same.")
//Get the primary key field of the receiver table
- int pkIndexRecv = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(receivers_table_name))
+ int pkIndexRecv = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(receivers_table_name, dbType))
if (pkIndexRecv < 1) {
throw new IllegalArgumentException(String.format("Source table %s does not contain a primary key", receiverTableIdentifier))
}
String building_table_name = input['tableBuilding']
- // do it case-insensitive
- building_table_name = building_table_name.toUpperCase()
+
// Check if srid are in metric projection and are all the same.
- int sridBuildings = GeometryTableUtilities.getSRID(connection, TableLocation.parse(building_table_name))
- if (sridBuildings == 3785 || sridReceivers == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+building_table_name+".")
+ int sridBuildings = GeometryTableUtilities.getSRID(connection, TableLocation.parse(building_table_name, dbType))
+ if (!DataBaseUtilities.isSridMetric(connection, sridBuildings)) throw new IllegalArgumentException("Error : Please use a metric projection for "+building_table_name+".")
if (sridBuildings == 0) throw new IllegalArgumentException("Error : The table "+building_table_name+" does not have an associated SRID.")
if (sridReceivers != sridBuildings) throw new IllegalArgumentException("Error : The SRID of table "+building_table_name+" and "+receivers_table_name+" are not the same.")
String dem_table_name = ""
if (input['tableDEM']) {
dem_table_name = input['tableDEM']
- // do it case-insensitive
- dem_table_name = dem_table_name.toUpperCase()
+
// Check if srid are in metric projection and are all the same.
- int sridDEM = GeometryTableUtilities.getSRID(connection, TableLocation.parse(dem_table_name))
- if (sridDEM == 3785 || sridReceivers == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+dem_table_name+".")
+ int sridDEM = GeometryTableUtilities.getSRID(connection, TableLocation.parse(dem_table_name, dbType))
+ if (!DataBaseUtilities.isSridMetric(connection, sridDEM)) throw new IllegalArgumentException("Error : Please use a metric projection for "+dem_table_name+".")
if (sridDEM == 0) throw new IllegalArgumentException("Error : The table "+dem_table_name+" does not have an associated SRID.")
if (sridDEM != sridSources) throw new IllegalArgumentException("Error : The SRID of table "+sources_table_name+" and "+dem_table_name+" are not the same.")
}
@@ -420,11 +396,10 @@ def exec(Connection connection, Map input) {
String ground_table_name = ""
if (input['tableGroundAbs']) {
ground_table_name = input['tableGroundAbs']
- // do it case-insensitive
- ground_table_name = ground_table_name.toUpperCase()
+
// Check if srid are in metric projection and are all the same.
- int sridGROUND = GeometryTableUtilities.getSRID(connection, TableLocation.parse(ground_table_name))
- if (sridGROUND == 3785 || sridReceivers == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+ground_table_name+".")
+ int sridGROUND = GeometryTableUtilities.getSRID(connection, TableLocation.parse(ground_table_name, dbType))
+ if (!DataBaseUtilities.isSridMetric(connection, sridGROUND)) throw new IllegalArgumentException("Error : Please use a metric projection for "+ground_table_name+".")
if (sridGROUND == 0) throw new IllegalArgumentException("Error : The table "+ground_table_name+" does not have an associated SRID.")
if (sridGROUND != sridSources) throw new IllegalArgumentException("Error : The SRID of table "+ground_table_name+" and "+sources_table_name+" are not the same.")
}
@@ -432,8 +407,6 @@ def exec(Connection connection, Map input) {
String tableSourceDirectivity = ""
if (input['tableSourceDirectivity']) {
tableSourceDirectivity = input['tableSourceDirectivity']
- // do it case-insensitive
- tableSourceDirectivity = tableSourceDirectivity.toUpperCase()
}
boolean recordProfile = false
@@ -441,31 +414,25 @@ def exec(Connection connection, Map input) {
recordProfile = input['confRecordProfile']
}
- int reflexion_order = 0
- if (input['confReflOrder']) {
- reflexion_order = Integer.valueOf(input['confReflOrder'] as String)
- }
+ int reflexion_order = input.getOrDefault("confReflOrder",1) as Integer
- double max_src_dist = 150
- if (input['confMaxSrcDist']) {
- max_src_dist = Double.valueOf(input['confMaxSrcDist'] as String)
- }
+ double max_src_dist = input.getOrDefault("confMaxSrcDist", 150.0) as Double
- double max_ref_dist = 50
- if (input['confMaxReflDist']) {
- max_ref_dist = Double.valueOf(input['confMaxReflDist'] as String)
- }
+ double max_ref_dist = input.getOrDefault("confMaxReflDist",50.0) as Double
- double wall_alpha = 0.1
- if (input['paramWallAlpha']) {
- wall_alpha = Double.valueOf(input['paramWallAlpha'] as String)
+ double close_receiver_reflection_wall_distance = input.getOrDefault("confMinWallReflDist", 0.0) as Double
+ if (close_receiver_reflection_wall_distance < 0) {
+ throw new IllegalArgumentException("Error : confMinWallReflDist must be greater than or equal to 0.")
}
-
- int n_thread = 0
- if (input['confThreadNumber']) {
- n_thread = Integer.valueOf(input['confThreadNumber'] as String)
+ if (close_receiver_reflection_wall_distance > 2.0) {
+ logger.warn("confMinWallReflDist is set to {} m, which is unusually high. Many reflections may be ignored.",
+ close_receiver_reflection_wall_distance)
}
+ double wall_alpha = input.getOrDefault("paramWallAlpha",0.1) as Double
+
+ int n_thread = input.getOrDefault("confThreadNumber",0) as Integer
+
boolean compute_vertical_diffraction = false
if (input['confDiffVertical']) {
compute_vertical_diffraction = input['confDiffVertical']
@@ -481,16 +448,14 @@ def exec(Connection connection, Map input) {
confExportSourceId = input['confExportSourceId']
}
- double confMaxError = 0.1
- if (input['confMaxError']) {
- confMaxError = Double.valueOf(input['confMaxError'] as String)
- }
+ double confMaxError = input.getOrDefault("confMaxError",0.1) as Double
String frequencyFieldPrepend = "HZ"
if (input['frequencyFieldPrepend']) {
frequencyFieldPrepend = input['frequencyFieldPrepend'] as String
}
+
// --------------------------------------------
// Initialize NoiseModelling propagation part
// --------------------------------------------
@@ -502,6 +467,11 @@ def exec(Connection connection, Map input) {
parameters.setMergeSources(!confExportSourceId)
parameters.exportReceiverPosition = true
+ int coefficientVersion = input.getOrDefault("coefficientVersion",2) as Integer
+ if (coefficientVersion != 2) {
+ pointNoiseMap.sceneInputSettings.setCoefficientVersion(coefficientVersion)
+ }
+
if (input['tableRoadsTraffic']) {
// Use the right default database caps according to db type
String tableRoadsTraffic = TableLocation.capsIdentifier(input['tableRoadsTraffic'] as String, dbType)
@@ -547,18 +517,18 @@ def exec(Connection connection, Map input) {
}
environmentalData.setWindRose(favOccurrences)
}
- if (input.containsKey('confHumidity')) {
- environmentalData.setHumidity(input['confHumidity'] as Double)
- }
- if (input.containsKey('confTemperature')) {
- environmentalData.setTemperature(input['confTemperature'] as Double)
- }
+ double confHumidity = input.getOrDefault("confHumidity",70.0) as Double
+ environmentalData.setHumidity(confHumidity)
+
+ double confTemperature = input.getOrDefault("confTemperature",15.0) as Double
+ environmentalData.setTemperature(confTemperature)
+
if(input.containsKey("tablePeriodAtmosphericSettings")) {
pointNoiseMap.getSceneInputSettings().setPeriodAtmosphericSettingsTableName(input.get("tablePeriodAtmosphericSettings") as String)
}
// Building height field name
- pointNoiseMap.setHeightField("HEIGHT")
+ pointNoiseMap.setHeightField(TableLocation.capsIdentifier("height", dbType))
// Import table with Snow, Forest, Grass, Pasture field polygons. Attribute G is associated with each polygon
if (ground_table_name != "") {
pointNoiseMap.setSoilTableName(ground_table_name)
@@ -570,6 +540,7 @@ def exec(Connection connection, Map input) {
pointNoiseMap.setMaximumPropagationDistance(max_src_dist)
pointNoiseMap.setMaximumReflectionDistance(max_ref_dist)
+ pointNoiseMap.setCloseReceiverReflectionWallDistance(close_receiver_reflection_wall_distance)
pointNoiseMap.setWallAbsorption(wall_alpha)
pointNoiseMap.setThreadCount(n_thread)
@@ -577,7 +548,7 @@ def exec(Connection connection, Map input) {
if(recordProfile) {
LocalDateTime now = LocalDateTime.now()
pointNoiseMap.noiseMapDatabaseParameters.CSVProfilerOutputPath = new File(String.format("profile_%d_%d_%d_%dh%d.csv",
- now.getYear(), now.getMonthValue(), now.getDayOfMonth(), now.getHour(), now.getMinute()))
+ now.getYear(), now.getMonthValue(), now.getDayOfMonth(), now.getHour(), now.getMinute()))
pointNoiseMap.noiseMapDatabaseParameters.CSVProfilerWriteInterval = 120 // delay write csv line in seconds
}
@@ -589,13 +560,13 @@ def exec(Connection connection, Map input) {
// Run Calculations
// --------------------------------------------
- // Init ProgressLogger (loading bar)
- RootProgressVisitor progressLogger = new RootProgressVisitor(1, true, 1)
-
logger.info("Start calculation... ")
- pointNoiseMap.run(connection, progressLogger)
+ pointNoiseMap.run(connection, progress)
- return "Calculation Done ! The table $pointNoiseMap.noiseMapDatabaseParameters.receiversLevelTable have been created."
+ return [result : pointNoiseMap.noiseMapDatabaseParameters.receiversLevelTable]
}
+def exec(Connection connection, Map input) {
+ return exec(connection, input, new EmptyProgressVisitor())
+}
diff --git a/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/package-info.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/package-info.groovy
new file mode 100644
index 000000000..3196ae297
--- /dev/null
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Deprecated/package-info.groovy
@@ -0,0 +1,4 @@
+/**
+ * Unused scripts that will be removed in the next major release
+ */
+package org.noise_planet.noisemodelling.scripts.Deprecated;
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Flow_2_Noisy_Vehicles.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Flow_2_Noisy_Vehicles.groovy
similarity index 89%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Flow_2_Noisy_Vehicles.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Flow_2_Noisy_Vehicles.groovy
index 2929ab150..3f422854f 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Flow_2_Noisy_Vehicles.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Flow_2_Noisy_Vehicles.groovy
@@ -15,13 +15,10 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel, Ghent University
*/
-package org.noise_planet.noisemodelling.wps.Dynamic
+package org.noise_planet.noisemodelling.scripts.Dynamic
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.time.TimeCategory
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.SpatialResultSet
@@ -30,7 +27,9 @@ import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.locationtech.jts.geom.*
import org.noise_planet.noisemodelling.emission.road.cnossosvar.RoadVehicleCnossosvar
import org.noise_planet.noisemodelling.emission.road.cnossosvar.RoadVehicleCnossosvarParameters
-
+import org.noise_planet.noisemodelling.jdbc.utils.DataBaseUtilities
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
import java.security.InvalidParameterException
import java.sql.Connection
import java.sql.ResultSet
@@ -38,17 +37,17 @@ import java.sql.SQLException
import java.util.stream.Collectors
title = 'From Road traffic flows to noisy individual vehicles'
-description = 'Calculating individual vehicle position and noise_level based on average traffic flows.' +
+description = 'Calculates individual vehicle position and noise level based on average traffic flows.' +
' A first output table is called : SOURCES_GEOM which is needed to compute the Noise Attenuation Matrix' +
'and contain : ' +
- '- IDSOURCE : an identifier (INTEGER, PRIMARY KEY). ' +
- '- ROAD_ID : id link to the road segment (INTEGER). ' +
+ '- IDSOURCE : an identifier (INTEGER, PRIMARY KEY). ' +
+ '- ROAD_ID : id link to the road segment (INTEGER). ' +
'- THE_GEOM : the 3D geometry of the sources (POINT). ' +
' The output table is called : SOURCES_EMISSION ' +
'and contain : ' +
- '- PK : an identifier (INTEGER, PRIMARY KEY). ' +
- '- IDSOURCE : link to the source point (INTEGER). ' +
- '- PERIOD : The TIMESTAMP iteration (VARCHAR).' +
+ '- PK : an identifier (INTEGER, PRIMARY KEY). ' +
+ '- IDSOURCE : link to the source point (INTEGER). ' +
+ '- PERIOD : The TIMESTAMP iteration (VARCHAR).' +
'- HZ63, HZ125, HZ250, HZ500, HZ1000,HZ2000, HZ4000, HZ8000 : 8 columns giving the instantaneous emission sound level for each octave band (FLOAT).'
inputs = [
@@ -67,61 +66,41 @@ inputs = [
description : "Two methods are available : " +
" - PROBA : Probabilistic representation of vehicle appearances for each time step (quicker, but sacrifices temporal coherence) Aumond, P., Jacquesson, L., & Can, A. (2018). Probabilistic modeling framework for multisource sound mapping. Applied Acoustics, 139, 34-43. ." +
" - TNP : Simplified vehicle movements (slower, but maintaining temporal coherence) De Coensel, B.; Brown, A.L.; Tomerini, D. A road traffic noise pattern simulation model that includes distributions of vehicle sound power levels. Appl. Acoust. 2016, 111, 170–178. .",
+ allowedValues: ['TNP', 'PROBA'],
type: String.class],
timestep : [name : 'timestep',
title : "timestep",
- description : "Number of iterations. Timestep in sec. Default value : 1 ",
+ description : "Number of iterations. Timestep in sec.",
+ default : 1,
type: Integer.class],
gridStep : [name : 'gridStep',
title : "gridStep",
- description : "Distance between location of vehicle along the network in meters. Default value : 10 ",
+ description : "Distance between location of vehicle along the network in meters.",
+ default : 10,
type: Integer.class],
- duration : [name : 'duration', title: 'duration in sec.',
- description: 'Number of seconds to compute (INTEGER). Default value : 60 ',
- type: Integer.class],
+ duration: [name : 'duration', title: 'duration in sec.',
+ description: 'Number of seconds to compute (INTEGER).',
+ default : 60,
+ type : Integer.class],
]
outputs = [
result: [
- name : 'Result output string',
- title : 'Result output string',
- description: 'This type of result does not allow the blocks to be linked together.',
+ name : 'Generated table name',
+ title : 'Generated table name',
+ description: 'Name of the generated table. Can be used as the input of other process.',
type : String.class
]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
// main function of the script
def exec(Connection connection, input) {
+ Logger logger = LoggerFactory.getLogger("Flow_2_Noisy_Vehicles")
//Need to change the ConnectionWrapper to WpsConnectionWrapper to work under postGIS database
connection = new ConnectionWrapper(connection)
@@ -132,29 +111,20 @@ def exec(Connection connection, input) {
String resultString = null
// print to command window
- System.out.println('Start : Traffic Probabilistic Modelling')
+ logger.info('Start : Traffic Probabilistic Modelling')
def start = new Date()
// -------------------
// Get every inputs
// -------------------
- int duration = 60
- if (input['duration']) {
- duration = Integer.valueOf(input['duration'] as String)
- }
+ int duration = input.getOrDefault("duration", 60) as Integer
- int timestep = 1
- if (input['timestep']) {
- timestep = Integer.valueOf(input['timestep'] as String)
- }
+ int timestep = input.getOrDefault("timestep", 1) as Integer
- int gridStep = 10
- if (input['gridStep']) {
- gridStep = Integer.valueOf(input['gridStep'] as String)
- }
+ int gridStep = input.getOrDefault("gridStep", 10) as Integer
- int nIterations = (int) Math.round(duration/timestep);
+ int nIterations = (int) Math.round(duration/timestep)
String method = "PROBA"
if (input['method']) {
@@ -166,10 +136,10 @@ def exec(Connection connection, input) {
sources_table_name = sources_table_name.toUpperCase()
// Check if srid are in metric projection.
int sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(sources_table_name))
- if (sridSources == 3785 || sridSources == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+sources_table_name+".")
+ if (!DataBaseUtilities.isSridMetric(connection, sridSources)) throw new IllegalArgumentException("Error : Please use a metric projection for "+sources_table_name+".")
if (sridSources == 0) throw new IllegalArgumentException("Error : The table "+sources_table_name+" does not have an associated SRID.")
- System.out.println('Start time : ' + TimeCategory.minus(new Date(), start))
+ logger.info('Start time : ' + TimeCategory.minus(new Date(), start))
sql.execute("DROP TABLE IF EXISTS ROAD_POINTS" )
sql.execute("CREATE TABLE ROAD_POINTS(ROAD_ID serial, THE_GEOM geometry, LV int, LV_SPD real, HV int, HV_SPD real) AS SELECT r.PK, ST_Tomultipoint(ST_Densify(the_geom, "+gridStep+")), r.LV_D, r.LV_SPD_D, r.HGV_D, r.HGV_SPD_D FROM "+sources_table_name+" r WHERE NOT ST_IsEmpty(r.THE_GEOM) ;")
@@ -189,7 +159,7 @@ def exec(Connection connection, input) {
"ALTER TABLE VEHICLES_PROBA ALTER COLUMN LV_DENS_D double;" +
"ALTER TABLE VEHICLES_PROBA ALTER COLUMN HGV_DENS_D double;" )
- IndividualVehicleEmissionProcessData probabilisticProcessData = new IndividualVehicleEmissionProcessData();
+ IndividualVehicleEmissionProcessData probabilisticProcessData = new IndividualVehicleEmissionProcessData()
probabilisticProcessData.setDynamicEmissionTable("VEHICLES_PROBA", sql)
@@ -239,7 +209,7 @@ def exec(Connection connection, input) {
while (rs.next()) {
Road road = new Road()
- System.out.println(k + "/" + roadCount + " % " + 100*k/roadCount)
+ logger.info(k + "/" + roadCount + " % " + 100*k/roadCount)
k++
road.setRoad(
@@ -291,19 +261,18 @@ def exec(Connection connection, input) {
sql.execute("drop table VEHICLES_PROBA if exists;")
- System.out.println('Intermediate time : ' + TimeCategory.minus(new Date(), start))
- System.out.println("Export data to table")
+ logger.info('Intermediate time : ' + TimeCategory.minus(new Date(), start))
+ logger.info("Export data to table")
resultString = "Calculation Done ! The table SOURCES_EMISSION and SOURCES_GEOM has been created."
// print to command window
- System.out.println('Result : ' + resultString)
- System.out.println('End : Traffic Probabilistic Modelling')
- System.out.println('Duration : ' + TimeCategory.minus(new Date(), start))
+ logger.info('Result : ' + resultString)
+ logger.info('End : Traffic Probabilistic Modelling')
+ logger.info('Duration : ' + TimeCategory.minus(new Date(), start))
- // print to WPS Builder
- return resultString
+ return [result : "SOURCES_GEOM"]
}
@@ -357,7 +326,7 @@ class Road {
Coordinate[] coordinates = geom.getCoordinates()
for(int i = 1; i < coordinates.length; i++){
- line_segments.add(new LineSegment(coordinates[i-1], coordinates[i]));
+ line_segments.add(new LineSegment(coordinates[i-1], coordinates[i]))
}
/*for (String vehicle_type: [
@@ -842,10 +811,6 @@ class IndividualVehicleEmissionProcessData {
LV.put(pk, (double) row[2])
SPEED_HV.put(pk, (double) row[3])
HV.put(pk, (double) row[4])
-
}
-
-
}
-
}
\ No newline at end of file
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy
similarity index 80%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy
index 2d2b41b69..d759b4117 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Ind_Vehicles_2_Noisy_Vehicles.groovy
@@ -12,16 +12,13 @@
/**
* @Author Pierre Aumond, Université Gustave Eiffel
- * @Author Valetin Le Bescond, Université Gustave Eiffel, Ghent University
+ * @Author Valentin Le Bescond, Université Gustave Eiffel, Ghent University
*/
-package org.noise_planet.noisemodelling.wps.Dynamic
+package org.noise_planet.noisemodelling.scripts.Dynamic
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.time.TimeCategory
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
@@ -32,7 +29,7 @@ import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.locationtech.jts.geom.*
import org.noise_planet.noisemodelling.emission.road.cnossosvar.RoadVehicleCnossosvar
import org.noise_planet.noisemodelling.emission.road.cnossosvar.RoadVehicleCnossosvarParameters
-
+import org.noise_planet.noisemodelling.jdbc.utils.DataBaseUtilities
import java.sql.Connection
import java.sql.SQLException
@@ -45,30 +42,35 @@ description = 'Calculating dynamic road emissions based on vehicles trajectories
'- HZ63, HZ125, HZ250, HZ500, HZ1000, HZ2000, HZ4000, HZ8000 : 8 columns giving the emission sound level for each octave band (FLOAT).'
inputs = [
- tableVehicles : [name : 'Individual Vehicles table',
- title : "table of the individual Vehicles",
- description : "it should contain timestep, geometry (POINT), speed, acceleration, veh_type...",
- type: String.class],
- tableSourceGeom : [name : 'Source geometry table',
- title : "table of the individual Vehicles",
- description : "table of points source geometry, the output emission will be reattached to" +
- " the index of this table according to the snap distance. Should be SOURCES_GEOM" +
- " See Point_Source_From_Network to convert lines to points",
- type: String.class],
+ tableVehicles : [
+ name : 'Individual Vehicles table',
+ title : "table of the individual Vehicles",
+ description : "it should contain timestep, geometry (POINT), speed, acceleration, veh_type...",
+ type: String.class],
+ tableSourceGeom : [
+ name : 'Source geometry table',
+ title : "table of the source geometry",
+ description : "table of points source geometry, the output emission will be reattached to" +
+ " the index of this table according to the snap distance. Should be SOURCES_GEOM" +
+ " See Point_Source_From_Network to convert lines to points",
+ type: String.class],
distance2snap : [name : 'Snap distance',
title : "Maximum distance to snap on the network point sources",
description : "Maximum distance to snap on the network point sources",
min : 0,
max : 1,
type: Double.class],
- tableFormat : [name : 'Vehicles table format',
- title : 'Format of the individual Vehicles table',
- description :'Format of the individual Vehicles table. Can be for the moment SUMO or Matsim. See in the code to understand the different format.',
- type: String.class],
- keepNoEmissionGeoms : [name : 'Keep source geometries without emission value',
- title : 'Keep source geometries without emission value',
- description :'Do not delete source geometries that does not contain any emission value. Default to true, it reduce the computation time when evaluating the attenuation',
- min : 0, max: 1, type: Boolean.class]
+ tableFormat : [
+ name : 'Vehicles table format',
+ title : 'Format of the individual Vehicles table',
+ description :'Format of the individual Vehicles table. Can be for the moment SUMO or Matsim. See in the code to understand the different format.',
+ type: String.class],
+ keepNoEmissionGeoms : [
+ name : 'Keep source geometries without emission value',
+ title : 'Keep source geometries without emission value',
+ description :'Do not delete source geometries that does not contain any emission value. Default to true, it reduce the computation time when evaluating the attenuation',
+ default: true,
+ type: Boolean.class]
]
outputs = [
@@ -80,32 +82,6 @@ outputs = [
]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
-
// main function of the script
def exec(Connection connection, Map input) {
@@ -134,7 +110,7 @@ def exec(Connection connection, Map input) {
String tableFormat = input['tableFormat']
- double distance2snap = input['distance2snap']
+ double distance2snap = input['distance2snap'] as Double
boolean removeGeomsNoEmission = true
if (input['keepNoEmissionGeoms']) {
@@ -145,8 +121,8 @@ def exec(Connection connection, Map input) {
// do it case-insensitive
vehicles_table_name = vehicles_table_name.toUpperCase()
// Check if srid are in metric projection.
- sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(vehicles_table_name))
- if (sridSources == 3785 || sridSources == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+vehicles_table_name+".")
+ int sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(vehicles_table_name))
+ if (!DataBaseUtilities.isSridMetric(connection, sridSources)) throw new IllegalArgumentException("Error : Please use a metric projection for "+vehicles_table_name+".")
if (sridSources == 0) throw new IllegalArgumentException("Error : The table "+vehicles_table_name+" does not have an associated SRID.")
System.out.println('Start time : ' + TimeCategory.minus(new Date(), start))
@@ -339,7 +315,4 @@ class VehicleEmissionProcessData {
return res_LV
}
-
-
-
}
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Noise_From_Attenuation_Matrix.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Noise_From_Attenuation_Matrix.groovy
similarity index 87%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Noise_From_Attenuation_Matrix.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Noise_From_Attenuation_Matrix.groovy
index 5034af663..20fa98193 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Noise_From_Attenuation_Matrix.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Noise_From_Attenuation_Matrix.groovy
@@ -14,11 +14,9 @@
* @Author Pierre Aumond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Dynamic
+package org.noise_planet.noisemodelling.scripts.Dynamic
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
import org.h2gis.utilities.dbtypes.DBTypes
@@ -77,27 +75,10 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
def exec(Connection connection, input) {
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Point_Source_From_Network.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Point_Source_From_Network.groovy
similarity index 80%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Point_Source_From_Network.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Point_Source_From_Network.groovy
index 1ebf39a12..f7b1675e0 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Point_Source_From_Network.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Point_Source_From_Network.groovy
@@ -14,12 +14,12 @@
* @Author Pierre Aumond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Dynamic
+package org.noise_planet.noisemodelling.scripts.Dynamic
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+
import org.h2gis.utilities.GeometryMetaData
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.TableLocation
@@ -67,27 +67,10 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
def exec(connection, Map input) {
@@ -101,15 +84,9 @@ def exec(connection, Map input) {
logger.info('Start : Point_Source_From_Network.groovy')
logger.info("inputs {}", input)
- int gridStep = 10 // 10 meters is the default value
- if (input['gridStep']) {
- gridStep = Integer.valueOf(input['gridStep'] as String)
- }
+ int gridStep = input.getOrDefault("gridStep", 10) as Integer // 10 meters is the default value
- double h = 0.05 // height of the source (0.05 m) for road traffic
- if (input['height']) {
- h = input['height'] as Double
- }
+ double h = input.getOrDefault("height", 0.05) as Double // height of the source (0.05 m) for road traffic
String roadsTableName = input['tableNetwork']
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Split_Sources_Period.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Split_Sources_Period.groovy
similarity index 85%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Split_Sources_Period.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Split_Sources_Period.groovy
index 3af652d61..490673adb 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Dynamic/Split_Sources_Period.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Dynamic/Split_Sources_Period.groovy
@@ -10,17 +10,16 @@
*
*/
-package org.noise_planet.noisemodelling.wps.Dynamic
+package org.noise_planet.noisemodelling.scripts.Dynamic
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.TableLocation
import org.h2gis.utilities.dbtypes.DBTypes
import org.h2gis.utilities.dbtypes.DBUtils
import org.h2gis.utilities.wrapper.ConnectionWrapper
+import org.noise_planet.noisemodelling.jdbc.utils.DataBaseUtilities
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -78,27 +77,10 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
def exec(Connection connection, Map input) {
@@ -132,7 +114,7 @@ def exec(Connection connection, Map input) {
}
int sridSources = GeometryTableUtilities.getSRID(connection, TableLocation.parse(tableSourceDynamic, dbType))
- if (sridSources == 3785 || sridSources == 4326) throw new IllegalArgumentException("Error : Please use a metric projection for "+tableSourceDynamic+".")
+ if (!DataBaseUtilities.isSridMetric(connection, sridSources)) throw new IllegalArgumentException("Error : Please use a metric projection for "+tableSourceDynamic+".")
if (sridSources == 0) throw new IllegalArgumentException("Error : The table "+tableSourceDynamic+" does not have an associated SRID.")
def columnNames = JDBCUtilities.getColumnNames(connection, tableSourceDynamic)
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Noise_Map_Difference.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Noise_Map_Difference.groovy
similarity index 87%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Noise_Map_Difference.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Noise_Map_Difference.groovy
index 56c1b8b46..fcab0292a 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Noise_Map_Difference.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Noise_Map_Difference.groovy
@@ -13,12 +13,12 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental
+package org.noise_planet.noisemodelling.scripts.Experimental
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -75,27 +75,10 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
def exec(Connection connection, input) {
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_AADF.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_AADF.groovy
similarity index 93%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_AADF.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_AADF.groovy
index ca0cd72b7..ab3057223 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_AADF.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_AADF.groovy
@@ -1,14 +1,14 @@
-package org.noise_planet.noisemodelling.wps.Experimental
+package org.noise_planet.noisemodelling.scripts.Experimental
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
/**
* @Author Pierre Aumond, 13/11/2019, Université Gustave Eiffel
*/
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.SpatialResultSet
@@ -36,29 +36,10 @@ outputs = [result: [name: 'result', title: 'Result', type: String.class]]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if(dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore)store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = ""
- if (input['databaseName']) {
- dbName = input['databaseName'] as String
- }
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable { Connection connection ->
- exec(connection, input)
- }
-}
def exec(connection, input) {
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_TMJA.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_TMJA.groovy
similarity index 56%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_TMJA.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_TMJA.groovy
index f041b177c..04bf77838 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental/Road_Emission_From_TMJA.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental/Road_Emission_From_TMJA.groovy
@@ -2,12 +2,12 @@
* @Author Pierre Aumond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental
+package org.noise_planet.noisemodelling.scripts.Experimental
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
+
import org.h2gis.utilities.GeometryTableUtilities
import org.h2gis.utilities.JDBCUtilities
import org.h2gis.utilities.SpatialResultSet
@@ -31,17 +31,7 @@ inputs = [databaseName : [name: 'Name of the database', title: 'Name of the d
outputs = [result: [name: 'result', title: 'Result', type: String.class]]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-
-def run(input) {
+def exec(Connection connection, Map input) {
String output = null
// -------------------
@@ -53,91 +43,72 @@ def run(input) {
}
sources_table_name = sources_table_name.toUpperCase()
- // Get name of the database
- String dbName = ""
- if (input['databaseName']) {
- dbName = input['databaseName'] as String
+ //Get the geometry field of the source table
+ TableLocation sourceTableIdentifier = TableLocation.parse(sources_table_name)
+ List geomFields = GeometryTableUtilities.getGeometryColumnNames(connection, sourceTableIdentifier)
+ if (geomFields.isEmpty()) {
+ output = String.format("The table %s does not exists or does not contain a geometry field", sourceTableIdentifier)
+ throw new SQLException(String.format("The table %s does not exists or does not contain a geometry field", sourceTableIdentifier));
}
+ String sourceGeomName = geomFields.get(0);
- // ----------------------------------
- // Start...
- // ----------------------------------
-
- System.out.println("Run ...")
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable { Connection connection ->
-
- //Need to change the ConnectionWrapper to WpsConnectionWrapper to work under postgis database
- connection = new ConnectionWrapper(connection)
- System.out.println("Connection to the database ok ...")
+ //Get the primary key field of the source table
+ int pkIndex = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(sources_table_name))
+ if (pkIndex < 1) {
+ throw new IllegalArgumentException(String.format("Source table %s does not contain a primary key", sourceTableIdentifier));
+ }
- //Get the geometry field of the source table
- TableLocation sourceTableIdentifier = TableLocation.parse(sources_table_name)
- List geomFields = GeometryTableUtilities.getGeometryColumnNames(connection, sourceTableIdentifier)
- if (geomFields.isEmpty()) {
- output = String.format("The table %s does not exists or does not contain a geometry field", sourceTableIdentifier)
- throw new SQLException(String.format("The table %s does not exists or does not contain a geometry field", sourceTableIdentifier));
+ // open sql connection
+ Sql sql = new Sql(connection)
+
+ // create empty LW_ROADS
+ sql.execute("drop table if exists LW_ROADS;")
+ sql.execute("create table LW_ROADS (IDSOURCE integer, the_geom Geometry, " +
+ "Ld63 double precision, Ld125 double precision, Ld250 double precision, Ld500 double precision, Ld1000 double precision, Ld2000 double precision, Ld4000 double precision, Ld8000 double precision," +
+ "Le63 double precision, Le125 double precision, Le250 double precision, Le500 double precision, Le1000 double precision, Le2000 double precision, Le4000 double precision, Le8000 double precision," +
+ "Ln63 double precision, Ln125 double precision, Ln250 double precision, Ln500 double precision, Ln1000 double precision, Ln2000 double precision, Ln4000 double precision, Ln8000 double precision);")
+
+ def qry = 'INSERT INTO LW_ROADS(IDSOURCE,the_geom, ' +
+ 'Ld63, Ld125, Ld250, Ld500, Ld1000,Ld2000, Ld4000, Ld8000,' +
+ 'Le63, Le125, Le250, Le500, Le1000,Le2000, Le4000, Le8000,' +
+ 'Ln63, Ln125, Ln250, Ln500, Ln1000,Ln2000, Ln4000, Ln8000) ' +
+ 'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);'
+
+
+ long start = System.currentTimeMillis()
+ System.out.println("Start ...")
+ // fill the table LW_ROADS
+ sql.withBatch(100, qry) { ps ->
+ PreparedStatement st = connection.prepareStatement("SELECT * FROM " + sources_table_name)
+ SpatialResultSet rs = st.executeQuery().unwrap(SpatialResultSet.class)
+ while (rs.next()) {
+ System.println(rs)
+ Geometry geo = rs.getGeometry()
+ def results = computeLw(rs.getLong(pkIndex), geo, rs)
+
+ ps.addBatch(rs.getLong(pkIndex) as Integer, geo as Geometry,
+ results[0][0] as Double, results[0][1] as Double, results[0][2] as Double,
+ results[0][3] as Double, results[0][4] as Double, results[0][5] as Double,
+ results[0][6] as Double, results[0][7] as Double,
+ results[1][0] as Double, results[1][1] as Double, results[1][2] as Double,
+ results[1][3] as Double, results[1][4] as Double, results[1][5] as Double,
+ results[1][6] as Double, results[1][7] as Double,
+ results[2][0] as Double, results[2][1] as Double, results[2][2] as Double,
+ results[2][3] as Double, results[2][4] as Double, results[2][5] as Double,
+ results[2][6] as Double, results[2][7] as Double)
}
- String sourceGeomName = geomFields.get(0);
+ }
- //Get the primary key field of the source table
- int pkIndex = JDBCUtilities.getIntegerPrimaryKey(connection, TableLocation.parse(sources_table_name))
- if (pkIndex < 1) {
- throw new IllegalArgumentException(String.format("Source table %s does not contain a primary key", sourceTableIdentifier));
- }
+ sql.execute("UPDATE LW_ROADS SET THE_GEOM = ST_UPDATEZ(The_geom,0.05);")
+ sql.execute("ALTER TABLE LW_ROADS ADD pk INT AUTO_INCREMENT PRIMARY KEY;")
+ long computationTime = System.currentTimeMillis() - start;
+ output = "The Table LW_ROADS have been created"
+ return [result: output]
- // open sql connection
- Sql sql = new Sql(connection)
-
- // create empty LW_ROADS
- sql.execute("drop table if exists LW_ROADS;")
- sql.execute("create table LW_ROADS (IDSOURCE integer, the_geom Geometry, " +
- "Ld63 double precision, Ld125 double precision, Ld250 double precision, Ld500 double precision, Ld1000 double precision, Ld2000 double precision, Ld4000 double precision, Ld8000 double precision," +
- "Le63 double precision, Le125 double precision, Le250 double precision, Le500 double precision, Le1000 double precision, Le2000 double precision, Le4000 double precision, Le8000 double precision," +
- "Ln63 double precision, Ln125 double precision, Ln250 double precision, Ln500 double precision, Ln1000 double precision, Ln2000 double precision, Ln4000 double precision, Ln8000 double precision);")
-
- def qry = 'INSERT INTO LW_ROADS(IDSOURCE,the_geom, ' +
- 'Ld63, Ld125, Ld250, Ld500, Ld1000,Ld2000, Ld4000, Ld8000,' +
- 'Le63, Le125, Le250, Le500, Le1000,Le2000, Le4000, Le8000,' +
- 'Ln63, Ln125, Ln250, Ln500, Ln1000,Ln2000, Ln4000, Ln8000) ' +
- 'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);'
-
-
- long start = System.currentTimeMillis()
- System.out.println("Start ...")
- // fill the table LW_ROADS
- sql.withBatch(100, qry) { ps ->
- PreparedStatement st = connection.prepareStatement("SELECT * FROM " + sources_table_name)
- SpatialResultSet rs = st.executeQuery().unwrap(SpatialResultSet.class)
- while (rs.next()) {
- System.println(rs)
- Geometry geo = rs.getGeometry()
- def results = computeLw(rs.getLong(pkIndex), geo, rs)
-
- ps.addBatch(rs.getLong(pkIndex) as Integer, geo as Geometry,
- results[0][0] as Double, results[0][1] as Double, results[0][2] as Double,
- results[0][3] as Double, results[0][4] as Double, results[0][5] as Double,
- results[0][6] as Double, results[0][7] as Double,
- results[1][0] as Double, results[1][1] as Double, results[1][2] as Double,
- results[1][3] as Double, results[1][4] as Double, results[1][5] as Double,
- results[1][6] as Double, results[1][7] as Double,
- results[2][0] as Double, results[2][1] as Double, results[2][2] as Double,
- results[2][3] as Double, results[2][4] as Double, results[2][5] as Double,
- results[2][6] as Double, results[2][7] as Double)
- }
- }
-
- sql.execute("UPDATE LW_ROADS SET THE_GEOM = ST_UPDATEZ(The_geom,0.05);")
- sql.execute("ALTER TABLE LW_ROADS ADD pk INT AUTO_INCREMENT PRIMARY KEY;")
- long computationTime = System.currentTimeMillis() - start;
- output = "The Table LW_ROADS have been created"
- return [result: output]
+}
- }
-}
static double[][] computeLw(Long pk, Geometry geom, SpatialResultSet rs) throws SQLException {
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Agent_Exposure.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Agent_Exposure.groovy
similarity index 91%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Agent_Exposure.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Agent_Exposure.groovy
index e61964c14..ca878b4da 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Agent_Exposure.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Agent_Exposure.groovy
@@ -13,16 +13,13 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
import com.opencsv.CSVParser
import com.opencsv.CSVParserBuilder
import com.opencsv.CSVReaderHeaderAware
import com.opencsv.CSVReaderHeaderAwareBuilder
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.matsim.api.core.v01.Id
import org.matsim.api.core.v01.Scenario
@@ -32,33 +29,32 @@ import org.matsim.core.population.io.PopulationReader
import org.matsim.core.scenario.ScenarioUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import groovy.transform.CompileStatic
import java.nio.file.Files
import java.nio.file.Paths
import java.sql.*
import java.util.zip.GZIPInputStream
-title = 'Calculate Mastim agents exposure'
-description = "Loads a Matsim plans.xml file and calculate agents noise exposure, based on previously claculated timesliced noisemap at receiver positions, linked with matsim activities (facilities)"
+title = 'Calculate Matsim agents exposure'
+description = "Loads a Matsim plans.xml file and calculate agents noise exposure, based on previously calculated timesliced noisemap at receiver positions, linked with matsim activities (facilities)"
inputs = [
plansFile: [
name: 'Path of the Matsim output_plans file',
title: 'Path of the Matsim output_plans file',
- description: 'Path of the Matsim output_plans file For example : /home/mastim/simulation_output/output_plans.xml.gz',
+ description: 'Path of the Matsim output_plans file For example : /home/matsim/simulation_output/output_plans.xml.gz',
type: String.class
],
experiencedPlansFile: [
name: 'Path of the Matsim output_experienced_plans file',
title: 'Path of the Matsim output_experienced_plans file',
- description: 'Path of the Matsim output_plans file For example : /home/mastim/simulation_output/output_experienced_plans.xml.gz',
+ description: 'Path of the Matsim output_plans file For example : /home/matsim/simulation_output/output_experienced_plans.xml.gz',
type: String.class
],
personsCsvFile: [
name: 'personsCsvFile',
title: 'personsCsvFile',
- description: 'Path of the Matsim output_persons csv file For example : /home/mastim/simulation_output/output_persons.csv.gz',
+ description: 'Path of the Matsim output_persons csv file For example : /home/matsim/simulation_output/output_persons.csv.gz',
min: 0,
max: 1,
type: String.class
@@ -76,7 +72,7 @@ inputs = [
title: 'Table containing the noise data',
description: 'Table containing the noise data' +
' The table must contain the following fields :' +
- ' PK, IDRECEIVER, THE_GEOM, HZ63, HZ125, HZ250, HZ500, HZ1000, HZ2000, HZ000, HZ8000, TIME',
+ ' PK, IDRECEIVER, THE_GEOM, HZ63, HZ125, HZ250, HZ500, HZ1000, HZ2000, HZ000, HZ8000, PERIOD',
type: String.class
],
timeBinSize: [
@@ -92,8 +88,7 @@ inputs = [
name: 'Projection identifier',
title: 'Projection identifier',
description: 'Original projection identifier (also called SRID) of your tables.' +
- 'It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).' +
- ' Default value : 4326 ',
+ 'It should be an EPSG code; an integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).',
min: 0,
max: 1,
type: Integer.class
@@ -117,30 +112,6 @@ outputs = [
]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-// run the script
-def run(input) {
-
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
@CompileStatic
@@ -200,21 +171,21 @@ static def exec(Connection connection, input) {
String column = rs.getString("COLUMN_NAME");
if (column == "IDRECEIVER") {
indexIDRECEIVER = true;
- logger.info("index on data table IDSOURCE found")
+ logger.info("index on column IDSOURCE found")
}
- else if (column == "TIME") {
+ else if (column == "PERIOD") {
indexTIMESTRING = true;
- logger.info("index on data table TIME found")
+ logger.info("index on column PERIOD found")
}
}
if (!indexIDRECEIVER) {
- logger.info("index on data table IDRECEIVER, NOT found, creating one...")
+ logger.info("index on column IDRECEIVER, NOT found, creating one...")
sql.execute("CREATE INDEX ON " + dataTable + " (IDRECEIVER)");
}
if (!indexTIMESTRING) {
- logger.info("index on data table TIME, NOT found, creating one...")
- sql.execute("CREATE INDEX ON " + dataTable + " (TIME)");
+ logger.info("index on column PERIOD, NOT found, creating one...")
+ sql.execute("CREATE INDEX ON " + dataTable + " (PERIOD)");
}
logger.info("searching index on receivers table... ")
@@ -224,12 +195,12 @@ static def exec(Connection connection, input) {
String column = rs.getString("COLUMN_NAME");
if (column == "FACILITY") {
indexFACILITY = true;
- logger.info("index on receivers table FACILITY found")
+ logger.info("index on column FACILITY found")
}
}
if (!indexFACILITY) {
- logger.info("index on receivers table FACILITY, NOT found, creating one...")
+ logger.info("index on column FACILITY, NOT found, creating one...")
sql.execute("CREATE INDEX ON " + receiversTable + " (FACILITY)");
}
@@ -358,17 +329,18 @@ static def exec(Connection connection, input) {
}
String query = '''
- SELECT D.LEQA, D.TIME
+ SELECT D.LAEQ, D.PERIOD
FROM ''' + dataTable + ''' D
INNER JOIN ''' + receiversTable + ''' R
ON D.IDRECEIVER = R.PK
WHERE R.FACILITY = \'''' + activityId + '''\'
+ AND D.PERIOD != ''
'''
ResultSet result = stmt.executeQuery(query)
Map timeSeries = new HashMap()
while(result.next()) {
- int timeBin = result.getInt("TIME")
- Double value = result.getDouble("LEQA")
+ int timeBin = Integer.parseInt(result.getString("PERIOD"));
+ Double value = result.getDouble("LAEQ")
timeSeries.put(timeBin, value)
}
activitiesTimeSeries.put(activityId, timeSeries)
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Import_Activities.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Import_Activities.groovy
similarity index 82%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Import_Activities.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Import_Activities.groovy
index aa80b9cad..d9fb88bd9 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Import_Activities.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Import_Activities.groovy
@@ -13,12 +13,9 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.matsim.api.core.v01.Coord
import org.matsim.api.core.v01.Scenario
@@ -29,7 +26,6 @@ import org.matsim.facilities.ActivityFacility
import org.matsim.facilities.MatsimFacilitiesReader
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.*
import groovy.sql.Sql
@@ -47,8 +43,7 @@ inputs = [
name: 'Projection identifier',
title: 'Projection identifier',
description: 'Original projection identifier (also called SRID) of your table.' +
- 'It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).' +
- ' Default value : 4326 ',
+ 'It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).',
min: 0,
max: 1,
type: Integer.class
@@ -72,27 +67,6 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-
-def run(input) {
-
- // Get name of the database
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
@CompileStatic
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Plot_Exposition_Distribution.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Plot_Exposition_Distribution.groovy
similarity index 87%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Plot_Exposition_Distribution.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Plot_Exposition_Distribution.groovy
index 0f258da9d..04bf1f36b 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Plot_Exposition_Distribution.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Plot_Exposition_Distribution.groovy
@@ -13,13 +13,13 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
+
+
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
+
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.jfree.chart.ChartFactory
import org.jfree.chart.ChartPanel
@@ -71,27 +71,10 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-def run(input) {
- // Get name of the database
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
@CompileStatic
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Closest.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Closest.groovy
similarity index 87%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Closest.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Closest.groovy
index 2349bcbef..44ced0751 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Closest.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Closest.groovy
@@ -13,23 +13,19 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.GroovyRowResult
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.locationtech.jts.geom.Geometry
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.*
import groovy.sql.Sql
-title = 'Chose Closest Receivers For Matsim Activities'
-description = 'Chose the closest receiver in a RECEIVERS table for every Mastim Activity in an ACTIVITIES table'
+title = 'Choose closest receivers for Matsim activities'
+description = 'Choose the closest receiver in a RECEIVERS table for every Mastim activity in an ACTIVITIES table'
inputs = [
activitiesTable : [
@@ -67,28 +63,6 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-
-def run(input) {
-
- // Get name of the database
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
-
// main function of the script
@CompileStatic
static def exec(Connection connection, input) {
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Random.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Random.groovy
similarity index 88%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Random.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Random.groovy
index 113628e51..61e30c712 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Receivers_From_Activities_Random.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Receivers_From_Activities_Random.groovy
@@ -13,23 +13,19 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.GroovyRowResult
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.locationtech.jts.geom.Geometry
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.*
import groovy.sql.Sql
-title = 'Chose a Random Receivers For Matsim Activities'
-description = 'Chose the closest building for every Mastim Activity in an ACTIVITIES table, and then chose a random receiver previously generated around this building.'
+title = 'Choose a random receivers for Matsim activities'
+description = 'Choose the closest building for every Mastim activity in an ACTIVITIES table, and then chose a random receiver previously generated around this building.'
inputs = [
activitiesTable : [
@@ -59,9 +55,8 @@ inputs = [
randomSeed : [
name: 'Random seed',
title: 'Random seed',
- description: 'Random seed, default: 1234',
- min : 0,
- max : 1,
+ description: 'Random seed',
+ default : 1234,
type: Integer.class
],
outTableName: [
@@ -83,27 +78,6 @@ outputs = [
]
]
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-
-
-def run(input) {
-
- // Get name of the database
- String dbName = "h2gisdb"
-
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
@CompileStatic
diff --git a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Traffic_From_Events.groovy b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Traffic_From_Events.groovy
similarity index 61%
rename from wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Traffic_From_Events.groovy
rename to noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Traffic_From_Events.groovy
index b7d973d9e..61dd23fa6 100644
--- a/wps_scripts/src/main/groovy/org/noise_planet/noisemodelling/wps/Experimental_Matsim/Traffic_From_Events.groovy
+++ b/noisemodelling-scripts/src/main/groovy/org/noise_planet/noisemodelling/scripts/Experimental_Matsim/Traffic_From_Events.groovy
@@ -13,18 +13,16 @@
* @Author Valentin Le Bescond, Université Gustave Eiffel
*/
-package org.noise_planet.noisemodelling.wps.Experimental_Matsim
+package org.noise_planet.noisemodelling.scripts.Experimental_Matsim
-import geoserver.GeoServer
-import geoserver.catalog.Store
import groovy.sql.Sql
import groovy.transform.CompileStatic
-import org.geotools.jdbc.JDBCDataStore
import org.h2gis.utilities.wrapper.ConnectionWrapper
import org.locationtech.jts.geom.Coordinate
import org.locationtech.jts.io.WKTWriter
import org.matsim.api.core.v01.Coord
import org.matsim.api.core.v01.Id
+import org.matsim.api.core.v01.Scenario
import org.matsim.api.core.v01.events.*
import org.matsim.api.core.v01.events.handler.*
import org.matsim.api.core.v01.network.Link
@@ -36,142 +34,128 @@ import org.matsim.core.events.EventsUtils
import org.matsim.core.events.MatsimEventsReader
import org.matsim.core.network.io.MatsimNetworkReader
import org.matsim.core.scenario.ScenarioUtils
+import org.matsim.vehicles.MatsimVehicleReader
import org.matsim.vehicles.Vehicle
+import org.matsim.vehicles.VehicleType
+import org.matsim.vehicles.Vehicles
import org.noise_planet.noisemodelling.emission.road.cnossos.RoadCnossos
import org.noise_planet.noisemodelling.emission.road.cnossos.RoadCnossosParameters
import org.slf4j.Logger
import org.slf4j.LoggerFactory
-
import java.sql.Connection
import java.sql.PreparedStatement
-
import java.io.FileNotFoundException
-title = 'Import traffic data from Mastim simultaion output folder'
-description = 'Read Mastim events output file in order to get traffic NoiseModelling input'
+title = 'Import traffic data from Matsim simulation output folder'
+description = 'Read Matsim events output file in order to get traffic NoiseModelling input'
inputs = [
- folder: [
- name: 'Path of the Matsim output folder',
- title: 'Path of the Matsim output folder',
- description: 'Path of the Matsim output folder For example : /home/mastim/simulation_output' +
- ' The folder must contain at least the following files: ' +
- ' - output_network.xml.gz' +
- ' - output_events.xml.gz',
- type: String.class
- ],
- timeBinSize: [
- name: 'The size of time bins in seconds.',
- title: 'The size of time bins in seconds.',
- description: 'This parameter dictates the time resolution of the resulting data ' +
- ' The time information stored will be the starting time of the time bins ' +
- ' For exemple with a timeBinSize of 3600, the data will be analysed using the following timeBins: ' +
- ' 0, 3600, 7200, ..., 79200, 82800' +
- ' Default: 3600',
- min: 0,
- max: 1,
- type: Integer.class
- ],
- populationFactor: [
- name: 'Population Factor',
- title: 'Population Factor',
- description: 'Set the population factor of the MATSim simulation' +
- ' Must be a decimal number between 0 and 1' +
- ' Default: 1.0',
- min: 0,
- max: 1,
- type: String.class
- ],
- link2GeometryFile: [
- name: 'Network CSV file',
- title: 'Network CSV file',
- description: 'The path of the pt2matsim CSV file generated when importing OSM network. Ignored if not set.' +
- ' The file must contain at least two columns : ' +
- ' - The link ID' +
- ' - The WKT geometry',
- min: 0,
- max: 1,
- type: String.class
- ],
- SRID : [
- name: 'Projection identifier',
- title: 'Projection identifier',
- description: 'Projection identifier (also called SRID) of the geometric data.' +
- 'It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).' +
- ' Default value : 4326 ',
- min: 0,
- max: 1,
- type: Integer.class
- ],
- exportTraffic: [
- name: 'Export additionnal traffic data ?',
- title: 'Export additionnal traffic data ?',
- description: 'Define if you want to output average speed and flow per vehicle category in an additional table' +
- ' Default: False',
- min: 0,
- max: 1,
- type: Boolean.class
- ],
- skipUnused: [
- name: 'Skip unused links ?',
- title: 'Skip unused links ?',
- description: 'Define if links with unused traffic should be omitted in the output table.' +
- ' Default: True',
- min: 0,
- max: 1,
- type: Boolean.class
- ],
- outTableName: [
- name: 'Output table name',
- title: 'Output table name',
- description: 'Name of the table you want to create.' +
- ' A table with this name will be created plus another with a "_LW" suffix' +
- ' For exemple if set to "MATSIM_ROADS (default value)":' +
- ' - the table MATSIM_ROADS, with the link ID and the geometry field' +
- ' - the table MATSIM_ROADS_LW, with the link ID and the traffic data',
- min: 0,
- max: 1,
- type: String.class
- ]
+ folder : [
+ name : 'Path of the Matsim output folder',
+ title : 'Path of the Matsim output folder',
+ description: 'Path of the Matsim output folder For example : /home/matsim/simulation_output' +
+ ' The folder must contain at least the following files: ' +
+ ' - output_network.xml.gz' +
+ ' - output_allVehicles.xml.gz' +
+ ' - output_events.xml.gz',
+ type : String.class
+ ],
+ timeBinSize : [
+ name : 'The size of time bins in seconds.',
+ title : 'The size of time bins in seconds.',
+ description: 'This parameter dictates the time resolution of the resulting data ' +
+ ' The time information stored will be the starting time of the time bins ' +
+ ' For exemple with a timeBinSize of 3600, the data will be analysed using the following timeBins: ' +
+ ' 0, 3600, 7200, ..., 79200, 82800',
+ default : 3600,
+ type : Integer.class
+ ],
+ timeBinMin : [
+ name : 'The minimum of time bins in seconds',
+ title : 'The minimum of time bins in seconds',
+ description: 'The minimum of time bins in seconds',
+ default : 0,
+ type : Integer.class
+ ],
+ timeBinMax : [
+ name : 'The maximum of time bins in seconds',
+ title : 'The maximum of time bins in seconds',
+ description: 'The maximum of time bins in seconds, default value: 86400 ',
+ default : 86400,
+ type : Integer.class
+ ],
+ populationFactor : [
+ name : 'Population Factor',
+ title : 'Population Factor',
+ description: 'Set the population factor of the MATSim simulation' +
+ ' Must be a decimal number between 0 and 1',
+ default : 1.0,
+ type : Double.class
+ ],
+ link2GeometryFile: [
+ name : 'Network CSV file',
+ title : 'Network CSV file',
+ description: 'The path of the pt2matsim CSV file generated when importing OSM network. Ignored if not set.' +
+ ' The file must contain at least two columns : ' +
+ ' - The link ID' +
+ ' - The WKT geometry',
+ min : 0,
+ max : 1,
+ type : String.class
+ ],
+ SRID : [
+ name : 'Projection identifier',
+ title : 'Projection identifier',
+ description: 'Projection identifier (also called SRID) of the geometric data.' +
+ 'It should be an EPSG code, a integer with 4 or 5 digits (ex: 3857 is Web Mercator projection).',
+ min : 0,
+ max : 1,
+ type : Integer.class
+ ],
+ exportTraffic : [
+ name : 'Export additionnal traffic data ?',
+ title : 'Export additionnal traffic data ?',
+ description: 'Define if you want to output average speed and flow per vehicle category in an additional table',
+ default : false,
+ type : Boolean.class
+ ],
+ skipUnused : [
+ name : 'Skip unused links ?',
+ title : 'Skip unused links ?',
+ description: 'Define if links with unused traffic should be omitted in the output table.',
+ default : true,
+ type : Boolean.class
+ ],
+ outTableName : [
+ name : 'Output table name',
+ title : 'Output table name',
+ description: 'Name of the table you want to create.' +
+ ' A table with this name will be created plus another with a "_LW" suffix' +
+ ' For exemple if set to "MATSIM_ROADS (default value)":' +
+ ' - the table MATSIM_ROADS, with the link ID and the geometry field' +
+ ' - the table MATSIM_ROADS_LW, with the link ID and the traffic data',
+ min : 0,
+ max : 1,
+ type : String.class
+ ]
]
outputs = [
- result: [
- name: 'Result output string',
- title: 'Result output string',
- description: 'This type of result does not allow the blocks to be linked together.',
- type: String.class
- ]
+ result: [
+ name : 'Result output string',
+ title : 'Result output string',
+ description: 'This type of result does not allow the blocks to be linked together.',
+ type : String.class
+ ]
]
-// Open Connection to Geoserver
-static Connection openGeoserverDataStoreConnection(String dbName) {
- if (dbName == null || dbName.isEmpty()) {
- dbName = new GeoServer().catalog.getStoreNames().get(0)
- }
- Store store = new GeoServer().catalog.getStore(dbName)
- JDBCDataStore jdbcDataStore = (JDBCDataStore) store.getDataStoreInfo().getDataStore(null)
- return jdbcDataStore.getDataSource().getConnection()
-}
-// run the script
-def run(input) {
- // Get name of the database
- // by default an embedded h2gis database is created
- // Advanced user can replace this database for a postGis or h2Gis server database.
- String dbName = "h2gisdb"
- // Open connection
- openGeoserverDataStoreConnection(dbName).withCloseable {
- Connection connection ->
- return [result: exec(connection, input)]
- }
-}
// main function of the script
@CompileStatic
-static def exec(Connection connection, input) {
+static def exec(Connection connection, Map input) {
connection = new ConnectionWrapper(connection)
Sql sql = new Sql(connection)
@@ -197,18 +181,10 @@ static def exec(Connection connection, input) {
link2GeometryFile = input["link2GeometryFile"]
}
- int timeBinSize = 3600;
- if (input["timeBinSize"]) {
- timeBinSize = input["timeBinSize"] as int
- }
- int timeBinMin = 0;
- if (input["timeBinMin"]) {
- timeBinMin = input["timeBinMin"] as int
- }
- int timeBinMax = 86400;
- if (input["timeBinMax"]) {
- timeBinMax = input["timeBinMax"] as int
- }
+ int timeBinSize = input.getOrDefault("timeBinSize",3600) as Integer
+ int timeBinMin = input.getOrDefault("timeBinMin",0) as Integer
+ int timeBinMax = input.getOrDefault("timeBinMax",86400) as Integer
+
String SRID = "4326"
if (input['SRID']) {
@@ -230,25 +206,27 @@ static def exec(Connection connection, input) {
exportTraffic = input["exportTraffic"] as boolean
}
- double populationFactor = 1.0
- if (input["populationFactor"]) {
- populationFactor = input["populationFactor"] as double
- }
+ double populationFactor = input.getOrDefault("populationFactor",1.0) as Double
File f
String eventFile = folder + "/output_events.xml.gz"
f = new File(eventFile)
- if(!f.exists() || f.isDirectory()) {
+ if (!f.exists() || f.isDirectory()) {
throw new FileNotFoundException("output_events.xml.gz not found in MATSim folder")
}
String networkFile = folder + "/output_network.xml.gz"
f = new File(networkFile)
- if(!f.exists() || f.isDirectory()) {
+ if (!f.exists() || f.isDirectory()) {
throw new FileNotFoundException("output_network.xml.gz not found in MATSim folder")
}
+ String vehiclesFile = folder + "/output_allVehicles.xml.gz"
+ f = new File(vehiclesFile)
+ if (!f.exists() || f.isDirectory()) {
+ throw new FileNotFoundException("output_allVehicles.xml.gz not found in MATSim folder")
+ }
if (link2GeometryFile != "") {
f = new File(link2GeometryFile)
- if(!f.exists() || f.isDirectory()) {
+ if (!f.exists() || f.isDirectory()) {
throw new FileNotFoundException(link2GeometryFile + " not found")
}
}
@@ -266,57 +244,72 @@ static def exec(Connection connection, input) {
sql.execute("DROP TABLE IF EXISTS " + lwTableName)
sql.execute("CREATE TABLE " + lwTableName + '''(
PK integer PRIMARY KEY AUTO_INCREMENT,
- LINK_ID varchar(255),
- LW63 double precision, LW125 double precision, LW250 double precision, LW500 double precision, LW1000 double precision, LW2000 double precision, LW4000 double precision, LW8000 double precision,
- TIME int
+ IDSOURCE integer NOT NULL,
+ HZ63 double precision, HZ125 double precision, HZ250 double precision, HZ500 double precision, HZ1000 double precision, HZ2000 double precision, HZ4000 double precision, HZ8000 double precision,
+ PERIOD varchar(50)
);''')
if (exportTraffic) {
sql.execute("DROP TABLE IF EXISTS " + trafficTableName)
sql.execute("CREATE TABLE " + trafficTableName + '''(
PK integer PRIMARY KEY AUTO_INCREMENT,
- LINK_ID varchar(255),
+ IDSOURCE integer NOT NULL,
LV_D double precision,
LV_SPD_D double precision,
MV_D double precision,
MV_SPD_D double precision,
HGV_D double precision,
HGV_SPD_D double precision,
- TIME int
+ WAV_D double precision,
+ WAV_SPD_D double precision,
+ WBV_D double precision,
+ WBV_SPD_D double precision,
+ PERIOD varchar(50)
);''')
}
if (keepVehicleContrib) {
sql.execute("DROP TABLE IF EXISTS " + contribTableName)
sql.execute("CREATE TABLE " + contribTableName + '''(
PK integer PRIMARY KEY AUTO_INCREMENT,
- LINK_ID varchar(255),
+ IDSOURCE integer NOT NULL,
PERSON_ID varchar(255),
VEHICLE_ID varchar(255),
- LW63 double precision, LW125 double precision, LW250 double precision, LW500 double precision, LW1000 double precision, LW2000 double precision, LW4000 double precision, LW8000 double precision,
- TIME int
+ HZ63 double precision, HZ125 double precision, HZ250 double precision, HZ500 double precision, HZ1000 double precision, HZ2000 double precision, HZ4000 double precision, HZ8000 double precision,
+ PERIOD varchar(50)
);''')
}
PreparedStatement roadStatement = connection.prepareStatement("INSERT INTO " + outTableName + " (LINK_ID, OSM_ID, THE_GEOM) VALUES (?, ?, ST_UpdateZ(ST_GeomFromText(?, " + SRID + "),0.05))")
- PreparedStatement lwStatement = connection.prepareStatement("INSERT INTO " + lwTableName + " (LINK_ID, LW63, LW125, LW250, LW500, LW1000, LW2000, LW4000, LW8000, TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ PreparedStatement lwStatement = connection.prepareStatement("INSERT INTO " + lwTableName + " (IDSOURCE, HZ63, HZ125, HZ250, HZ500, HZ1000, HZ2000, HZ4000, HZ8000, PERIOD) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
PreparedStatement trafficStatement;
if (exportTraffic) {
- trafficStatement = connection.prepareStatement("INSERT INTO " + trafficTableName + " (LINK_ID, LV_D, LV_SPD_D, MV_D, MV_SPD_D, HGV_D, HGV_SPD_D, TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
+ trafficStatement = connection.prepareStatement("INSERT INTO " + trafficTableName + " (IDSOURCE, LV_D, LV_SPD_D, MV_D, MV_SPD_D, HGV_D, HGV_SPD_D, WAV_D, WAV_SPD_D, WBV_D, WBV_SPD_D, PERIOD) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
}
PreparedStatement contribStatement;
if (keepVehicleContrib) {
- contribStatement = connection.prepareStatement("INSERT INTO " + contribTableName + " (LINK_ID, PERSON_ID, VEHICLE_ID, LW63, LW125, LW250, LW500, LW1000, LW2000, LW4000, LW8000, TIME) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
+ contribStatement = connection.prepareStatement("INSERT INTO " + contribTableName + " (IDSOURCE, PERSON_ID, VEHICLE_ID, HZ63, HZ125, HZ250, HZ500, HZ1000, HZ2000, HZ4000, HZ8000, PERIOD) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
}
logger.info("Done Creating SQL tables")
- Network network = ScenarioUtils.loadScenario(ConfigUtils.createConfig()).getNetwork()
+ Scenario scenario = ScenarioUtils.loadScenario(ConfigUtils.createConfig());
+
+ Network network = scenario.getNetwork()
MatsimNetworkReader networkReader = new MatsimNetworkReader(network)
+ networkReader.setValidating(false)
logger.info("Start reading network file ... ")
networkReader.readFile(networkFile)
logger.info("Done reading network file ")
Map, Link> links = (Map, Link>) network.getLinks()
+ Vehicles vehicles = scenario.getVehicles();
+ MatsimVehicleReader vehicleReader = new MatsimVehicleReader(vehicles);
+ logger.info("Start reading vehicles file ... ")
+ vehicleReader.readFile(vehiclesFile);
+ logger.info("Done reading vehicles file ")
+
+ Map, Vehicle> vehicles_definitions = vehicles.getVehicles();
+
EventsManager evMgr = EventsUtils.createEventsManager()
ProcessOutputEventHandler evHandler = new ProcessOutputEventHandler()
@@ -326,6 +319,7 @@ static def exec(Connection connection, input) {
evHandler.setSRID(SRID)
evHandler.setPopulationFactor(populationFactor)
evHandler.initLinks((Map, Link>) links)
+ evHandler.initVehicles((Map, Vehicle>) vehicles_definitions)
evMgr.addHandler(evHandler)
@@ -342,7 +336,7 @@ static def exec(Connection connection, input) {
String line = null;
while ((line = br.readLine()) != null) {
String[] str = line.split(",", 2)
- if (str.size() > 1) {
+ if (str.length > 1) {
link2geomData.put(str[0], str[1].trim().replace("\"", ""))
}
}
@@ -359,10 +353,10 @@ static def exec(Connection connection, input) {
if (counter >= doprint) {
double elapsed = (System.currentTimeMillis() - start + 1) / 1000
logger.info(String.format("Processing Link %d (max:%d) - elapsed : %ss (%.1fit/s) - eta : %ss",
- counter, evHandler.links.size(), elapsed, counter/elapsed, (evHandler.links.size() - counter) / (counter / elapsed)))
+ counter, evHandler.links.size(), elapsed, counter / elapsed, (evHandler.links.size() - counter) / (counter / elapsed)))
doprint *= 2
}
- counter ++
+ counter++
if (skipUnused && !linkStatStruct.isUsed) {
continue
@@ -380,60 +374,60 @@ static def exec(Connection connection, input) {
roadStatement.setString(2, linkStatStruct.getOsmId())
roadStatement.setString(3, geomString)
roadStatement.execute()
- for (int timeBin = timeBinMin ; timeBin < timeBinMax; timeBin += timeBinSize) {
+ for (int timeBin = timeBinMin; timeBin < timeBinMax; timeBin += timeBinSize) {
int index = 1
lwStatement.setString(index, linkId)
List levels = linkStatStruct.getSourceLevels(timeBin)
- for (Double level: levels) {
- index ++
+ for (Double level : levels) {
+ index++
lwStatement.setDouble(index, level)
}
- index ++
- lwStatement.setInt(index, timeBin)
+ index++
+ lwStatement.setString(index, timeBin.toString())
lwStatement.addBatch()
}
lwStatement.executeBatch()
if (exportTraffic) {
- for (int timeBin = timeBinMin ; timeBin < timeBinMax; timeBin += timeBinSize) {
+ for (int timeBin = timeBinMin; timeBin < timeBinMax; timeBin += timeBinSize) {
int index = 1
trafficStatement.setString(index, linkId)
- Trip.Type[] types = [Trip.Type.LV, Trip.Type.MV, Trip.Type.HV]
+ Trip.Type[] types = [Trip.Type.LV, Trip.Type.MV, Trip.Type.HV, Trip.Type.WAV, Trip.Type.WBV]
for (Trip.Type type in types) {
int count = linkStatStruct.getVehicleCount(type, timeBin)
double speed = 0.0
if (count > 0) {
speed = Math.round(3.6 * linkStatStruct.link.getLength() / linkStatStruct.getMeanTravelTime(type, timeBin))
}
- index ++
+ index++
trafficStatement.setInt(index, count)
- index ++
+ index++
trafficStatement.setDouble(index, speed)
}
- index ++
- trafficStatement.setInt(index, timeBin)
+ index++
+ trafficStatement.setString(index, timeBin.toString())
trafficStatement.addBatch()
}
trafficStatement.executeBatch()
}
if (keepVehicleContrib) {
// sql.execute(linkStatStruct.toSqlInsertContrib(contribTableName));
- for (int timeBin = 0 ; timeBin < 86400; timeBin += timeBinSize) {
+ for (int timeBin = 0; timeBin < 86400; timeBin += timeBinSize) {
for (PersonContribFreq person_contrib : linkStatStruct.contributions.get(timeBin)) {
String personId = person_contrib.personId.toString()
String vehicleId = person_contrib.vehicleId.toString()
List contributions = person_contrib.contributions
int index = 1
contribStatement.setString(index, linkId)
- index ++
+ index++
contribStatement.setString(index, personId)
- index ++
+ index++
contribStatement.setString(index, vehicleId)
- for (Double contrib: contributions) {
- index ++
+ for (Double contrib : contributions) {
+ index++
contribStatement.setDouble(index, contrib)
}
- index ++
- contribStatement.setInt(index, timeBin)
+ index++
+ contribStatement.setString(index, timeBin.toString())
contribStatement.addBatch()
}
}
@@ -443,19 +437,25 @@ static def exec(Connection connection, input) {
logger.info("DONE Inserting Into SQL tables...")
logger.info("Start Creating indexes on tables ...")
- logger.info("CREATE INDEX " + outTableName + "_LINK_ID_IDX ON " + outTableName + " (LINK_ID);")
- sql.execute("CREATE INDEX " + outTableName + "_LINK_ID_IDX ON " + outTableName + " (LINK_ID);")
- logger.info("CREATE INDEX " + lwTableName + "_LINK_ID_IDX ON " + lwTableName + " (LINK_ID);")
- sql.execute("CREATE INDEX " + lwTableName + "_LINK_ID_IDX ON " + lwTableName + " (LINK_ID);")
- logger.info("CREATE INDEX " + lwTableName + "_TIME_IDX ON " + lwTableName + " (TIME);")
- sql.execute("CREATE INDEX " + lwTableName + "_TIME_IDX ON " + lwTableName + " (TIME);")
+ String indexSql = "CREATE SPATIAL INDEX " + outTableName + "_GEOM_IDX ON " + outTableName + " (THE_GEOM);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
+ indexSql = "CREATE INDEX " + lwTableName + "_IDSOURCE_IDX ON " + lwTableName + " (IDSOURCE);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
+ indexSql = "CREATE INDEX " + lwTableName + "_PERIOD_IDX ON " + lwTableName + " (PERIOD);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
if (keepVehicleContrib) {
- logger.info("CREATE INDEX " + contribTableName + "_LINK_ID_IDX ON " + contribTableName + " (LINK_ID);")
- sql.execute("CREATE INDEX " + contribTableName + "_LINK_ID_IDX ON " + contribTableName + " (LINK_ID);")
- logger.info("CREATE INDEX " + contribTableName + "_TIME_IDX ON " + contribTableName + " (TIME);")
- sql.execute("CREATE INDEX " + contribTableName + "_TIME_IDX ON " + contribTableName + " (TIME);")
- logger.info("CREATE INDEX " + contribTableName + "_PERSON_ID_IDX ON " + contribTableName + " (PERSON_ID);")
- sql.execute("CREATE INDEX " + contribTableName + "_PERSON_ID_IDX ON " + contribTableName + " (PERSON_ID);")
+ indexSql = "CREATE INDEX " + contribTableName + "_IDSOURCE_IDX ON " + contribTableName + " (IDSOURCE);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
+ indexSql = "CREATE INDEX " + contribTableName + "_PERIOD_IDX ON " + contribTableName + " (PERIOD);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
+ indexSql = "CREATE INDEX " + contribTableName + "_PERSON_ID_IDX ON " + contribTableName + " (PERSON_ID);"
+ logger.info(indexSql)
+ sql.execute(indexSql)
}
logger.info("Done Creating indexes on tables.")
@@ -470,6 +470,7 @@ class ProcessOutputEventHandler implements
PersonEntersVehicleEventHandler, PersonLeavesVehicleEventHandler {
Map, LinkStatStruct> links = new HashMap