diff --git a/pom.xml b/pom.xml index 466ebe83f..1f776207e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,6 @@ - + 4.0.0 ca.bc.gov.nrs.vdyp @@ -35,13 +37,13 @@ jsr305 3.0.2 - + org.apache.commons commons-math3 3.6.1 - + org.slf4j slf4j-api @@ -52,7 +54,7 @@ slf4j-jdk14 2.0.7 - + org.junit.jupiter junit-jupiter-engine @@ -66,7 +68,13 @@ ${junit.version} test - + + org.junit.jupiter + junit-jupiter-params + ${junit.version} + test + + org.hamcrest hamcrest @@ -147,7 +155,7 @@ - + diff --git a/vdyp-common/pom.xml b/vdyp-common/pom.xml index 8de7982ff..fbf1c9084 100644 --- a/vdyp-common/pom.xml +++ b/vdyp-common/pom.xml @@ -43,13 +43,18 @@ org.slf4j slf4j-jdk14 - + org.junit.jupiter junit-jupiter-api test - + + org.junit.jupiter + junit-jupiter-params + test + org.hamcrest hamcrest diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ControlKeys.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ControlKeys.java new file mode 100644 index 000000000..5a000158b --- /dev/null +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ControlKeys.java @@ -0,0 +1,12 @@ +package ca.bc.gov.nrs.vdyp.common; + +public class ControlKeys { + + private ControlKeys() { + } + + public static final String VDYP_POLYGON = "VDYP_POLYGON"; + public static final String VDYP_LAYER_BY_SPECIES = "VDYP_LAYER_BY_SPECIES"; + public static final String VDYP_LAYER_BY_SP0_BY_UTIL = "VDYP_LAYER_BY_SP0_BY_UTIL"; + +} diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ExpectationDifference.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ExpectationDifference.java index 32b455cb5..f360e3b10 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ExpectationDifference.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ExpectationDifference.java @@ -34,7 +34,7 @@ public static ExpectationDifference difference(Collection values, Coll missing.removeAll(values); var unexpected = new HashSet(values); unexpected.removeAll(expected); - return new ExpectationDifference(missing, unexpected); + return new ExpectationDifference<>(missing, unexpected); } /** diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java index 9fcd95560..3a3ae0511 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/Utils.java @@ -11,6 +11,8 @@ import javax.annotation.Nullable; +import ca.bc.gov.nrs.vdyp.model.Coefficients; + public class Utils { private Utils() { @@ -86,4 +88,25 @@ public static Map constMap(Consumer> body) { body.accept(map); return Collections.unmodifiableMap(map); } + + public static Coefficients heightVector(float small, float all) { + return new Coefficients(new float[] { small, all }, -1); + } + + public static Coefficients utilizationVector(float small, float all, float u1, float u2, float u3, float u4) { + return new Coefficients(new float[] { small, all, u1, u2, u3, u4 }, -1); + } + + public static Coefficients utilizationVector(float small, float u1, float u2, float u3, float u4) { + return utilizationVector(small, u1 + u2 + u3 + u4, u1, u2, u3, u4); + } + + public static Coefficients utilizationVector(float singleValue) { + return utilizationVector(0f, singleValue, 0f, 0f, singleValue); + } + + public static Coefficients utilizationVector() { + return utilizationVector(0f); + } + } diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ValueOrMarker.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ValueOrMarker.java index d1e7db6e6..c42aafda3 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ValueOrMarker.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common/ValueOrMarker.java @@ -88,7 +88,7 @@ public T handle(Function valueHandler, Function markerH * @return */ public static Builder builder(Class vClazz, Class mClazz) { - return new Builder(); + return new Builder<>(); } /** @@ -102,22 +102,18 @@ public static Builder builder(Class vClazz */ public static class Builder { - public Builder() { - - } - /** * Create a ValueOrMarker with a Marker */ public ValueOrMarker marker(Marker m) { - return new ValueOrMarker(m, true); + return new ValueOrMarker<>(m, true); } /** * Create a ValueOrMarker with a Value */ public ValueOrMarker value(Value v) { - return new ValueOrMarker(v, false); + return new ValueOrMarker<>(v, false); } } } diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameter.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameter.java new file mode 100644 index 000000000..544376891 --- /dev/null +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameter.java @@ -0,0 +1,34 @@ +package ca.bc.gov.nrs.vdyp.common_calculators; + +import static ca.bc.gov.nrs.vdyp.math.FloatMath.sqrt; + +/* + * Converts between trees per hectare and quad mean diameter given a base area + */ +public class BaseAreaTreeDensityDiameter { + + private BaseAreaTreeDensityDiameter() { + } + + public static final float PI_40K = 0.78539816E-04f; + + // FT_BD + public static float treesPerHectare(float baseArea, float quadraticMeanDiameter) { + if (baseArea != 0) { + return baseArea / PI_40K / (quadraticMeanDiameter * quadraticMeanDiameter); + } + return 0f; + } + + // FD_BT + public static float quadMeanDiameter(float baseArea, float treesPerHectare) { + if (baseArea > 1e6f || treesPerHectare > 1e6f || Float.isNaN(baseArea) || Float.isNaN(treesPerHectare)) { + return 0f; + } else if (baseArea > 0f && treesPerHectare > 0f) { + return sqrt(baseArea / treesPerHectare / PI_40K); + } + return 0f; + + } + +} diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileResolver.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileResolver.java index 7ba6a629d..a344c04d7 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileResolver.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileResolver.java @@ -2,9 +2,12 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; public interface FileResolver { - InputStream resolve(String filename) throws IOException; + InputStream resolveForInput(String filename) throws IOException; + + OutputStream resolveForOutput(String filename) throws IOException; String toString(String filename) throws IOException; } \ No newline at end of file diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileSystemFileResolver.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileSystemFileResolver.java index 0c5b99c01..67bd56c99 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileSystemFileResolver.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/FileSystemFileResolver.java @@ -1,17 +1,26 @@ package ca.bc.gov.nrs.vdyp.io; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; public class FileSystemFileResolver implements FileResolver { + @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { return new FileInputStream(filename); } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + return new FileOutputStream(filename); + } + @Override public String toString(String filename) throws IOException { return String.format("file:%s", filename); } + } \ No newline at end of file diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ControlFileParser.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ControlFileParser.java index 7fb8cc1c2..ffcc0d614 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ControlFileParser.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ControlFileParser.java @@ -2,7 +2,6 @@ import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; @@ -46,8 +45,8 @@ public class ControlFileParser implements ResourceParser> { public static final int CONTROL_LENGTH_EXTENDED = 120; public static final int CONTROL_LENGTH = 50; - public static final List EXTEND_FLAGS = Arrays.asList("X", ">"); - public static final List COMMENT_FLAGS = Arrays.asList("C"); + public static final List EXTEND_FLAGS = List.of("X", ">"); + public static final List COMMENT_FLAGS = List.of("C"); public static final String COMMENT_MARKER = "!"; private Map identifiers; diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/GenusDefinitionParser.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/GenusDefinitionParser.java index 384be1bd8..6f65209ba 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/GenusDefinitionParser.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/GenusDefinitionParser.java @@ -7,7 +7,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.model.GenusDefinition; @@ -46,7 +45,7 @@ public class GenusDefinitionParser implements ControlMapSubResourceParser parse(InputStream is, Map control) throws IOException, ResourceParseException { - GenusDefinition[] result = new GenusDefinition[num_sp0]; + GenusDefinition[] result = new GenusDefinition[numSp0]; result = lineParser.parse(is, result, (v, r) -> { String alias = (String) v.get("alias"); Optional preference = (Optional) v.get("preference"); @@ -79,9 +78,9 @@ public List parse(InputStream is, Map control) var defn = new GenusDefinition(alias, preference, name); int p = preference.orElse(lineNumber); - if (p > num_sp0) { + if (p > numSp0) { throw new ValueParseException( - Integer.toString(p), String.format("Preference %d is larger than %d", p, num_sp0) + Integer.toString(p), String.format("Preference %d is larger than %d", p, numSp0) ); } if (p < 1) { @@ -120,7 +119,7 @@ public static List getSpecies(final Map control } public static List getSpeciesAliases(final Map controlMap) { - return getSpecies(controlMap).stream().map(GenusDefinition::getAlias).collect(Collectors.toList()); + return getSpecies(controlMap).stream().map(GenusDefinition::getAlias).toList(); } /** @@ -135,6 +134,10 @@ public static GenusDefinition getSpeciesByIndex(final int index, final Map getIndex(final String alias, final Map controlMap) { + return Optional.of(getSpeciesAliases(controlMap).indexOf(alias) + 1).filter(x -> x != 0); + } + @Override public String getControlKey() { return CONTROL_KEY; diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalResourceControlMapModifier.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalResourceControlMapModifier.java index 9c61d125a..a202ee476 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalResourceControlMapModifier.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/OptionalResourceControlMapModifier.java @@ -22,7 +22,7 @@ default void modify(Map control, FileResolver fileResolver) @SuppressWarnings("unchecked") var filename = (Optional) control.get(getControlKey()); if (filename.isPresent()) { - try (InputStream data = fileResolver.resolve(filename.get())) { + try (InputStream data = fileResolver.resolveForInput(filename.get())) { modify(control, data); } } else { diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ResourceControlMapModifier.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ResourceControlMapModifier.java index 06bb77022..baf51f511 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ResourceControlMapModifier.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/parse/ResourceControlMapModifier.java @@ -29,7 +29,7 @@ public interface ResourceControlMapModifier extends ControlMapModifier, KeyedCon default void modify(Map control, FileResolver fileResolver) throws IOException, ResourceParseException { var filename = (String) control.get(getControlKey()); - try (InputStream data = fileResolver.resolve(filename)) { + try (InputStream data = fileResolver.resolveForInput(filename)) { modify(control, data); } } diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriter.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriter.java new file mode 100644 index 000000000..9f7d7be70 --- /dev/null +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriter.java @@ -0,0 +1,275 @@ +package ca.bc.gov.nrs.vdyp.io.write; + +import java.io.Closeable; +import java.io.IOException; +import java.io.OutputStream; +import java.util.AbstractMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.stream.Stream; + +import ca.bc.gov.nrs.vdyp.common.ControlKeys; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; +import ca.bc.gov.nrs.vdyp.io.FileResolver; +import ca.bc.gov.nrs.vdyp.io.parse.GenusDefinitionParser; +import ca.bc.gov.nrs.vdyp.model.FipMode; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.model.VdypUtilizationHolder; + +/** + * Write files to be input into VRI Adjust. + */ +public class VriAdjustInputWriter implements Closeable { + + Map controlMap; + OutputStream polygonFile; + OutputStream speciesFile; + OutputStream utilizationFile; + + static final String POLY_IDENTIFIER_FORMAT = "%-25s"; + static final String LAYER_TYPE_FORMAT = "%-1s"; + static final String SPEC_IDENTIFIER_FORMAT = "%-2s"; + static final String SPEC_INDEX_FORMAT = "%2d"; + static final String SPEC_DIST_FORMAT = "%-3s%5.1f"; + static final String DISTANCE_FORMAT = "%6.2f"; + static final String AGE_FORMAT = "%6.1f"; + + static final float EMPTY_FLOAT = -9f; + static final int EMPTY_INT = -9; + + // FORMAT(A25, 1x,A4,1x,A1,I6, I3, I3 , I3 ) + static final String POLY_FORMAT = POLY_IDENTIFIER_FORMAT + " %-4s %1s%6d%3d%3d%3d\n"; + + // FORMAT(A25,1x,A1,1x,I2,1x,A2,1x,A32,2f6.2,3f6.1,I2,I3) + static final String SPEC_FORMAT = POLY_IDENTIFIER_FORMAT + " " + LAYER_TYPE_FORMAT + " " + SPEC_INDEX_FORMAT + " " + + SPEC_IDENTIFIER_FORMAT + " " + SPEC_DIST_FORMAT.repeat(4) + DISTANCE_FORMAT.repeat(2) + + AGE_FORMAT.repeat(3) + "%2d%3d\n"; + + // 082E004 615 1988 P 0 0 19.97867 1485.82 13.0660 117.9938 67.7539 67.0665 + // 66.8413 65.4214 13.1 + // FORMAT (A25, 1x, A1, 1x, I2, 1x, A2, I3, + // 1 F9.5, F9.2, F9.4, 5F9.4, F6.1) + static final String UTIL_FORMAT = POLY_IDENTIFIER_FORMAT + " " + LAYER_TYPE_FORMAT + " " + SPEC_INDEX_FORMAT + " " + + SPEC_IDENTIFIER_FORMAT + "%3d%9.5f%9.2f%9.4f%9.4f%9.4f%9.4f%9.4f%9.4f%6.1f\n"; + + static final String END_RECORD_FORMAT = POLY_IDENTIFIER_FORMAT + " "; + + /** + * Create a writer for VRI Adjust input files using provided OutputStreams. The + * Streams will be closed when the writer is closed. + * + * @param polygonFile + * @param speciesFile + * @param utilizationFile + * @param controlMap + */ + public VriAdjustInputWriter( + OutputStream polygonFile, OutputStream speciesFile, OutputStream utilizationFile, + Map controlMap + ) { + this.controlMap = controlMap; + this.polygonFile = polygonFile; + this.speciesFile = speciesFile; + this.utilizationFile = utilizationFile; + } + + /** + * Create a writer for VRI Adjust input files configured using the given control + * map. + * + * @param polygonFile + * @param speciesFile + * @param utilizationFile + * @param controlMap + */ + public VriAdjustInputWriter(Map controlMap, FileResolver resolver) throws IOException { + this( + getOutputStream(controlMap, resolver, ControlKeys.VDYP_POLYGON), + getOutputStream(controlMap, resolver, ControlKeys.VDYP_LAYER_BY_SPECIES), + getOutputStream(controlMap, resolver, ControlKeys.VDYP_LAYER_BY_SP0_BY_UTIL), controlMap + ); + } + + static OutputStream getOutputStream(Map controlMap, FileResolver resolver, String key) + throws IOException { + String fileName = Utils.expectParsedControl(controlMap, key, String.class); + return resolver.resolveForOutput(fileName); + } + + // V7W_AIP + /** + * Write a polygon record to the polygon file + * + * @param polygon + * @throws IOException + */ + void writePolygon(VdypPolygon polygon) throws IOException { + writeFormat( + polygonFile, // + POLY_FORMAT, // + + polygon.getPolygonIdentifier(), // + polygon.getBiogeoclimaticZone(), // + polygon.getForestInventoryZone(), // + + polygon.getPercentAvailable().intValue(), // + polygon.getInventoryTypeGroup(), // + polygon.getGrpBa1(), // + polygon.getModeFip().map(FipMode::getCode).orElse(0) + ); + } + + /** + * Write a species record to the species file + * + * @param layer + * @param spec + * @throws IOException + */ + void writeSpecies(VdypLayer layer, VdypSpecies spec) throws IOException { + + // Ensure we have a list of 4 distribution entries + var specDistributionEntries = Stream.concat( + spec.getSpeciesPercent().entrySet().stream().sorted(Utils.compareUsing(Entry::getValue)), + Stream.generate(() -> new AbstractMap.SimpleEntry("", 0f)) + ).limit(4).toList(); + // 082E004 615 1988 P 9 L LW 100.0 0.0 0.0 0.0 -9.00 -9.00 -9.0 -9.0 -9.0 0 -9 + var specIndex = GenusDefinitionParser.getIndex(spec.getGenus(), controlMap); + writeFormat( + speciesFile, // + SPEC_FORMAT, // + + spec.getPolygonIdentifier(), // + spec.getLayer().getAlias(), // + + specIndex.orElse(0), // + spec.getGenus(), // + + specDistributionEntries.get(0).getKey(), // + specDistributionEntries.get(0).getValue(), // + specDistributionEntries.get(1).getKey(), // + specDistributionEntries.get(1).getValue(), // + specDistributionEntries.get(2).getKey(), // + specDistributionEntries.get(2).getValue(), // + specDistributionEntries.get(3).getKey(), // + specDistributionEntries.get(3).getValue(), // + + layer.getSiteIndex().orElse(EMPTY_FLOAT), // + layer.getHeight().orElse(EMPTY_FLOAT), // + layer.getAgeTotal().orElse(EMPTY_FLOAT), // + layer.getBreastHeightAge().orElse(EMPTY_FLOAT), // + layer.getYearsToBreastHeight().orElse(EMPTY_FLOAT), // + layer.getSiteGenus().map(id -> id.equals(spec.getGenus())).orElse(false) ? 1 : 0, // + layer.getSiteCurveNumber().orElse(EMPTY_INT) + + ); + + } + + /** + * Write the utilization records for a layer or species to the utilization file. + * + * @param layer + * @param utils + * @throws IOException + */ + // V7W_AIU Internalized loop over utilization classes + void writeUtilization(VdypLayer layer, VdypUtilizationHolder utils) throws IOException { + Optional specId = Optional.empty(); + if (utils instanceof VdypSpecies spec) { + specId = Optional.of(spec.getGenus()); + } + + Optional specIndex = specId.flatMap(id -> GenusDefinitionParser.getIndex(id, controlMap)); + + for (var uc : UtilizationClass.values()) { + Optional height = Optional.empty(); + if (uc.index < 1) { + height = Optional.of(utils.getLoreyHeightByUtilization().getCoe(uc.index)); + } + Optional quadMeanDiameter = Optional.empty(); + if (utils.getBaseAreaByUtilization().getCoe(uc.index) > 0) { + quadMeanDiameter = Optional.of( + BaseAreaTreeDensityDiameter.quadMeanDiameter( + utils.getBaseAreaByUtilization().getCoe(uc.index), + utils.getTreesPerHectareByUtilization().getCoe(uc.index) + ) + ); + } + + writeFormat( + utilizationFile, // + UTIL_FORMAT, // + + layer.getPolygonIdentifier(), // + layer.getLayer().getAlias(), // + + specIndex.orElse(0), // + specId.orElse(" "), // + + uc.index, + + utils.getBaseAreaByUtilization().getCoe(uc.index), // + utils.getTreesPerHectareByUtilization().getCoe(uc.index), // + height.orElse(EMPTY_FLOAT), // + + utils.getWholeStemVolumeByUtilization().getCoe(uc.index), // + utils.getCloseUtilizationVolumeByUtilization().getCoe(uc.index), // + utils.getCloseUtilizationVolumeNetOfDecayByUtilization().getCoe(uc.index), // + utils.getCloseUtilizationVolumeNetOfDecayAndWasteByUtilization().getCoe(uc.index), // + utils.getCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization().getCoe(uc.index), // + + quadMeanDiameter.orElse(EMPTY_FLOAT) + ); + } + } + + /** + * Output a polygon and its children. + * + * @param polygon + * @throws IOException + */ + // VDYP_OUT when JPROGRAM = 1 (FIPSTART) or 3 (VRISTART) + public void writePolygonWithSpeciesAndUtilization(VdypPolygon polygon) throws IOException { + + writePolygon(polygon); + for (var layer : polygon.getLayers().values()) { + writeUtilization(layer, layer); + for (var species : layer.getSpecies().values()) { + writeSpecies(layer, species); + writeUtilization(layer, species); + } + } + writeSpeciesEndRecord(polygon); + writeUtilizationEndRecord(polygon); + } + + private void writeEndRecord(OutputStream os, VdypPolygon polygon) throws IOException { + writeFormat(os, END_RECORD_FORMAT, polygon.getPolygonIdentifier()); + } + + private void writeUtilizationEndRecord(VdypPolygon polygon) throws IOException { + writeEndRecord(utilizationFile, polygon); + } + + private void writeSpeciesEndRecord(VdypPolygon polygon) throws IOException { + writeEndRecord(speciesFile, polygon); + } + + void writeFormat(OutputStream os, String format, Object... params) throws IOException { + os.write(String.format(format, params).getBytes()); + } + + @Override + public void close() throws IOException { + polygonFile.close(); + speciesFile.close(); + utilizationFile.close(); + } +} diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypLayer.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypLayer.java index ec9d2b21f..1119bfa48 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypLayer.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypLayer.java @@ -6,25 +6,36 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.Consumer; public class BaseVdypLayer { private final String polygonIdentifier; private final LayerType layer; - private float ageTotal; // LVCOM3/AGETOTLV, L1COM3/AGETOTL1 - private float height; // LVCOM3/HDLV, L1COM3/HDL1 - private float yearsToBreastHeight; // LVCOM3/YTBHLV, L1COM3/YTBHL1 + private Optional ageTotal = Optional.empty(); // LVCOM3/AGETOTLV, L1COM3/AGETOTL1 + private Optional height = Optional.empty(); // LVCOM3/HDLV, L1COM3/HDL1 + private Optional yearsToBreastHeight = Optional.empty(); // LVCOM3/YTBHLV, L1COM3/YTBHL1 private LinkedHashMap species = new LinkedHashMap<>(); - - public BaseVdypLayer( - String polygonIdentifier, LayerType layer, float ageTotal, float yearsToBreastHeight, float height + private Optional siteIndex = Optional.empty(); + private Optional siteCurveNumber = Optional.empty(); + private Optional inventoryTypeGroup = Optional.empty(); + private Optional siteGenus = Optional.empty(); // FIPL_1A/SITESP0_L1 + + protected BaseVdypLayer( + String polygonIdentifier, LayerType layer, Optional ageTotal, Optional height, + Optional yearsToBreastHeight, Optional siteIndex, Optional siteCurveNumber, + Optional inventoryTypeGroup, Optional siteGenus ) { + super(); this.polygonIdentifier = polygonIdentifier; this.layer = layer; + this.ageTotal = ageTotal; - this.yearsToBreastHeight = yearsToBreastHeight; this.height = height; + this.yearsToBreastHeight = yearsToBreastHeight; + this.siteIndex = siteIndex; + this.siteCurveNumber = siteCurveNumber; + this.inventoryTypeGroup = inventoryTypeGroup; + this.siteGenus = siteGenus; } public String getPolygonIdentifier() { @@ -35,27 +46,27 @@ public LayerType getLayer() { return layer; } - public float getAgeTotal() { + public Optional getAgeTotal() { return ageTotal; } - public float getHeight() { + public Optional getHeight() { return height; } - public float getYearsToBreastHeight() { + public Optional getYearsToBreastHeight() { return yearsToBreastHeight; } - public void setAgeTotal(float ageTotal) { + public void setAgeTotal(Optional ageTotal) { this.ageTotal = ageTotal; } - public void setHeight(float height) { + public void setHeight(Optional height) { this.height = height; } - public void setYearsToBreastHeight(float yearsToBreastHeight) { + public void setYearsToBreastHeight(Optional yearsToBreastHeight) { this.yearsToBreastHeight = yearsToBreastHeight; } @@ -73,6 +84,38 @@ public void setSpecies(Collection species) { species.forEach(spec -> this.species.put(spec.getGenus(), spec)); } + public Optional getSiteIndex() { + return siteIndex; + } + + public void setSiteIndex(Optional siteIndex) { + this.siteIndex = siteIndex; + } + + public Optional getSiteCurveNumber() { + return siteCurveNumber; + } + + public void setSiteCurveNumber(Optional siteCurveNumber) { + this.siteCurveNumber = siteCurveNumber; + } + + public Optional getInventoryTypeGroup() { + return inventoryTypeGroup; + } + + public void setInventoryTypeGroup(Optional inventoryTypeGroup) { + this.inventoryTypeGroup = inventoryTypeGroup; + } + + public Optional getSiteGenus() { + return siteGenus; + } + + public void setSiteGenus(Optional siteGenus) { + this.siteGenus = siteGenus; + } + protected abstract static class Builder, S extends BaseVdypSpecies> extends ModelClassBuilder { protected Optional polygonIdentifier = Optional.empty(); @@ -80,6 +123,12 @@ protected abstract static class Builder, S extends Ba protected Optional ageTotal = Optional.empty(); protected Optional height = Optional.empty(); protected Optional yearsToBreastHeight = Optional.empty(); + + protected Optional siteIndex = Optional.empty(); + protected Optional siteCurveNumber = Optional.empty(); + protected Optional inventoryTypeGroup = Optional.empty(); + protected Optional siteGenus = Optional.empty(); + protected List species = new LinkedList<>(); public Builder polygonIdentifier(String polygonIdentifier) { @@ -93,17 +142,29 @@ public Builder layerType(LayerType layer) { } public Builder ageTotal(float ageTotal) { - this.ageTotal = Optional.of(ageTotal); + return this.ageTotal(Optional.of(ageTotal)); + } + + public Builder ageTotal(Optional ageTotal) { + this.ageTotal = ageTotal; return this; } public Builder height(float height) { - this.height = Optional.of(height); + return this.height(Optional.of(height)); + } + + public Builder height(Optional height) { + this.height = height; return this; } public Builder yearsToBreastHeight(float yearsToBreastHeight) { - this.yearsToBreastHeight = Optional.of(yearsToBreastHeight); + return this.yearsToBreastHeight(Optional.of(yearsToBreastHeight)); + } + + public Builder yearsToBreastHeight(Optional yearsToBreastHeight) { + this.yearsToBreastHeight = yearsToBreastHeight; return this; } @@ -117,12 +178,52 @@ public Builder addSpecies(Collection spec) { return this; } + public Builder siteIndex(float siteIndex) { + return this.siteIndex(Optional.of(siteIndex)); + } + + public Builder siteCurveNumber(int siteCurveNumber) { + return this.siteCurveNumber(Optional.of(siteCurveNumber)); + } + + public Builder inventoryTypeGroup(int inventoryTypeGroup) { + return this.inventoryTypeGroup(Optional.of(inventoryTypeGroup)); + } + + public Builder siteGenus(String siteGenus) { + return this.siteGenus(Optional.of(siteGenus)); + } + + public Builder siteIndex(Optional siteIndex) { + this.siteIndex = siteIndex; + return this; + } + + public Builder siteCurveNumber(Optional siteCurveNumber) { + this.siteCurveNumber = siteCurveNumber; + return this; + } + + public Builder inventoryTypeGroup(Optional inventoryTypeGroup) { + this.inventoryTypeGroup = inventoryTypeGroup; + return this; + } + + public Builder siteGenus(Optional siteGenus) { + this.siteGenus = siteGenus; + return this; + } + public Builder copy(BaseVdypLayer toCopy) { polygonIdentifier(toCopy.getPolygonIdentifier()); layerType(toCopy.getLayer()); ageTotal(toCopy.getAgeTotal()); - height(toCopy.getHeight()); yearsToBreastHeight(toCopy.getYearsToBreastHeight()); + height(toCopy.getHeight()); + siteIndex(toCopy.getSiteIndex()); + siteCurveNumber(toCopy.getSiteCurveNumber()); + inventoryTypeGroup(toCopy.getInventoryTypeGroup()); + siteGenus(toCopy.getSiteGenus()); return this; } @@ -130,9 +231,6 @@ public Builder copy(BaseVdypLayer toCopy) { protected void check(Collection errors) { requirePresent(polygonIdentifier, "polygonIdentifier", errors); requirePresent(layer, "layer", errors); - requirePresent(ageTotal, "ageTotal", errors); - requirePresent(yearsToBreastHeight, "yearsToBreastHeight", errors); - requirePresent(height, "height", errors); } @Override diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java index ad36a00eb..071d2da86 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/BaseVdypPolygon.java @@ -1,7 +1,6 @@ package ca.bc.gov.nrs.vdyp.model; import java.util.Collection; -import java.util.Collections; import java.util.EnumMap; import java.util.LinkedHashMap; import java.util.LinkedList; @@ -11,17 +10,42 @@ import java.util.function.Consumer; import java.util.function.Function; -public class BaseVdypPolygon, PA> { - - private String polygonIdentifier; // FIP_P/POLYDESC - private PA percentAvailable; // FIP_P2/PCTFLAND - private Map layers = new LinkedHashMap<>(); - - public BaseVdypPolygon(String polygonIdentifier, PA percentAvailable) { +public abstract class BaseVdypPolygon, PA> { + + String polygonIdentifier; // FIP_P/POLYDESC + PA percentAvailable; // FIP_P2/PCTFLAND + Map layers = new LinkedHashMap<>(); + protected String biogeoclimaticZone; + protected String forestInventoryZone; + protected Optional modeFip; + + protected BaseVdypPolygon( + String polygonIdentifier, PA percentAvailable, String fiz, String becIdentifier, Optional modeFip + ) { + super(); + this.forestInventoryZone = fiz; + this.biogeoclimaticZone = becIdentifier; + this.modeFip = modeFip; this.polygonIdentifier = polygonIdentifier; this.percentAvailable = percentAvailable; } + /** + * Copy constructs from the simple attributes of another polygon, but does not + * copy layers. + * + * @param Type of the polygon to copy + * @param Type of percent available in the other polygon + * @param toCopy The polygon to copy + * @param convertPercentAvailable Function to convert + */ + protected , U> BaseVdypPolygon(O toCopy, Function convertPercentAvailable) { + this( + toCopy.getPolygonIdentifier(), convertPercentAvailable.apply(toCopy.getPercentAvailable()), + toCopy.getForestInventoryZone(), toCopy.getBiogeoclimaticZone(), toCopy.getModeFip() + ); + } + public String getPolygonIdentifier() { return polygonIdentifier; } @@ -51,10 +75,38 @@ public void setPercentAvailable(PA percentAvailable) { this.percentAvailable = percentAvailable; } + public String getBiogeoclimaticZone() { + return biogeoclimaticZone; + } + + public void setBiogeoclimaticZone(String biogeoclimaticZone) { + this.biogeoclimaticZone = biogeoclimaticZone; + } + + public String getForestInventoryZone() { + return forestInventoryZone; + } + + public void setForestInventoryZone(String forestInventoryZone) { + this.forestInventoryZone = forestInventoryZone; + } + + public Optional getModeFip() { + return modeFip; + } + + public void setModeFip(Optional modeFip) { + this.modeFip = modeFip; + } + protected abstract static class Builder, L extends BaseVdypLayer, PA> extends ModelClassBuilder { protected Optional polygonIdentifier = Optional.empty(); protected Optional percentAvailable = Optional.empty(); + protected Optional biogeoclimaticZone = Optional.empty(); + protected Optional forestInventoryZone = Optional.empty(); + protected Optional modeFip = Optional.empty(); + protected List layers = new LinkedList<>(); public Builder polygonIdentifier(String polygonIdentifier) { @@ -67,6 +119,25 @@ public Builder percentAvailable(PA pa) { return this; } + public Builder biogeoclimaticZone(String biogeoclimaticZone) { + this.biogeoclimaticZone = Optional.of(biogeoclimaticZone); + return this; + } + + public Builder forestInventoryZone(String forestInventoryZone) { + this.forestInventoryZone = Optional.of(forestInventoryZone); + return this; + } + + public Builder modeFip(Optional modeFip) { + this.modeFip = modeFip; + return this; + } + + public Builder modeFip(FipMode modeFip) { + return modeFip(Optional.of(modeFip)); + } + public Builder addLayer(L layer) { this.layers.add(layer); return this; @@ -90,6 +161,8 @@ public Builder buildLayer(Consumer> specCo public Builder copy(BaseVdypPolygon toCopy, Function paConvert) { polygonIdentifier(toCopy.getPolygonIdentifier()); percentAvailable(paConvert.apply(toCopy.getPercentAvailable())); + biogeoclimaticZone(toCopy.getBiogeoclimaticZone()); + forestInventoryZone(toCopy.getForestInventoryZone()); return this; } @@ -97,6 +170,8 @@ public Builder copy(BaseVdypPolygon toCopy, Function errors) { requirePresent(polygonIdentifier, "polygonIdentifier", errors); requirePresent(percentAvailable, "percentAvailable", errors); + requirePresent(biogeoclimaticZone, "biogeoclimaticZone", errors); + requirePresent(forestInventoryZone, "forestInventoryZone", errors); } @Override diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/Coefficients.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/Coefficients.java index 4c4823b19..a634f2c83 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/Coefficients.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/Coefficients.java @@ -40,6 +40,7 @@ private static float[] listToArray(List coe) { return floatArray; } + @Override public Float get(int i) { return coe[i]; } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipMode.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/FipMode.java similarity index 92% rename from vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipMode.java rename to vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/FipMode.java index 84e51c685..b8c5e078f 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipMode.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/FipMode.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.fip.model; +package ca.bc.gov.nrs.vdyp.model; import java.util.Optional; diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/JProgram.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/JProgram.java new file mode 100644 index 000000000..e6c8ce18b --- /dev/null +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/JProgram.java @@ -0,0 +1,25 @@ +package ca.bc.gov.nrs.vdyp.model; + +public enum JProgram { + FIP_START(true, 1), // 1 + VRI_START(true, 3), // 3 + VDYP(false, 6), // 6 + VDYP_BACK(false, 7), // 7 + VRI_ADJUST(false, 8); // 8 + + JProgram(boolean isStart, int index) { + this.isStart = isStart; + this.index = index; + } + + public boolean isStart() { + return isStart; + } + + public int getIndex() { + return index; + } + + private final boolean isStart; + private final int index; +} diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/LayerType.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/LayerType.java index 8d813da1e..f22c54d4e 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/LayerType.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/LayerType.java @@ -4,13 +4,23 @@ public enum LayerType { /** * The primary layer */ - PRIMARY, + PRIMARY("P"), /** * The parser is aware of this but it is never implemented */ - SECONDARY, + SECONDARY("S"), /** * An older layer than the primary layer, also called the "overstory" */ - VETERAN + VETERAN("V"); + + private final String alias; + + private LayerType(String alias) { + this.alias = alias; + } + + public String getAlias() { + return alias; + } } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/UtilizationClass.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClass.java similarity index 77% rename from vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/UtilizationClass.java rename to vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClass.java index cea798fe2..9e86cb3bb 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/UtilizationClass.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/UtilizationClass.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.fip; +package ca.bc.gov.nrs.vdyp.model; import java.util.Optional; @@ -11,7 +11,7 @@ public enum UtilizationClass { OVER225(4, ">22.5 cm", 22.5f, 10000f); public final int index; - public final String name; + public final String className; public final float lowBound; public final float highBound; @@ -25,18 +25,18 @@ public enum UtilizationClass { } } - UtilizationClass(int index, String name, float lowBound, float highBound) { + UtilizationClass(int index, String className, float lowBound, float highBound) { this.index = index; - this.name = name; + this.className = className; this.lowBound = lowBound; this.highBound = highBound; } - Optional next() { + public Optional next() { return this.next; } - Optional previous() { + public Optional previous() { return this.previous; } } \ No newline at end of file diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java index f0f5ed128..d2b9d81b1 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypLayer.java @@ -1,9 +1,9 @@ package ca.bc.gov.nrs.vdyp.model; +import java.util.Optional; import java.util.function.Consumer; import ca.bc.gov.nrs.vdyp.common.Computed; -import ca.bc.gov.nrs.vdyp.model.VdypSpecies.Builder; public class VdypLayer extends BaseVdypLayer implements VdypUtilizationHolder { @@ -27,15 +27,22 @@ public class VdypLayer extends BaseVdypLayer implements VdypUtiliza Coefficients closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = // VdypUtilizationHolder.emptyUtilization(); // LVCOM/VOL_DWB species 0 + private Optional dominantSpecies; + public VdypLayer( - String polygonIdentifier, LayerType layer, float ageTotal, float yearsToBreastHeight, float height + String polygonIdentifier, LayerType layer, Optional ageTotal, Optional height, + Optional yearsToBreastHeight, Optional siteIndex, Optional siteCurveNumber, + Optional inventoryTypeGroup, Optional siteGenus ) { - super(polygonIdentifier, layer, ageTotal, yearsToBreastHeight, height); + super( + polygonIdentifier, layer, ageTotal, height, yearsToBreastHeight, siteIndex, siteCurveNumber, + inventoryTypeGroup, siteGenus + ); } @Computed - public float getBreastHeightAge() { - return this.getAgeTotal() - this.getYearsToBreastHeight(); + public Optional getBreastHeightAge() { + return this.getAgeTotal().flatMap(at -> this.getYearsToBreastHeight().map(bha -> at - bha)); } @Override @@ -134,6 +141,14 @@ public void setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( this.closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization = closeUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization; } + public Optional getDominantSpecies() { + return dominantSpecies; + } + + public void setDominantSpecies(Optional dominantSpecies) { + this.dominantSpecies = dominantSpecies; + } + /** * Accepts a configuration function that accepts a builder to configure. * @@ -187,9 +202,13 @@ protected VdypLayer doBuild() { return (new VdypLayer( polygonIdentifier.get(), // layer.get(), // - ageTotal.get(), // - yearsToBreastHeight.get(), // - height.get() + ageTotal, // + height, // + yearsToBreastHeight, // + siteIndex, // + siteCurveNumber, // + inventoryTypeGroup, // + siteGenus )); } diff --git a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java index 007285377..391c32ae9 100644 --- a/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java +++ b/vdyp-common/src/main/java/ca/bc/gov/nrs/vdyp/model/VdypPolygon.java @@ -1,11 +1,56 @@ package ca.bc.gov.nrs.vdyp.model; +import java.util.Optional; import java.util.function.Consumer; +import java.util.function.Function; + +import ca.bc.gov.nrs.vdyp.common.Computed; public class VdypPolygon extends BaseVdypPolygon { - public VdypPolygon(String polygonIdentifier, Float percentAvailable) { - super(polygonIdentifier, percentAvailable); + // TODO better name + int grpBa1; + + public VdypPolygon( + String polygonIdentifier, Float percentAvailable, String fiz, String becIdentifier, + Optional modeFip + ) { + super(polygonIdentifier, percentAvailable, fiz, becIdentifier, modeFip); + } + + /** + * Copy constructs from the simple attributes of another polygon, but does not + * copy layers. + * + * @param Type of the polygon to copy + * @param Type of percent available in the other polygon + * @param toCopy The polygon to copy + * @param convertPercentAvailable Function to convert + */ + public , U> VdypPolygon(O toCopy, Function convertPercentAvailable) { + super(toCopy, convertPercentAvailable); + } + + @Computed + public int getInventoryTypeGroup() { + return this.getLayers().get(LayerType.PRIMARY).getInventoryTypeGroup().orElseThrow( + () -> new IllegalArgumentException("Inventory Type Group does not exist if there is no primary layer") + ); + } + + @Computed + public void setInventoryTypeGroup(int itg) { + this.getLayers().get(LayerType.PRIMARY).setInventoryTypeGroup(Optional.of(itg)); + } + + // TODO better name + public int getGrpBa1() { + return grpBa1; + } + + // TODO better name + public void setGrpBa1(int grpBa1) { + this.grpBa1 = grpBa1; } /** @@ -31,12 +76,15 @@ public static VdypPolygon build(Consumer config) { public static class Builder extends BaseVdypPolygon.Builder { + // TODO better name + int grpBa1; + @Override protected VdypPolygon doBuild() { - return (new VdypPolygon( - polygonIdentifier.get(), // - percentAvailable.get() - )); + return new VdypPolygon( + polygonIdentifier.get(), percentAvailable.get(), forestInventoryZone.get(), + biogeoclimaticZone.get(), modeFip + ); } @Override diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameterTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameterTest.java new file mode 100644 index 000000000..52f5c4284 --- /dev/null +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/common_calculators/BaseAreaTreeDensityDiameterTest.java @@ -0,0 +1,65 @@ +package ca.bc.gov.nrs.vdyp.common_calculators; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.closeTo; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.api.Test; + +class BaseAreaTreeDensityDiameterTest { + + @Test + void testTPH() { + var tph = BaseAreaTreeDensityDiameter.treesPerHectare(4f, 20f); + assertThat(tph, closeTo(127.324f)); + } + + @Test + void testTPHWhereBAIsZero() { + var tph = BaseAreaTreeDensityDiameter.treesPerHectare(0f, 4f); + assertThat(tph, is(0f)); + } + + @Test + void testDQ() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(4f, 127.324f); + assertThat(dq, closeTo(20f)); + } + + @Test + void testDQWhereBAIsZero() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(0f, 127.324f); + assertThat(dq, is(0f)); + } + + @Test + void testDQWhereTPHIsZero() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(4f, 0f); + assertThat(dq, is(0f)); + } + + @Test + void testDQWhereBAIsBig() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(10_000_0000f, 127.324f); + assertThat(dq, is(0f)); + } + + @Test + void testDQWhereTPHIsBig() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(4f, 10_000_0000f); + assertThat(dq, is(0f)); + } + + @Test + void testDQWhereBAIsNan() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(Float.NaN, 127.324f); + assertThat(dq, is(0f)); + } + + @Test + void testDQWhereTPHIsNan() { + var dq = BaseAreaTreeDensityDiameter.quadMeanDiameter(4f, Float.NaN); + assertThat(dq, is(0f)); + } + +} diff --git a/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/common/LazyValueTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/common/LazyValueTest.java similarity index 93% rename from vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/common/LazyValueTest.java rename to vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/common/LazyValueTest.java index 17e5d1e75..db4bd214b 100644 --- a/vdyp-core/src/test/java/ca/bc/gov/nrs/vdyp/common/LazyValueTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/common/LazyValueTest.java @@ -1,4 +1,4 @@ -package ca.bc.gov.nrs.vdyp.common; +package ca.bc.gov.nrs.vdyp.io.common; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -8,6 +8,8 @@ import org.easymock.EasyMock; import org.junit.jupiter.api.Test; +import ca.bc.gov.nrs.vdyp.common.LazyValue; + class LazyValueTest { @Test diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BecDefinitionParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BecDefinitionParserTest.java index 13cb48109..782b6efd5 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BecDefinitionParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BecDefinitionParserTest.java @@ -5,22 +5,15 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasProperty; -import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; import static org.junit.jupiter.api.Assertions.assertThrows; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; -import java.util.Map; - import static org.hamcrest.Matchers.equalTo; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import ca.bc.gov.nrs.vdyp.model.BecDefinition; -import ca.bc.gov.nrs.vdyp.model.BecLookup; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.test.TestUtils; @@ -261,7 +254,7 @@ void testParse() throws Exception { @Test void testParseNoDefault() throws Exception { var parser = new BecDefinitionParser(); - try (var is = TestUtils.makeStream("")) { + try (var is = TestUtils.makeInputStream("")) { var ex = assertThrows(IllegalStateException.class, () -> parser.parse(is, Collections.emptyMap())); assertThat(ex, hasProperty("message", Matchers.is("Could not find default BEC ESSF"))); } diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BySpeciesDqCoefficientParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BySpeciesDqCoefficientParserTest.java index f0670e0bf..a667685d3 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BySpeciesDqCoefficientParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/BySpeciesDqCoefficientParserTest.java @@ -20,7 +20,7 @@ void testParseSimple() throws Exception { var parser = new BySpeciesDqCoefficientParser(); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "A0 1 -0.65484 -0.48275 -0.75134 0.04482 -0.31195 -0.53012 -0.12645 -0.64668 -0.43538 -0.31134 -0.03435 -0.27833 -0.32476 0.10819 -0.38103 -0.12273", "A1 2 2.26389 0.19886 -0.25704 0.18579 -0.38547 -0.14115 -0.10146 0.09067 0.54304 -0.02947 0.08473 -0.39934 0.02206 -0.18235 0.01411 -0.21683", "A2 0 0.23162" diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CoefficientParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CoefficientParserTest.java index 91e99a911..58f6bf2ca 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CoefficientParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/CoefficientParserTest.java @@ -20,7 +20,7 @@ void testParseSimple() throws Exception { var parser = new CoefficientParser("TEST"); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "B1 A0 2 2.0028 -0.5343 1.3949 -0.3683 -0.3343 0.5699 0.2314 0.0528 0.2366 -0.3343 0.5076 0.5076 0.6680 -0.1353 1.2445 -0.4507" ); @@ -41,7 +41,7 @@ void testBadBec() throws Exception { var parser = new CoefficientParser("TEST"); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "BX A0 0 2.0028 -0.5343 1.3949 -0.3683 -0.3343 0.5699 0.2314 0.0528 0.2366 -0.3343 0.5076 0.5076 0.6680 -0.1353 1.2445 -0.4507" ); @@ -59,7 +59,7 @@ void testBadIndex() throws Exception { var parser = new CoefficientParser("TEST"); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "B1 AX 0 2.0028 -0.5343 1.3949 -0.3683 -0.3343 0.5699 0.2314 0.0528 0.2366 -0.3343 0.5076 0.5076 0.6680 -0.1353 1.2445 -0.4507" ); @@ -77,7 +77,7 @@ void testParseDelta() throws Exception { var parser = new CoefficientParser("TEST"); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "B1 A0 1 2.0028 -0.5343 1.3949 -0.3683 -0.3343 0.5699 0.2314 0.0528 0.2366 -0.3343 0.5076 0.5076 0.6680 -0.1353 1.2445 -0.4507" ); @@ -99,7 +99,7 @@ void testParseFixed() throws Exception { var parser = new CoefficientParser("TEST"); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( "B1 A0 0 2.0028 -0.5343 1.3949 -0.3683 -0.3343 0.5699 0.2314 0.0528 0.2366 -0.3343 0.5076 0.5076 0.6680 -0.1353 1.2445 -0.4507" ); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ComponentSizeParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ComponentSizeParserTest.java index adb59e796..196a6b3c9 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ComponentSizeParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ComponentSizeParserTest.java @@ -18,7 +18,7 @@ class ComponentSizeParserTest { @Test void testParseSimpleP1() throws Exception { - var is = TestUtils.makeStream("S1 C 49.4 153.3 0.726 3.647"); + var is = TestUtils.makeInputStream("S1 C 49.4 153.3 0.726 3.647"); Map controlMap = new HashMap<>(); @@ -34,7 +34,7 @@ void testParseSimpleP1() throws Exception { @Test void testParseBadSpecies() throws Exception { - var is = TestUtils.makeStream("SX C 49.4 153.3 0.726 3.647"); + var is = TestUtils.makeInputStream("SX C 49.4 153.3 0.726 3.647"); Map controlMap = new HashMap<>(); @@ -50,7 +50,7 @@ void testParseBadSpecies() throws Exception { @Test void testParseBadRegion() throws Exception { - var is = TestUtils.makeStream("S1 X 49.4 153.3 0.726 3.647"); + var is = TestUtils.makeInputStream("S1 X 49.4 153.3 0.726 3.647"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationGroupParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationGroupParserTest.java index 36809031e..455acb382 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationGroupParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationGroupParserTest.java @@ -38,7 +38,7 @@ void testParse() throws Exception { var controlMap = makeControlMapSingle(); String[] lines = { "S1 B1 001" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); var result = parser.parse(is, Collections.unmodifiableMap(controlMap)); assertThat(result, mmHasEntry(is(1), "S1", "B1")); @@ -51,7 +51,7 @@ void testSP0MustExist() throws Exception { var controlMap = makeControlMapSingle(); String[] lines = { "SX B1 001" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); ResourceParseLineException ex1 = Assertions.assertThrows( ResourceParseLineException.class, () -> parser.parse(is, Collections.unmodifiableMap(controlMap)) @@ -69,7 +69,7 @@ void testBecMustExist() throws Exception { var controlMap = makeControlMapSingle(); String[] lines = { "S1 BX 001" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); ResourceParseLineException ex1 = Assertions.assertThrows( ResourceParseLineException.class, () -> parser.parse(is, Collections.unmodifiableMap(controlMap)) @@ -90,7 +90,7 @@ void testParseOvewrite() throws Exception { var controlMap = makeControlMapSingle(); String[] lines = { "S1 B1 001", "S1 B1 002" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); var result = parser.parse(is, Collections.unmodifiableMap(controlMap)); assertThat(result, mmHasEntry(is(2), "S1", "B1")); @@ -104,7 +104,7 @@ void testParseMultiple() throws Exception { String[] lines = { "S1 B1 011", "S1 B2 012", "S1 B3 013", "S1 B4 014", "S2 B1 021", "S2 B2 022", "S2 B3 023", "S2 B4 024" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); var result = parser.parse(is, Collections.unmodifiableMap(controlMap)); assertThat(result, mmHasEntry(is(11), "S1", "B1")); @@ -128,7 +128,7 @@ void testRequireNoMissingSp0() throws Exception { List unusedBecs = Arrays.asList("B2", "B4"); String[] lines = { "S1 B1 011", "S1 B2 012", "S1 B3 013", "S1 B4 014" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); ResourceParseValidException ex1 = assertThrows( ResourceParseValidException.class, () -> parser.parse(is, Collections.unmodifiableMap(controlMap)) @@ -149,7 +149,7 @@ void testRequireNoMissingBec() throws Exception { String[] lines = { "S1 B1 011", "S1 B2 012", "S1 B4 014", "S2 B1 021", "S2 B2 022", "S2 B3 023", "S2 B4 024" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); ResourceParseValidException ex1 = assertThrows( ResourceParseValidException.class, () -> parser.parse(is, Collections.unmodifiableMap(controlMap)) @@ -171,7 +171,7 @@ void testRequireNoUnexpectedBec() throws Exception { String[] lines = { "S1 B1 011", "S1 B2 012", "S1 B4 014", "S2 B1 021", "S2 B2 022", "S2 B3 023", "S2 B4 024" }; - var is = TestUtils.makeStream(lines); + var is = TestUtils.makeInputStream(lines); ResourceParseValidException ex1 = assertThrows( ResourceParseValidException.class, () -> parser.parse(is, Collections.unmodifiableMap(controlMap)) diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationModifierParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationModifierParserTest.java index 37ce9be88..507433cf4 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationModifierParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/EquationModifierParserTest.java @@ -19,7 +19,7 @@ void testParseSimple() throws Exception { var parser = new EquationModifierParser(); - var is = TestUtils.makeStream(" 25 18 26"); + var is = TestUtils.makeInputStream(" 25 18 26"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLCoefficientParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLCoefficientParserTest.java index d771a905c..c04dc2d3b 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLCoefficientParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLCoefficientParserTest.java @@ -21,7 +21,7 @@ void testParseSimpleP1() throws Exception { var parser = new HLCoefficientParser(HLCoefficientParser.NUM_COEFFICIENTS_P1, "TEST"); - var is = TestUtils.makeStream("S1 I 1.00160 0.20508-0.0013743"); + var is = TestUtils.makeInputStream("S1 I 1.00160 0.20508-0.0013743"); Map controlMap = new HashMap<>(); @@ -37,7 +37,7 @@ void testParseSimpleP2() throws Exception { var parser = new HLCoefficientParser(HLCoefficientParser.NUM_COEFFICIENTS_P2, "TEST"); - var is = TestUtils.makeStream("S1 C 0.49722 1.18403"); + var is = TestUtils.makeInputStream("S1 C 0.49722 1.18403"); Map controlMap = new HashMap<>(); @@ -53,7 +53,7 @@ void testParseSimpleP3() throws Exception { var parser = new HLCoefficientParser(HLCoefficientParser.NUM_COEFFICIENTS_P3, "TEST"); - var is = TestUtils.makeStream("S1 I 1.04422 0.93010 -0.05745 -2.50000"); + var is = TestUtils.makeInputStream("S1 I 1.04422 0.93010 -0.05745 -2.50000"); Map controlMap = new HashMap<>(); @@ -71,7 +71,7 @@ void testParseBadSpecies() throws Exception { var parser = new HLCoefficientParser(HLCoefficientParser.NUM_COEFFICIENTS_P1, "TEST"); - var is = TestUtils.makeStream("SX I 1.00160 0.20508-0.0013743"); + var is = TestUtils.makeInputStream("SX I 1.00160 0.20508-0.0013743"); Map controlMap = new HashMap<>(); @@ -86,7 +86,7 @@ void testParseBadRegion() throws Exception { var parser = new HLCoefficientParser(HLCoefficientParser.NUM_COEFFICIENTS_P1, "TEST"); - var is = TestUtils.makeStream("S1 X 1.00160 0.20508-0.0013743"); + var is = TestUtils.makeInputStream("S1 X 1.00160 0.20508-0.0013743"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLNonprimaryCoefficientParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLNonprimaryCoefficientParserTest.java index 207518636..2aaef2991 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLNonprimaryCoefficientParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/HLNonprimaryCoefficientParserTest.java @@ -26,7 +26,7 @@ void testParseSimple() throws Exception { var parser = new HLNonprimaryCoefficientParser(); - var is = TestUtils.makeStream("S1 S2 C 1 0.86323 1.00505"); + var is = TestUtils.makeInputStream("S1 S2 C 1 0.86323 1.00505"); Map controlMap = new HashMap<>(); @@ -42,7 +42,7 @@ void testParseBadSpecies1() throws Exception { var parser = new HLNonprimaryCoefficientParser(); - var is = TestUtils.makeStream("SX S2 C 1 0.86323 1.00505"); + var is = TestUtils.makeInputStream("SX S2 C 1 0.86323 1.00505"); Map controlMap = new HashMap<>(); @@ -57,7 +57,7 @@ void testParseBadSpecies2() throws Exception { var parser = new HLNonprimaryCoefficientParser(); - var is = TestUtils.makeStream("S1 SX C 1 0.86323 1.00505"); + var is = TestUtils.makeInputStream("S1 SX C 1 0.86323 1.00505"); Map controlMap = new HashMap<>(); @@ -72,7 +72,7 @@ void testParseBadRegion() throws Exception { var parser = new HLNonprimaryCoefficientParser(); - var is = TestUtils.makeStream("S1 S2 X 1 0.86323 1.00505"); + var is = TestUtils.makeInputStream("S1 S2 X 1 0.86323 1.00505"); Map controlMap = new HashMap<>(); @@ -87,7 +87,7 @@ void testParseMissingCoefficient() throws Exception { var parser = new HLNonprimaryCoefficientParser(); - var is = TestUtils.makeStream("S1 S2 C 1 0.86323"); + var is = TestUtils.makeInputStream("S1 S2 C 1 0.86323"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParserTest.java index 8e9ccf8db..5d4e88e4e 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveAgeMaximumParserTest.java @@ -24,7 +24,7 @@ public class SiteCurveAgeMaximumParserTest { @Test void testSimple() throws Exception { var parser = new SiteCurveAgeMaximumParser(); - var is = TestUtils.makeStream(" 16 150.0 150.0 20.0 60.0"); + var is = TestUtils.makeInputStream(" 16 150.0 150.0 20.0 60.0"); Map controlMap = new HashMap<>(); @@ -45,7 +45,7 @@ void testSimple() throws Exception { @Test void testDefault() throws Exception { var parser = new SiteCurveAgeMaximumParser(); - var is = TestUtils.makeStream(" -1 150.0 150.0 20.0 60.0"); + var is = TestUtils.makeInputStream(" -1 150.0 150.0 20.0 60.0"); Map controlMap = new HashMap<>(); @@ -66,7 +66,7 @@ void testDefault() throws Exception { @Test void testEndLine() throws Exception { var parser = new SiteCurveAgeMaximumParser(); - var is = TestUtils.makeStream( + var is = TestUtils.makeInputStream( " -1 150.0 150.0 20.0 60.0", "999 End of usuable info", " 42 160.0 145.0 25.0 65.0" ); @@ -92,7 +92,7 @@ void testEndLine() throws Exception { @Test void testDefaultValue() throws Exception { var parser = new SiteCurveAgeMaximumParser(); - var is = TestUtils.makeStream(" 16 150.0 150.0 20.0 60.0"); + var is = TestUtils.makeInputStream(" 16 150.0 150.0 20.0 60.0"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveParserTest.java index 9c2502896..b5dcc3589 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SiteCurveParserTest.java @@ -22,7 +22,7 @@ class SiteCurveParserTest { @Test void testSimple() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream("S1 001002"); + var is = TestUtils.makeInputStream("S1 001002"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); @@ -39,7 +39,7 @@ void testSimple() throws Exception { @Test void testExtraSpecies() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream("S1 001002", "X2 003004"); + var is = TestUtils.makeInputStream("S1 001002", "X2 003004"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); @@ -57,7 +57,7 @@ void testExtraSpecies() throws Exception { @Test void testMissingSpecies() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream("S1 001002"); + var is = TestUtils.makeInputStream("S1 001002"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); @@ -75,7 +75,7 @@ void testMissingSpecies() throws Exception { @Test void testHashComment() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream("# Foo", "S1 001002"); + var is = TestUtils.makeInputStream("# Foo", "S1 001002"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); @@ -92,7 +92,7 @@ void testHashComment() throws Exception { @Test void testSpaceComment() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream(" Foo", "S1 001002"); + var is = TestUtils.makeInputStream(" Foo", "S1 001002"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); @@ -109,7 +109,7 @@ void testSpaceComment() throws Exception { @Test void testEndFileLine() throws Exception { var parser = new SiteCurveParser(); - var is = TestUtils.makeStream("S1 001002", "##", "S2 003004"); + var is = TestUtils.makeInputStream("S1 001002", "##", "S2 003004"); Map controlMap = new HashMap<>(); List sp0List = new ArrayList<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SmallComponentProbabilityParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SmallComponentProbabilityParserTest.java index 2fcc62b66..344c6b19a 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SmallComponentProbabilityParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/SmallComponentProbabilityParserTest.java @@ -20,7 +20,7 @@ class SmallComponentProbabilityParserTest { @Test void testParseSimpleP1() throws Exception { - var is = TestUtils.makeStream("S1 0.48205 0.00000 -0.011862 -0.10014"); + var is = TestUtils.makeInputStream("S1 0.48205 0.00000 -0.011862 -0.10014"); Map controlMap = new HashMap<>(); @@ -35,7 +35,7 @@ void testParseSimpleP1() throws Exception { @Test void testParseBadSpecies() throws Exception { - var is = TestUtils.makeStream("SX 0.48205 0.00000 -0.011862 -0.10014"); + var is = TestUtils.makeInputStream("SX 0.48205 0.00000 -0.011862 -0.10014"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/StockingClassFactorParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/StockingClassFactorParserTest.java index aa0f8ca08..2e72a3ad8 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/StockingClassFactorParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/StockingClassFactorParserTest.java @@ -20,7 +20,7 @@ class StockingClassFactorParserTest { @Test void testEmpty() throws Exception { var parser = new StockingClassFactorParser(); - var is = TestUtils.makeStream(""); + var is = TestUtils.makeInputStream(""); var result = parser.parse(is, Collections.emptyMap()); @@ -30,7 +30,7 @@ void testEmpty() throws Exception { @Test void testSimple() throws Exception { var parser = new StockingClassFactorParser(); - var is = TestUtils.makeStream("R I P 0 1.00 100", "Z Z P 0 1.00 100"); + var is = TestUtils.makeInputStream("R I P 0 1.00 100", "Z Z P 0 1.00 100"); var result = parser.parse(is, Collections.emptyMap()); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UpperCoefficientParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UpperCoefficientParserTest.java index 6061f3d8c..0798c5488 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UpperCoefficientParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UpperCoefficientParserTest.java @@ -23,7 +23,7 @@ void testParseSimple() throws Exception { var parser = new UpperCoefficientParser(); - var is = TestUtils.makeStream("S1 I 2.0028 -0.5343"); + var is = TestUtils.makeInputStream("S1 I 2.0028 -0.5343"); Map controlMap = new HashMap<>(); @@ -44,7 +44,7 @@ void testParseBadSpecies() throws Exception { var parser = new UpperCoefficientParser(); - var is = TestUtils.makeStream("SX I 2.0028 -0.5343"); + var is = TestUtils.makeInputStream("SX I 2.0028 -0.5343"); Map controlMap = new HashMap<>(); @@ -64,7 +64,7 @@ void testParseBadRegion() throws Exception { var parser = new UpperCoefficientParser(); - var is = TestUtils.makeStream("S1 X 2.0028 -0.5343"); + var is = TestUtils.makeInputStream("S1 X 2.0028 -0.5343"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UtilComponentBaseAreaParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UtilComponentBaseAreaParserTest.java index 0aa84e0e8..d20afbaf7 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UtilComponentBaseAreaParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/UtilComponentBaseAreaParserTest.java @@ -19,7 +19,7 @@ void testParseSingleBec() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 S1 B1 -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BA12 S1 B1 -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -46,7 +46,7 @@ void testParseSingleRegion() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 S1 C -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BA12 S1 C -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -73,7 +73,7 @@ void testParseAllBecs() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 S1 -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BA12 S1 -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -101,7 +101,7 @@ void testParseBadSpecies() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 SX B1 -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BA12 SX B1 -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -117,7 +117,7 @@ void testParseBadBec() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 S1 BX -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BA12 S1 BX -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -133,7 +133,7 @@ void testParseBadBau() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BAXX S1 B1 -23.22790 12.60472"); + var is = TestUtils.makeInputStream("BAXX S1 B1 -23.22790 12.60472"); Map controlMap = new HashMap<>(); @@ -149,7 +149,7 @@ void testParseMissingCoefficient() throws Exception { var parser = new UtilComponentBaseAreaParser(); - var is = TestUtils.makeStream("BA12 S1 B1 -23.22790"); + var is = TestUtils.makeInputStream("BA12 S1 B1 -23.22790"); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java index ecc5c701c..f3dd95536 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/ValueParserTest.java @@ -1,8 +1,12 @@ package ca.bc.gov.nrs.vdyp.io.parse; -import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.*; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.hasEntry; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.HashMap; @@ -64,7 +68,7 @@ static enum TestEnum { } @Test - void enumParserTest() throws Exception { + void testEnumParser() throws Exception { var parser = ValueParser.enumParser(TestEnum.class); assertThat(parser.parse("VALUE1"), is(TestEnum.VALUE1)); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/VeteranDQParserTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/VeteranDQParserTest.java index af92c1eeb..4130a96f4 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/VeteranDQParserTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/parse/VeteranDQParserTest.java @@ -20,7 +20,7 @@ void testParseSingleRegion() throws Exception { var parser = new VeteranDQParser(); - var is = TestUtils.makeStream("S1 C 22.500 0.24855 1.46089"); + var is = TestUtils.makeInputStream("S1 C 22.500 0.24855 1.46089"); Map controlMap = new HashMap<>(); @@ -38,7 +38,7 @@ void testParseAllRegions() throws Exception { var parser = new VeteranDQParser(); - var is = TestUtils.makeStream("S1 22.500 0.24855 1.46089"); + var is = TestUtils.makeInputStream("S1 22.500 0.24855 1.46089"); Map controlMap = new HashMap<>(); @@ -56,7 +56,7 @@ void testParseBadSpecies() throws Exception { var parser = new VeteranDQParser(); - var is = TestUtils.makeStream("SX C 22.500 0.24855 1.46089"); + var is = TestUtils.makeInputStream("SX C 22.500 0.24855 1.46089"); Map controlMap = new HashMap<>(); @@ -72,7 +72,7 @@ void testParseMissingCoefficient() throws Exception { var parser = new VeteranDQParser(); - var is = TestUtils.makeStream("S1 C 22.500 0.24855 "); + var is = TestUtils.makeInputStream("S1 C 22.500 0.24855 "); Map controlMap = new HashMap<>(); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriterTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriterTest.java new file mode 100644 index 000000000..b9ac95e60 --- /dev/null +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/io/write/VriAdjustInputWriterTest.java @@ -0,0 +1,466 @@ +package ca.bc.gov.nrs.vdyp.io.write; + +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import ca.bc.gov.nrs.vdyp.common.ControlKeys; +import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.io.FileResolver; +import ca.bc.gov.nrs.vdyp.model.FipMode; +import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.VdypLayer; +import ca.bc.gov.nrs.vdyp.model.VdypPolygon; +import ca.bc.gov.nrs.vdyp.model.VdypSpecies; +import ca.bc.gov.nrs.vdyp.test.TestUtils; +import ca.bc.gov.nrs.vdyp.test.TestUtils.MockOutputStream; +import ca.bc.gov.nrs.vdyp.test.VdypMatchers; + +class VriAdjustInputWriterTest { + + MockOutputStream polyStream; + MockOutputStream specStream; + MockOutputStream utilStream; + + FileResolver fileResolver; + + Map controlMap; + + @BeforeEach + void initStreams() { + controlMap = new HashMap(); + TestUtils.populateControlMapGenusReal(controlMap); + + polyStream = new TestUtils.MockOutputStream("polygons"); + specStream = new TestUtils.MockOutputStream("species"); + utilStream = new TestUtils.MockOutputStream("utilization"); + + controlMap.put(ControlKeys.VDYP_POLYGON, "testPolygonFile"); + controlMap.put(ControlKeys.VDYP_LAYER_BY_SPECIES, "testSpeciesFile"); + controlMap.put(ControlKeys.VDYP_LAYER_BY_SP0_BY_UTIL, "testUtilizationFile"); + + fileResolver = new FileResolver() { + + @Override + public InputStream resolveForInput(String filename) throws IOException { + fail("Should not be attempting to open for reading"); + return null; + } + + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + switch (filename) { + case "testPolygonFile": + return polyStream; + case "testSpeciesFile": + return specStream; + case "testUtilizationFile": + return utilStream; + default: + fail("Unexpected file " + filename + " opened"); + } + return null; + } + + @Override + public String toString(String filename) throws IOException { + return "TEST:" + filename; + } + + }; + + } + + @Test + void testClosesGivenStreams() throws IOException { + + var unit = new VriAdjustInputWriter(polyStream, specStream, utilStream, controlMap); + + unit.close(); + + polyStream.assertClosed(); + specStream.assertClosed(); + utilStream.assertClosed(); + + polyStream.assertContent(emptyString()); + specStream.assertContent(emptyString()); + utilStream.assertContent(emptyString()); + } + + @Test + void testClosesOpenedStreams() throws IOException { + + var unit = new VriAdjustInputWriter(controlMap, fileResolver); + + unit.close(); + + polyStream.assertClosed(); + specStream.assertClosed(); + utilStream.assertClosed(); + + polyStream.assertContent(emptyString()); + specStream.assertContent(emptyString()); + utilStream.assertContent(emptyString()); + } + + @Test + void testWritePolygon() throws IOException { + try (var unit = new VriAdjustInputWriter(controlMap, fileResolver);) { + + VdypPolygon polygon = VdypPolygon.build(builder -> { + + builder.polygonIdentifier("082E004 615 1988"); + builder.percentAvailable(90f); + builder.biogeoclimaticZone("IDF"); + builder.forestInventoryZone("D"); + builder.modeFip(FipMode.FIPSTART); + }); + var layer = VdypLayer.build(polygon, builder -> { + builder.polygonIdentifier("082E004 615 1988"); + builder.layerType(LayerType.PRIMARY); + + builder.height(15f); + builder.siteIndex(14.7f); + builder.ageTotal(60f); + builder.yearsToBreastHeight(8.5f); + builder.siteGenus("PL"); + builder.siteCurveNumber(0); + }); + + // FIXME Add to builder + polygon.setInventoryTypeGroup(28); + polygon.setGrpBa1(119); + + unit.writePolygon(polygon); + } + + polyStream.assertContent(is("082E004 615 1988 IDF D 90 28119 1\n")); + specStream.assertContent(emptyString()); + utilStream.assertContent(emptyString()); + } + + @Test + void testWriteSpecies() throws IOException { + try (var unit = new VriAdjustInputWriter(controlMap, fileResolver);) { + + var layer = VdypLayer.build(builder -> { + builder.polygonIdentifier("082E004 615 1988"); + builder.layerType(LayerType.PRIMARY); + + builder.height(15f); + builder.siteIndex(14.7f); + builder.ageTotal(60f); + builder.yearsToBreastHeight(8.5f); + builder.siteGenus("PL"); + builder.siteCurveNumber(0); + }); + + var species = VdypSpecies.build(layer, builder -> { + builder.genus("PL"); + builder.addSpecies("PL", 100f); + + builder.percentGenus(100f); + builder.volumeGroup(0); + builder.decayGroup(0); + builder.breakageGroup(0); + }); + + unit.writeSpecies(layer, species); + } + specStream.assertContent( + is( + "082E004 615 1988 P 12 PL PL 100.0 0.0 0.0 0.0 14.70 15.00 60.0 51.5 8.5 1 0\n" + ) + ); + polyStream.assertContent(emptyString()); + utilStream.assertContent(emptyString()); + } + + @Test + void testWriteUtilizationForLayer() throws IOException { + try (var unit = new VriAdjustInputWriter(controlMap, fileResolver);) { + + var layer = VdypLayer.build(builder -> { + builder.polygonIdentifier("082E004 615 1988"); + builder.layerType(LayerType.PRIMARY); + + builder.height(15f); + builder.siteIndex(14.7f); + builder.ageTotal(60f); + builder.yearsToBreastHeight(8.5f); + builder.siteGenus("PL"); + builder.siteCurveNumber(0); + }); + + var species = VdypSpecies.build(layer, builder -> { + builder.genus("PL"); + builder.addSpecies("PL", 100f); + + builder.percentGenus(100f); + builder.volumeGroup(0); + builder.decayGroup(0); + builder.breakageGroup(0); + }); + + layer.setBaseAreaByUtilization( + Utils.utilizationVector(0.02865f, 19.97867f, 6.79731f, 8.54690f, 3.63577f, 0.99869f) + ); + layer.setTreesPerHectareByUtilization( + Utils.utilizationVector(9.29f, 1485.82f, 834.25f, 509.09f, 123.56f, 18.92f) + ); + layer.setLoreyHeightByUtilization(Utils.heightVector(7.8377f, 13.0660f)); + + layer.setWholeStemVolumeByUtilization( + Utils.utilizationVector(0.1077f, 117.9938f, 33.3680f, 52.4308f, 25.2296f, 6.9654f) + ); + layer.setCloseUtilizationVolumeByUtilization( + Utils.utilizationVector(0f, 67.7539f, 2.4174f, 36.8751f, 22.0156f, 6.4459f) + ); + layer.setCloseUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0f, 67.0665f, 2.3990f, 36.5664f, 21.7930f, 6.3080f) + ); + layer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0f, 66.8413f, 2.3951f, 36.4803f, 21.7218f, 6.2442f) + ); + layer.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0f, 65.4214f, 2.3464f, 35.7128f, 21.2592f, 6.1030f) + ); + + layer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f)); // Should be + // ignored + // and + // computed + // from + // BA and + // TPH + + unit.writeUtilization(layer, layer); + } + utilStream.assertContent( + VdypMatchers.hasLines( + "082E004 615 1988 P 0 -1 0.02865 9.29 7.8377 0.1077 0.0000 0.0000 0.0000 0.0000 6.3", + "082E004 615 1988 P 0 0 19.97867 1485.82 13.0660 117.9938 67.7539 67.0665 66.8413 65.4214 13.1", + "082E004 615 1988 P 0 1 6.79731 834.25 -9.0000 33.3680 2.4174 2.3990 2.3951 2.3464 10.2", + "082E004 615 1988 P 0 2 8.54690 509.09 -9.0000 52.4308 36.8751 36.5664 36.4803 35.7128 14.6", + "082E004 615 1988 P 0 3 3.63577 123.56 -9.0000 25.2296 22.0156 21.7930 21.7218 21.2592 19.4", + "082E004 615 1988 P 0 4 0.99869 18.92 -9.0000 6.9654 6.4459 6.3080 6.2442 6.1030 25.9" + ) + ); + polyStream.assertContent(emptyString()); + specStream.assertContent(emptyString()); + } + + @Test + void testWriteUtilizationZeroBaseArea() throws IOException { + try (var unit = new VriAdjustInputWriter(controlMap, fileResolver);) { + + var layer = VdypLayer.build(builder -> { + builder.polygonIdentifier("082E004 615 1988"); + builder.layerType(LayerType.PRIMARY); + + builder.height(15f); + builder.siteIndex(14.7f); + builder.ageTotal(60f); + builder.yearsToBreastHeight(8.5f); + builder.siteGenus("PL"); + builder.siteCurveNumber(0); + }); + + var species = VdypSpecies.build(layer, builder -> { + builder.genus("PL"); + builder.addSpecies("PL", 100f); + + builder.percentGenus(100f); + builder.volumeGroup(0); + builder.decayGroup(0); + builder.breakageGroup(0); + }); + + species.setBaseAreaByUtilization( + Utils.utilizationVector(0.02865f, 19.97867f, 6.79731f, 8.54690f, 3.63577f, 0f) + ); + species.setTreesPerHectareByUtilization( + Utils.utilizationVector(9.29f, 1485.82f, 834.25f, 509.09f, 123.56f, 18.92f) + ); + species.setLoreyHeightByUtilization(Utils.heightVector(7.8377f, 13.0660f)); + + species.setWholeStemVolumeByUtilization( + Utils.utilizationVector(0.1077f, 117.9938f, 33.3680f, 52.4308f, 25.2296f, 6.9654f) + ); + species.setCloseUtilizationVolumeByUtilization( + Utils.utilizationVector(0f, 67.7539f, 2.4174f, 36.8751f, 22.0156f, 6.4459f) + ); + species.setCloseUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0f, 67.0665f, 2.3990f, 36.5664f, 21.7930f, 6.3080f) + ); + species.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0f, 66.8413f, 2.3951f, 36.4803f, 21.7218f, 6.2442f) + ); + species.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0f, 65.4214f, 2.3464f, 35.7128f, 21.2592f, 6.1030f) + ); + + species.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f)); // Should be + // ignored + // and + // computed + // from + // BA and + // TPH + + unit.writeUtilization(layer, species); + } + utilStream.assertContent( + VdypMatchers.hasLines( + "082E004 615 1988 P 12 PL -1 0.02865 9.29 7.8377 0.1077 0.0000 0.0000 0.0000 0.0000 6.3", + "082E004 615 1988 P 12 PL 0 19.97867 1485.82 13.0660 117.9938 67.7539 67.0665 66.8413 65.4214 13.1", + "082E004 615 1988 P 12 PL 1 6.79731 834.25 -9.0000 33.3680 2.4174 2.3990 2.3951 2.3464 10.2", + "082E004 615 1988 P 12 PL 2 8.54690 509.09 -9.0000 52.4308 36.8751 36.5664 36.4803 35.7128 14.6", + "082E004 615 1988 P 12 PL 3 3.63577 123.56 -9.0000 25.2296 22.0156 21.7930 21.7218 21.2592 19.4", + "082E004 615 1988 P 12 PL 4 0.00000 18.92 -9.0000 6.9654 6.4459 6.3080 6.2442 6.1030 -9.0" // DQ + // should + // be + // -9 + ) + ); + polyStream.assertContent(emptyString()); + specStream.assertContent(emptyString()); + } + + @Test + void testWritePolygonWithChildren() throws IOException { + try (var unit = new VriAdjustInputWriter(controlMap, fileResolver);) { + + VdypPolygon polygon = VdypPolygon.build(builder -> { + + builder.polygonIdentifier("082E004 615 1988"); + builder.percentAvailable(90f); + builder.biogeoclimaticZone("IDF"); + builder.forestInventoryZone("D"); + builder.modeFip(FipMode.FIPSTART); + }); + + var layer = VdypLayer.build(polygon, builder -> { + builder.layerType(LayerType.PRIMARY); + + builder.height(15f); + builder.siteIndex(14.7f); + builder.ageTotal(60f); + builder.yearsToBreastHeight(8.5f); + builder.siteGenus("PL"); + builder.siteCurveNumber(0); + }); + + var species = VdypSpecies.build(layer, builder -> { + builder.genus("PL"); + builder.addSpecies("PL", 100f); + + builder.percentGenus(100f); + builder.volumeGroup(0); + builder.decayGroup(0); + builder.breakageGroup(0); + }); + + polygon.setInventoryTypeGroup(28); + polygon.setGrpBa1(119); + + layer.setBaseAreaByUtilization( + Utils.utilizationVector(0.02865f, 19.97867f, 6.79731f, 8.54690f, 3.63577f, 0.99869f) + ); + layer.setTreesPerHectareByUtilization( + Utils.utilizationVector(9.29f, 1485.82f, 834.25f, 509.09f, 123.56f, 18.92f) + ); + layer.setLoreyHeightByUtilization(Utils.heightVector(7.8377f, 13.0660f)); + + layer.setWholeStemVolumeByUtilization( + Utils.utilizationVector(0.1077f, 117.9938f, 33.3680f, 52.4308f, 25.2296f, 6.9654f) + ); + layer.setCloseUtilizationVolumeByUtilization( + Utils.utilizationVector(0f, 67.7539f, 2.4174f, 36.8751f, 22.0156f, 6.4459f) + ); + layer.setCloseUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0f, 67.0665f, 2.3990f, 36.5664f, 21.7930f, 6.3080f) + ); + layer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0f, 66.8413f, 2.3951f, 36.4803f, 21.7218f, 6.2442f) + ); + layer.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0f, 65.4214f, 2.3464f, 35.7128f, 21.2592f, 6.1030f) + ); + + layer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f)); // Should be + // ignored + // and + // computed + // from + // BA and + // TPH + + species.setBaseAreaByUtilization( + Utils.utilizationVector(0.02865f, 19.97867f, 6.79731f, 8.54690f, 3.63577f, 0f) + ); + species.setTreesPerHectareByUtilization( + Utils.utilizationVector(9.29f, 1485.82f, 834.25f, 509.09f, 123.56f, 18.92f) + ); + species.setLoreyHeightByUtilization(Utils.heightVector(7.8377f, 13.0660f)); + + species.setWholeStemVolumeByUtilization( + Utils.utilizationVector(0.1077f, 117.9938f, 33.3680f, 52.4308f, 25.2296f, 6.9654f) + ); + species.setCloseUtilizationVolumeByUtilization( + Utils.utilizationVector(0f, 67.7539f, 2.4174f, 36.8751f, 22.0156f, 6.4459f) + ); + species.setCloseUtilizationVolumeNetOfDecayByUtilization( + Utils.utilizationVector(0f, 67.0665f, 2.3990f, 36.5664f, 21.7930f, 6.3080f) + ); + species.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( + Utils.utilizationVector(0f, 66.8413f, 2.3951f, 36.4803f, 21.7218f, 6.2442f) + ); + species.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( + Utils.utilizationVector(0f, 65.4214f, 2.3464f, 35.7128f, 21.2592f, 6.1030f) + ); + + species.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(4f, 4f, 4f, 4f, 4f, 4f)); // Should be + // ignored + // and + // computed + // from + // BA and + // TPH + + unit.writePolygonWithSpeciesAndUtilization(polygon); + } + polyStream.assertContent(is("082E004 615 1988 IDF D 90 28119 1\n")); + utilStream.assertContent( + VdypMatchers.hasLines( + "082E004 615 1988 P 0 -1 0.02865 9.29 7.8377 0.1077 0.0000 0.0000 0.0000 0.0000 6.3", + "082E004 615 1988 P 0 0 19.97867 1485.82 13.0660 117.9938 67.7539 67.0665 66.8413 65.4214 13.1", + "082E004 615 1988 P 0 1 6.79731 834.25 -9.0000 33.3680 2.4174 2.3990 2.3951 2.3464 10.2", + "082E004 615 1988 P 0 2 8.54690 509.09 -9.0000 52.4308 36.8751 36.5664 36.4803 35.7128 14.6", + "082E004 615 1988 P 0 3 3.63577 123.56 -9.0000 25.2296 22.0156 21.7930 21.7218 21.2592 19.4", + "082E004 615 1988 P 0 4 0.99869 18.92 -9.0000 6.9654 6.4459 6.3080 6.2442 6.1030 25.9", + "082E004 615 1988 P 12 PL -1 0.02865 9.29 7.8377 0.1077 0.0000 0.0000 0.0000 0.0000 6.3", + "082E004 615 1988 P 12 PL 0 19.97867 1485.82 13.0660 117.9938 67.7539 67.0665 66.8413 65.4214 13.1", + "082E004 615 1988 P 12 PL 1 6.79731 834.25 -9.0000 33.3680 2.4174 2.3990 2.3951 2.3464 10.2", + "082E004 615 1988 P 12 PL 2 8.54690 509.09 -9.0000 52.4308 36.8751 36.5664 36.4803 35.7128 14.6", + "082E004 615 1988 P 12 PL 3 3.63577 123.56 -9.0000 25.2296 22.0156 21.7930 21.7218 21.2592 19.4", + "082E004 615 1988 P 12 PL 4 0.00000 18.92 -9.0000 6.9654 6.4459 6.3080 6.2442 6.1030 -9.0", + "082E004 615 1988 " + ) + ); + specStream.assertContent( + VdypMatchers.hasLines( + "082E004 615 1988 P 12 PL PL 100.0 0.0 0.0 0.0 14.70 15.00 60.0 51.5 8.5 1 0", + "082E004 615 1988 " + ) + ); + } +} diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/FipModeTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/FipModeTest.java new file mode 100644 index 000000000..f0a7917af --- /dev/null +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/FipModeTest.java @@ -0,0 +1,35 @@ +package ca.bc.gov.nrs.vdyp.model; + +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.notPresent; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; + +class FipModeTest { + + @ParameterizedTest() + @EnumSource(FipMode.class) + void testGetByCodeExpected(FipMode mode) { + var result = FipMode.getByCode(mode.getCode()); + assertThat(result, present(is(mode))); + } + + @ParameterizedTest() + @ValueSource(ints = { -2, Integer.MIN_VALUE, Integer.MAX_VALUE, 42 }) + void testGetByCodeUnexpected(int code) { + var result = FipMode.getByCode(code); + assertThat(result, present(is(FipMode.DONT_PROCESS))); + } + + @ParameterizedTest() + @ValueSource(ints = { 0 }) + void testGetByCodeMissing(int code) { + var result = FipMode.getByCode(code); + assertThat(result, notPresent()); + } + +} diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java index cab5bf1f4..042e5b68d 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypLayerTest.java @@ -1,5 +1,6 @@ package ca.bc.gov.nrs.vdyp.model; +import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.present; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -20,9 +21,9 @@ void build() throws Exception { }); assertThat(result, hasProperty("polygonIdentifier", is("Test"))); assertThat(result, hasProperty("layer", is(LayerType.PRIMARY))); - assertThat(result, hasProperty("ageTotal", is(42f))); - assertThat(result, hasProperty("yearsToBreastHeight", is(2f))); - assertThat(result, hasProperty("height", is(10f))); + assertThat(result, hasProperty("ageTotal", present(is(42f)))); + assertThat(result, hasProperty("yearsToBreastHeight", present(is(2f)))); + assertThat(result, hasProperty("height", present(is(10f)))); assertThat(result, hasProperty("species", anEmptyMap())); } @@ -30,17 +31,7 @@ void build() throws Exception { void buildNoProperties() throws Exception { var ex = assertThrows(IllegalStateException.class, () -> VdypLayer.build(builder -> { })); - assertThat( - ex, - hasProperty( - "message", - allOf( - containsString("polygonIdentifier"), containsString("layer"), - containsString("ageTotal"), containsString("yearsToBreastHeight"), - containsString("height") - ) - ) - ); + assertThat(ex, hasProperty("message", allOf(containsString("polygonIdentifier"), containsString("layer")))); } @Test @@ -49,6 +40,9 @@ void buildForPolygon() throws Exception { var poly = VdypPolygon.build(builder -> { builder.polygonIdentifier("Test"); builder.percentAvailable(50f); + + builder.forestInventoryZone("?"); + builder.biogeoclimaticZone("?"); }); var result = VdypLayer.build(poly, builder -> { @@ -60,9 +54,9 @@ void buildForPolygon() throws Exception { assertThat(result, hasProperty("polygonIdentifier", is("Test"))); assertThat(result, hasProperty("layer", is(LayerType.PRIMARY))); - assertThat(result, hasProperty("ageTotal", is(42f))); - assertThat(result, hasProperty("yearsToBreastHeight", is(2f))); - assertThat(result, hasProperty("height", is(10f))); + assertThat(result, hasProperty("ageTotal", present(is(42f)))); + assertThat(result, hasProperty("yearsToBreastHeight", present(is(2f)))); + assertThat(result, hasProperty("height", present(is(10f)))); assertThat(result, hasProperty("species", anEmptyMap())); assertThat(poly.getLayers(), hasEntry(LayerType.PRIMARY, result)); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypPolygonTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypPolygonTest.java index 888c8ff31..2882a1e40 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypPolygonTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypPolygonTest.java @@ -5,7 +5,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import org.easymock.EasyMock; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; class VdypPolygonTest { @@ -15,6 +14,9 @@ void build() throws Exception { var result = VdypPolygon.build(builder -> { builder.polygonIdentifier("Test"); builder.percentAvailable(90f); + + builder.forestInventoryZone("?"); + builder.biogeoclimaticZone("?"); }); assertThat(result, hasProperty("polygonIdentifier", is("Test"))); assertThat(result, hasProperty("percentAvailable", is(90f))); @@ -39,6 +41,10 @@ void buildAddLayer() throws Exception { var result = VdypPolygon.build(builder -> { builder.polygonIdentifier("Test"); builder.percentAvailable(90f); + + builder.forestInventoryZone("?"); + builder.biogeoclimaticZone("?"); + builder.addLayer(mock); }); assertThat(result, hasProperty("polygonIdentifier", is("Test"))); @@ -51,6 +57,10 @@ void buildBuildLayer() throws Exception { var result = VdypPolygon.build(builder -> { builder.polygonIdentifier("Test"); builder.percentAvailable(90f); + + builder.forestInventoryZone("?"); + builder.biogeoclimaticZone("?"); + builder.buildLayer(layerBuilder -> { layerBuilder.layerType(LayerType.PRIMARY); layerBuilder.ageTotal(10f); diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java index 4ac1dae45..d4097dde1 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/model/VdypSpeciesTest.java @@ -4,7 +4,6 @@ import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertThrows; -import org.easymock.EasyMock; import org.junit.jupiter.api.Test; class VdypSpeciesTest { diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java index c6d001615..b3570db97 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/TestUtils.java @@ -1,10 +1,14 @@ package ca.bc.gov.nrs.vdyp.test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -17,6 +21,8 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.hamcrest.Matcher; + import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.BreakageEquationGroupParser; @@ -46,10 +52,59 @@ public class TestUtils { /** * Create a stream returning the given sequence of lines. */ - public static InputStream makeStream(String... lines) { + public static InputStream makeInputStream(String... lines) { return new ByteArrayInputStream(String.join("\r\n", lines).getBytes()); } + public static class MockOutputStream extends OutputStream { + private boolean isOpen = true; + private ByteArrayOutputStream realStream; + private String streamName; + + public MockOutputStream(String streamName) { + this.realStream = new ByteArrayOutputStream(); + this.streamName = streamName; + } + + @Override + public void write(int b) throws IOException { + if (isOpen) { + realStream.write(b); + } else { + fail("Attempt to write to closed stream"); + } + } + + @Override + public void close() throws IOException { + isOpen = false; + } + + @Override + public String toString() { + return realStream.toString(); + } + + public boolean isOpen() { + return isOpen; + } + + public void assertClosed() { + assertTrue(!isOpen, "stream " + streamName + " was not closed"); + } + + public void assertContent(Matcher matcher) { + assertThat("Stream " + streamName + "contents", toString(), matcher); + } + } + + /** + * Read an output streams contents as a string.. + */ + public static String readOutputStream(ByteArrayOutputStream os) { + return os.toString(); + } + /** * Return a mock file resolver that expects to resolve one file with the given * name and returns the given input stream. @@ -62,7 +117,7 @@ public static FileResolver fileResolver(String expectedFilename, InputStream is) return new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { if (filename.equals(expectedFilename)) { return is; } else { @@ -76,6 +131,12 @@ public String toString(String filename) throws IOException { return "TEST:" + filename; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Unexpected attempt to open expectedFileName for output"); + return null; + } + }; } diff --git a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java index 29c5fe777..6e1d7f93c 100644 --- a/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java +++ b/vdyp-common/src/test/java/ca/bc/gov/nrs/vdyp/test/VdypMatchers.java @@ -507,4 +507,33 @@ public static Matcher closeTo(float expected) { float epsilon = Float.max(EPSILON * expected, Float.MIN_VALUE); return asFloat(Matchers.closeTo(expected, epsilon)); } + + public static Matcher hasLines(String... expectedLines) { + return hasLines(Matchers.contains(expectedLines)); + } + + public static Matcher hasLines(Matcher> lineMatcher) { + return new TypeSafeDiagnosingMatcher() { + + @Override + public void describeTo(Description description) { + description.appendText("A string with lines that "); + lineMatcher.describeTo(description); + } + + @Override + protected boolean matchesSafely(String item, Description mismatchDescription) { + var lines = List.of(item.split("\n")); + + if (lineMatcher.matches(lines)) { + return true; + } else { + lineMatcher.describeMismatch(lines, mismatchDescription); + return false; + } + } + + }; + + } } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipControlParser.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipControlParser.java index 604aa134a..11684d0b2 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipControlParser.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipControlParser.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -9,6 +10,7 @@ import java.util.List; import java.util.Map; +import ca.bc.gov.nrs.vdyp.common.ControlKeys; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.BecDefinitionParser; import ca.bc.gov.nrs.vdyp.io.parse.BreakageEquationGroupParser; @@ -42,6 +44,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.VolumeEquationGroupParser; import ca.bc.gov.nrs.vdyp.io.parse.VolumeNetDecayParser; import ca.bc.gov.nrs.vdyp.io.parse.VolumeNetDecayWasteParser; +import ca.bc.gov.nrs.vdyp.model.JProgram; import ca.bc.gov.nrs.vdyp.io.parse.EquationModifierParser; import ca.bc.gov.nrs.vdyp.io.parse.HLCoefficientParser; import ca.bc.gov.nrs.vdyp.io.parse.HLNonprimaryCoefficientParser; @@ -58,9 +61,6 @@ public class FipControlParser { public static final String FIP_YIELD_POLY_INPUT = FipPolygonParser.CONTROL_KEY; public static final String FIP_YIELD_LAYER_INPUT = FipLayerParser.CONTROL_KEY; public static final String FIP_YIELD_LX_SP0_INPUT = FipSpeciesParser.CONTROL_KEY; - public static final String VDYP_POLYGON = "VDYP_POLYGON"; - public static final String VDYP_LAYER_BY_SPECIES = "VDYP_LAYER_BY_SPECIES"; - public static final String VDYP_LAYER_BY_SP0_BY_UTIL = "VDYP_LAYER_BY_SP0_BY_UTIL"; public static final String VOLUME_EQN_GROUPS = VolumeEquationGroupParser.CONTROL_KEY; public static final String DECAY_GROUPS = DecayEquationGroupParser.CONTROL_KEY; public static final String BREAKAGE_GROUPS = BreakageEquationGroupParser.CONTROL_KEY; @@ -119,9 +119,9 @@ public class FipControlParser { .record(12, FIP_YIELD_LAYER_INPUT, FILENAME) // GET_FIPL .record(13, FIP_YIELD_LX_SP0_INPUT, FILENAME) // GET_FIPS - .record(15, VDYP_POLYGON, FILENAME) // - .record(16, VDYP_LAYER_BY_SPECIES, FILENAME) // - .record(18, VDYP_LAYER_BY_SP0_BY_UTIL, FILENAME) // + .record(15, ControlKeys.VDYP_POLYGON, FILENAME) // + .record(16, ControlKeys.VDYP_LAYER_BY_SPECIES, FILENAME) // + .record(18, ControlKeys.VDYP_LAYER_BY_SP0_BY_UTIL, FILENAME) // .record(20, VOLUME_EQN_GROUPS, FILENAME) // RD_VGRP .record(21, DECAY_GROUPS, FILENAME) // RD_DGRP @@ -188,7 +188,7 @@ public class FipControlParser { ; - int jprogram = 1; // FIPSTART only TODO Track this down + JProgram jprogram = JProgram.FIP_START; // FIPSTART only TODO Track this down public FipControlParser() { @@ -200,10 +200,15 @@ public FipControlParser() { return parse(is, new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { return Files.newInputStream(inputFile.resolveSibling(filename)); } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + return Files.newOutputStream(inputFile.resolveSibling(filename)); + } + @Override public String toString(String filename) throws IOException { return inputFile.resolveSibling(filename).toString(); @@ -212,31 +217,6 @@ public String toString(String filename) throws IOException { } } - Map parse(Class klazz, String resourceName) throws IOException, ResourceParseException { - try (var is = klazz.getResourceAsStream(resourceName)) { - - return parse(is, fileResolver(klazz)); - } - } - - static FileResolver fileResolver(Class klazz) { - return new FileResolver() { - - @Override - public InputStream resolve(String filename) throws IOException { - InputStream resourceAsStream = klazz.getResourceAsStream(filename); - if (resourceAsStream == null) - throw new IOException("Could not load " + filename); - return resourceAsStream; - } - - @Override - public String toString(String filename) throws IOException { - return klazz.getResource(filename).toString(); - } - }; - } - List DATA_FILES = Arrays.asList( // V7O_FIP @@ -416,7 +396,7 @@ public Map parse(InputStream is, FileResolver fileResolver) applyModifiers(map, DATA_FILES, fileResolver); - if (jprogram == 1) { + if (jprogram == JProgram.FIP_START) { applyModifiers(map, FIPSTART_ONLY, fileResolver); } @@ -427,7 +407,7 @@ public Map parse(InputStream is, FileResolver fileResolver) applyModifiers(map, COEFFICIENTS, fileResolver); // Initiation items NOT for FIPSTART - if (jprogram > 1) { + if (jprogram != JProgram.FIP_START) { throw new UnsupportedOperationException(); // RD_E106 diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java index 0541e6204..bacefb427 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParser.java @@ -73,13 +73,13 @@ public String getControlKey() { .value(6, BREAST_HEIGHT_AGE, ValueParser.optional(ValueParser.FLOAT)) // .value(3, SITE_CURVE_NUMBER, ValueParser.optional(ValueParser.INTEGER)); - var is = fileResolver.resolve(fileName); + var is = fileResolver.resolveForInput(fileName); var delegateStream = new AbstractStreamingParser, EndOfRecord>>( is, lineParser, control ) { - @SuppressWarnings({ "unchecked" }) + @SuppressWarnings({ "unchecked", "deprecation" }) @Override protected ValueOrMarker, EndOfRecord> convert(Map entry) { var polygonId = (String) entry.get(FipPolygonParser.POLYGON_IDENTIFIER); @@ -148,7 +148,7 @@ protected boolean skip(ValueOrMarker, EndOfRecord> nextChild) return nextChild.getValue().map(x -> x.map(layer -> { // TODO log this // If the layer is present but has height or closure that's not positive, ignore - return layer.getHeight() <= 0f || layer.getCrownClosure() <= 0f; + return layer.getHeightSafe() <= 0f || layer.getCrownClosure() <= 0f; }).orElse(true)) // If the layer is not present (Unknown layer type) ignore .orElse(false); // If it's a marker, let it through so the stop method can see it. } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParser.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParser.java index dd040cdc0..536b03927 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParser.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParser.java @@ -4,7 +4,6 @@ import java.util.Map; import java.util.Optional; -import ca.bc.gov.nrs.vdyp.fip.model.FipMode; import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon; import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.AbstractStreamingParser; @@ -13,6 +12,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.ResourceParseException; import ca.bc.gov.nrs.vdyp.io.parse.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.io.parse.ValueParser; +import ca.bc.gov.nrs.vdyp.model.FipMode; public class FipPolygonParser implements ControlMapValueReplacer, String> { @@ -43,7 +43,7 @@ public String getControlKey() { .value(5, NONPRODUCTIVE_DESCRIPTION, ValueParser.optional(ValueParser.STRING)) .value(5, YIELD_FACTOR, ValueParser.optional(ValueParser.FLOAT)); - var is = fileResolver.resolve(fileName); + var is = fileResolver.resolveForInput(fileName); return new AbstractStreamingParser(is, lineParser, control) { diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParser.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParser.java index 7f1904bbf..ec4b6bf9b 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParser.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParser.java @@ -65,7 +65,7 @@ public String getControlKey() { .value(3, SPECIES_4, ControlledValueParser.optional(ValueParser.SPECIES)) .value(5, PERCENT_SPECIES_4, ValueParser.PERCENTAGE); - var is = fileResolver.resolve(fileName); + var is = fileResolver.resolveForInput(fileName); var delegateStream = new AbstractStreamingParser, EndOfRecord>>( is, lineParser, control diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java index db3a09116..baa549804 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/FipStart.java @@ -52,9 +52,9 @@ import ca.bc.gov.nrs.vdyp.common.IndexedFloatBinaryOperator; import ca.bc.gov.nrs.vdyp.common.Utils; +import ca.bc.gov.nrs.vdyp.common_calculators.BaseAreaTreeDensityDiameter; import ca.bc.gov.nrs.vdyp.fip.model.FipLayer; import ca.bc.gov.nrs.vdyp.fip.model.FipLayerPrimary; -import ca.bc.gov.nrs.vdyp.fip.model.FipMode; import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon; import ca.bc.gov.nrs.vdyp.fip.model.FipSpecies; import ca.bc.gov.nrs.vdyp.io.FileResolver; @@ -94,12 +94,15 @@ import ca.bc.gov.nrs.vdyp.io.parse.VolumeNetDecayWasteParser; import ca.bc.gov.nrs.vdyp.model.BecDefinition; import ca.bc.gov.nrs.vdyp.model.Coefficients; +import ca.bc.gov.nrs.vdyp.model.FipMode; import ca.bc.gov.nrs.vdyp.model.LayerType; +import ca.bc.gov.nrs.vdyp.model.JProgram; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap3; import ca.bc.gov.nrs.vdyp.model.NonprimaryHLCoefficients; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.StockingClassFactor; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; @@ -121,9 +124,7 @@ public class FipStart { public static final float TOLERANCE = 2.0e-3f; - int jprogram = 1; // FIPSTART only TODO Track this down - - public static final float PI_40K = 0.78539816E-04f; + JProgram jprogram = JProgram.FIP_START; // FIPSTART only TODO Track this down static final Collection UTIL_CLASSES = List.of( UtilizationClass.U75TO125, UtilizationClass.U125TO175, UtilizationClass.U175TO225, UtilizationClass.OVER225 @@ -171,7 +172,7 @@ void init(FileResolver resolver, String controlFilePath) throws IOException, Res // Load the control map var parser = new FipControlParser(); - try (var is = resolver.resolve(controlFilePath)) { + try (var is = resolver.resolveForInput(controlFilePath)) { setControlMap(parser.parse(is, resolver)); } @@ -365,27 +366,27 @@ VdypPolygon createVdypPolygon(FipPolygon fipPolygon, Map p float percentAvailable = estimatePercentForestLand(fipPolygon, fipVetLayer, fipPrimaryLayer); - var vdypPolygon = VdypPolygon.build(builder -> { - builder.polygonIdentifier(fipPolygon.getPolygonIdentifier()); - builder.percentAvailable(percentAvailable); - }); + var vdypPolygon = VdypPolygon.build(builder -> builder.copy(fipPolygon, x -> percentAvailable)); vdypPolygon.setLayers(processedLayers); return vdypPolygon; } // FIPLAND + @SuppressWarnings("java:S3655") float estimatePercentForestLand(FipPolygon fipPolygon, FipLayer fipVetLayer, FipLayerPrimary fipPrimaryLayer) throws ProcessingException { if (fipPolygon.getPercentAvailable().isPresent()) { return fipPolygon.getPercentAvailable().get(); } else { - boolean veteran = fipVetLayer != null && fipVetLayer.getHeight() > 0f && fipVetLayer.getCrownClosure() > 0f; // LAYERV + boolean veteran = fipVetLayer != null && fipVetLayer.getHeight().orElse(0f) > 0f + && fipVetLayer.getCrownClosure() > 0f; // LAYERV - if (jprogram == 1 && fipPolygon.getModeFip().map(mode -> mode == FipMode.FIPYOUNG).orElse(false)) { + if (jprogram == JProgram.FIP_START + && fipPolygon.getModeFip().map(mode -> mode == FipMode.FIPYOUNG).orElse(false)) { return 100f; } - if (jprogram == 3) { + if (jprogram == JProgram.VRI_START) { veteran = fipVetLayer != null; } @@ -394,8 +395,8 @@ float estimatePercentForestLand(FipPolygon fipPolygon, FipLayer fipVetLayer, Fip float crownClosure = fipPrimaryLayer.getCrownClosure(); // Assume crown closure linear with age, to 25. - if (fipPrimaryLayer.getAgeTotal() < 25f) { - crownClosure *= 25f / fipPrimaryLayer.getAgeTotal(); + if (fipPrimaryLayer.getAgeTotalSafe() < 25f) { + crownClosure *= 25f / fipPrimaryLayer.getAgeTotalSafe(); } // define crown closure as the SUM of two layers if (veteran) { @@ -411,7 +412,7 @@ float estimatePercentForestLand(FipPolygon fipPolygon, FipLayer fipVetLayer, Fip // Obtain the percent yield (in comparison with CC = 90%) float crownClosureTop = 90f; - float breastHeightAge = fipPrimaryLayer.getAgeTotal() - fipPrimaryLayer.getYearsToBreastHeight(); + float breastHeightAge = fipPrimaryLayer.getAgeTotalSafe() - fipPrimaryLayer.getYearsToBreastHeightSafe(); float yieldFactor = fipPolygon.getYieldFactor(); @@ -437,13 +438,13 @@ float estimatePercentForestLand(FipPolygon fipPolygon, FipLayer fipVetLayer, Fip } float gainMax; - if (fipPrimaryLayer.getAgeTotal() > 125f) { + if (fipPrimaryLayer.getAgeTotalSafe() > 125f) { gainMax = 0f; - } else if (fipPrimaryLayer.getAgeTotal() < 25f) { + } else if (fipPrimaryLayer.getAgeTotalSafe() < 25f) { gainMax = max(90f - percentYield, 0); } else { gainMax = max(90f - percentYield, 0); - gainMax = min(gainMax, 125 - fipPrimaryLayer.getAgeTotal()); + gainMax = min(gainMax, 125 - fipPrimaryLayer.getAgeTotalSafe()); } return floor(min(percentYield + gainMax, 100f)); @@ -467,23 +468,20 @@ VdypLayer processLayerAsPrimary(FipPolygon fipPolygon, FipLayerPrimary fipLayer, var result = VdypLayer.build(builder -> builder.copy(fipLayer)); - result.setYearsToBreastHeight(fipLayer.getYearsToBreastHeight()); - result.setHeight(fipLayer.getHeight()); - // EMP040 var baseArea = estimatePrimaryBaseArea( - fipLayer, bec, fipPolygon.getYieldFactor(), result.getBreastHeightAge(), baseAreaOverstory + fipLayer, bec, fipPolygon.getYieldFactor(), result.getBreastHeightAge().orElse(0f), baseAreaOverstory ); // BA_TOT result.getBaseAreaByUtilization().setCoe(UTIL_ALL, baseArea); var quadMeanDiameter = estimatePrimaryQuadMeanDiameter( - fipLayer, bec, result.getBreastHeightAge(), baseAreaOverstory + fipLayer, bec, result.getBreastHeightAge().orElse(0f), baseAreaOverstory ); result.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_ALL, quadMeanDiameter); - var tphTotal = treesPerHectare(baseArea, quadMeanDiameter); + var tphTotal = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, quadMeanDiameter); result.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, tphTotal); @@ -523,7 +521,7 @@ VdypLayer processLayerAsPrimary(FipPolygon fipPolygon, FipLayerPrimary fipLayer, result.setSpecies(vdypSpecies); float primaryHeight; - float leadHeight = fipLayer.getHeight(); + float leadHeight = fipLayer.getHeight().orElse(0f); for (var iPass = 1; iPass <= maxPass; iPass++) { if (iPass == 2) { for (var vSpec : vdypSpecies.values()) { @@ -719,7 +717,7 @@ void findRootsForDiameterAndBaseArea(VdypLayer result, FipLayerPrimary fipLayer, assert dq >= 0; float ba = baseAreaTotal * spec.getPercentGenus() / 100f; assert ba >= 0; - float tph = treesPerHectare(ba, dq); + float tph = BaseAreaTreeDensityDiameter.treesPerHectare(ba, dq); assert tph >= 0; spec.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_ALL, dq); spec.getBaseAreaByUtilization().setCoe(UTIL_ALL, ba); @@ -750,7 +748,7 @@ void findRootsForDiameterAndBaseArea(VdypLayer result, FipLayerPrimary fipLayer, result.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, treesPerHectareSum); result.getQuadraticMeanDiameterByUtilization().setCoe( UTIL_ALL, - quadMeanDiameter( + BaseAreaTreeDensityDiameter.quadMeanDiameter( result.getBaseAreaByUtilization().getCoe(UTIL_ALL), result.getTreesPerHectareByUtilization().getCoe(UTIL_ALL) ) @@ -882,16 +880,16 @@ float estimateQuadMeanDiameterForSpecies( } } - float quadMeanDiameter1 = quadMeanDiameter(baseArea1, treesPerHectare1); + float quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); float treesPerHectare2 = standTreesPerHectare - treesPerHectare1; - float quadMeanDiameter2 = quadMeanDiameter(baseArea2, treesPerHectare2); + float quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); if (quadMeanDiameter2 < minQuadMeanDiameter) { // species 2 is too small. Make target species smaller. quadMeanDiameter2 = minQuadMeanDiameter; - treesPerHectare2 = treesPerHectare(baseArea2, quadMeanDiameter2); + treesPerHectare2 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea2, quadMeanDiameter2); treesPerHectare1 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter1 = quadMeanDiameter(baseArea1, treesPerHectare1); + quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); } var limitCoe = getLimitsForHeightAndDiameter(species, region); @@ -899,19 +897,19 @@ float estimateQuadMeanDiameterForSpecies( final var dqMaxSp = max(7.6f, min(limitCoe.getCoe(2), limitCoe.getCoe(4) * loreyHeightSpec)); if (quadMeanDiameter1 < dqMinSp) { quadMeanDiameter1 = dqMinSp; - treesPerHectare1 = treesPerHectare(baseArea1, quadMeanDiameter1); + treesPerHectare1 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea1, quadMeanDiameter1); treesPerHectare2 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter2 = quadMeanDiameter(baseArea2, treesPerHectare2); + quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); } if (quadMeanDiameter1 > dqMaxSp) { // target species is too big. Make target species smaller, DQ2 bigger. quadMeanDiameter1 = dqMaxSp; - treesPerHectare1 = treesPerHectare(baseArea1, quadMeanDiameter1); + treesPerHectare1 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea1, quadMeanDiameter1); treesPerHectare2 = standTreesPerHectare - treesPerHectare2; if (treesPerHectare2 > 0f && baseArea2 > 0f) { - quadMeanDiameter2 = quadMeanDiameter(baseArea2, treesPerHectare2); + quadMeanDiameter2 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea2, treesPerHectare2); } else { quadMeanDiameter2 = 1000f; } @@ -919,9 +917,9 @@ float estimateQuadMeanDiameterForSpecies( // under rare circumstances, let DQ1 exceed DQMAXsp if (quadMeanDiameter2 < minQuadMeanDiameter) { quadMeanDiameter2 = minQuadMeanDiameter; - treesPerHectare2 = treesPerHectare(baseArea2, quadMeanDiameter2); + treesPerHectare2 = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea2, quadMeanDiameter2); treesPerHectare1 = standTreesPerHectare - treesPerHectare2; - quadMeanDiameter1 = quadMeanDiameter(baseArea1, treesPerHectare1); + quadMeanDiameter1 = BaseAreaTreeDensityDiameter.quadMeanDiameter(baseArea1, treesPerHectare1); } } @@ -1006,14 +1004,14 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws .getGenus(); // ageTotal copy, LVCOM3/AGETOTLV copied from FIPL_V/AGETOT_LV - var ageTotal = fipLayer.getAgeTotal(); + var ageTotal = fipLayer.getAgeTotal().orElse(0f); // yearsToBreastHeight copy, minimum 6.0, LVCOM3/YTBHLV copied from // FIPL_V/YTBH_L - var yearsToBreastHeight = Math.max(fipLayer.getYearsToBreastHeight(), 6.0f); + var yearsToBreastHeight = Math.max(fipLayer.getYearsToBreastHeight().orElse(0f), 6.0f); // height? copy LVCOM3/HDLV = FIPL_V/HT_LV - var height = fipLayer.getHeight(); + var height = fipLayer.getHeight().orElse(0f); var crownClosure = fipLayer.getCrownClosure(); @@ -1025,9 +1023,7 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws // Call EMP098 to get Veteran Basal Area, store in LVCOM1/BA array at positions // 0,0 and 0,4 var estimatedBaseArea = estimateVeteranBaseArea(height, crownClosure, primaryGenus, region); - var baseAreaByUtilization = new Coefficients( - new float[] { 0.0f, estimatedBaseArea, 0.0f, 0.0f, 0.0f, estimatedBaseArea }, -1 - ); + var baseAreaByUtilization = Utils.utilizationVector(estimatedBaseArea); // Copy over Species entries. // LVCOM/ISPLV=ISPV // LVCOM4/SP0LV=FIPSA/SP0V @@ -1083,8 +1079,11 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws float hl = vSpec.getLoreyHeightByUtilization().getCoe(0); float dq = max(a0 + a1 * pow(hl, a2), 22.5f); vSpec.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_LARGEST, dq); - vSpec.getTreesPerHectareByUtilization() - .setCoe(UTIL_LARGEST, treesPerHectare(vSpec.getBaseAreaByUtilization().getCoe(UTIL_LARGEST), dq)); + vSpec.getTreesPerHectareByUtilization().setCoe( + UTIL_LARGEST, + BaseAreaTreeDensityDiameter + .treesPerHectare(vSpec.getBaseAreaByUtilization().getCoe(UTIL_LARGEST), dq) + ); } var vdypLayer = VdypLayer.build(builder -> { @@ -1103,18 +1102,6 @@ VdypLayer processLayerAsVeteran(FipPolygon fipPolygon, FipLayer fipLayer) throws return vdypLayer; } - static Coefficients utilizationVector() { - return new Coefficients(new float[] { 0f, 0f, 0f, 0f, 0f, 0f }, -1); - } - - static Coefficients utilizationVector(float small, float all, float u1, float u2, float u3, float u4) { - return new Coefficients(new float[] { small, all, u1, u2, u3, u4 }, -1); - } - - static Coefficients utilizationVector(float small, float u1, float u2, float u3, float u4) { - return new Coefficients(new float[] { small, u1 + u2 + u3 + u4, u1, u2, u3, u4 }, -1); - } - enum VolumeComputeMode { /** * set volume components to zero @@ -1180,23 +1167,23 @@ void computeUtilizationComponentsPrimary( } float wholeStemVolumeSpec = spec.getWholeStemVolumeByUtilization().getCoe(UTIL_ALL); - var baseAreaUtil = utilizationVector(); - var quadMeanDiameterUtil = utilizationVector(); - var treesPerHectareUtil = utilizationVector(); - var wholeStemVolumeUtil = utilizationVector(); - var closeVolumeUtil = utilizationVector(); - var closeVolumeNetDecayUtil = utilizationVector(); - var closeVolumeNetDecayWasteUtil = utilizationVector(); - var closeVolumeNetDecayWasteBreakUtil = utilizationVector(); + var baseAreaUtil = Utils.utilizationVector(); + var quadMeanDiameterUtil = Utils.utilizationVector(); + var treesPerHectareUtil = Utils.utilizationVector(); + var wholeStemVolumeUtil = Utils.utilizationVector(); + var closeVolumeUtil = Utils.utilizationVector(); + var closeVolumeNetDecayUtil = Utils.utilizationVector(); + var closeVolumeNetDecayWasteUtil = Utils.utilizationVector(); + var closeVolumeNetDecayWasteBreakUtil = Utils.utilizationVector(); baseAreaUtil.setCoe(UTIL_ALL, baseAreaSpec); // BAU quadMeanDiameterUtil.setCoe(UTIL_ALL, quadMeanDiameterSpec); // DQU treesPerHectareUtil.setCoe(UTIL_ALL, treesPerHectareSpec); // TPHU wholeStemVolumeUtil.setCoe(UTIL_ALL, wholeStemVolumeSpec); // WSU - var adjustCloseUtil = utilizationVector(); // ADJVCU - var adjustDecayUtil = utilizationVector(); // ADJVD - var adjustDecayWasteUtil = utilizationVector(); // ADJVDW + var adjustCloseUtil = Utils.utilizationVector(); // ADJVCU + var adjustDecayUtil = Utils.utilizationVector(); // ADJVD + var adjustDecayWasteUtil = Utils.utilizationVector(); // ADJVDW // EMP071 estimateQuadMeanDiameterByUtilization(bec, quadMeanDiameterUtil, spec); @@ -1207,7 +1194,9 @@ void computeUtilizationComponentsPrimary( // Calculate tree density components for (var uc : UTIL_CLASSES) { treesPerHectareUtil.setCoe( - uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) + uc.index, + BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) ); } @@ -1224,7 +1213,9 @@ uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.ge for (var uc : UTIL_CLASSES) { treesPerHectareUtil.setCoe( - uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) + uc.index, + BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) ); } @@ -1265,18 +1256,18 @@ uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.ge // EMP093 estimateNetDecayVolume( spec.getGenus(), bec.getRegion(), UtilizationClass.ALL, adjustCloseUtil, spec.getDecayGroup(), - loreyHeightSpec, vdypLayer.getBreastHeightAge(), quadMeanDiameterUtil, closeVolumeUtil, - closeVolumeNetDecayUtil + loreyHeightSpec, vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, + closeVolumeUtil, closeVolumeNetDecayUtil ); // EMP094 estimateNetDecayAndWasteVolume( bec.getRegion(), UtilizationClass.ALL, adjustCloseUtil, spec.getGenus(), loreyHeightSpec, - vdypLayer.getBreastHeightAge(), quadMeanDiameterUtil, closeVolumeUtil, closeVolumeNetDecayUtil, - closeVolumeNetDecayWasteUtil + vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeVolumeUtil, + closeVolumeNetDecayUtil, closeVolumeNetDecayWasteUtil ); - if (jprogram < 6) { + if (jprogram.isStart()) { // EMP095 estimateNetDecayWasteAndBreakageVolume( UtilizationClass.ALL, spec.getBreakageGroup(), quadMeanDiameterUtil, closeVolumeUtil, @@ -1372,15 +1363,15 @@ private void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinit try { for (var vdypSpecies : vdypLayer.getSpecies().values()) { - var treesPerHectareUtil = utilizationVector(); - var quadMeanDiameterUtil = utilizationVector(); - var baseAreaUtil = utilizationVector(); - var wholeStemVolumeUtil = utilizationVector(); + var treesPerHectareUtil = Utils.utilizationVector(); + var quadMeanDiameterUtil = Utils.utilizationVector(); + var baseAreaUtil = Utils.utilizationVector(); + var wholeStemVolumeUtil = Utils.utilizationVector(); - var closeUtilizationVolumeUtil = utilizationVector(); - var closeUtilizationNetOfDecayUtil = utilizationVector(); - var closeUtilizationNetOfDecayAndWasteUtil = utilizationVector(); - var closeUtilizationNetOfDecayWasteAndBreakageUtil = utilizationVector(); + var closeUtilizationVolumeUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayAndWasteUtil = Utils.utilizationVector(); + var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); var hlSp = vdypSpecies.getLoreyHeightByUtilization().getCoe(UTIL_ALL); { @@ -1423,19 +1414,19 @@ private void computeUtilizationComponentsVeteran(VdypLayer vdypLayer, BecDefinit // EMP093 estimateNetDecayVolume( vdypSpecies.getGenus(), bec.getRegion(), utilizationClass, adjust, vdypSpecies.getDecayGroup(), - hlSp, vdypLayer.getBreastHeightAge(), quadMeanDiameterUtil, closeUtilizationVolumeUtil, - closeUtilizationNetOfDecayUtil + hlSp, vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, + closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil ); adjust.setCoe(4, volumeAdjustCoe.getCoe(4)); // EMP094 estimateNetDecayAndWasteVolume( bec.getRegion(), utilizationClass, adjust, vdypSpecies.getGenus(), hlSp, - vdypLayer.getBreastHeightAge(), quadMeanDiameterUtil, closeUtilizationVolumeUtil, + vdypLayer.getBreastHeightAge().orElse(0f), quadMeanDiameterUtil, closeUtilizationVolumeUtil, closeUtilizationNetOfDecayUtil, closeUtilizationNetOfDecayAndWasteUtil ); - if (jprogram < 6) { + if (jprogram.isStart()) { // EMP095 estimateNetDecayWasteAndBreakageVolume( utilizationClass, vdypSpecies.getBreakageGroup(), quadMeanDiameterUtil, @@ -1492,8 +1483,9 @@ private void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) { // Quadratic mean diameter for the layer is computed from the BA and TPH after // they have been found from the species { - var utilVector = vdypLayer.getBaseAreaByUtilization() - .pairwise(vdypLayer.getTreesPerHectareByUtilization(), FipStart::quadMeanDiameter); + var utilVector = vdypLayer.getBaseAreaByUtilization().pairwise( + vdypLayer.getTreesPerHectareByUtilization(), BaseAreaTreeDensityDiameter::quadMeanDiameter + ); vdypLayer.setQuadraticMeanDiameterByUtilization(utilVector); } } @@ -1502,7 +1494,7 @@ private void computeLayerUtilizationComponentsFromSpecies(VdypLayer vdypLayer) { private void sumSpeciesUtilizationVectorsToLayer(VdypLayer vdypLayer) throws IllegalStateException { try { for (var accessors : SUMMABLE_UTILIZATION_VECTOR_ACCESSORS) { - var utilVector = utilizationVector(); + var utilVector = Utils.utilizationVector(); for (var vdypSpecies : vdypLayer.getSpecies().values()) { var speciesVector = (Coefficients) accessors.getReadMethod().invoke(vdypSpecies); utilVector.pairwiseInPlace(speciesVector, (x, y) -> x + y); @@ -1557,7 +1549,8 @@ void reconcileComponents( throw new ProcessingException("Computed base areas for 7.5+ components do not sum to expected total"); } - float dq0 = quadMeanDiameter(baseAreaUtil.getCoe(UTIL_ALL), treesPerHectareUtil.getCoe(UTIL_ALL)); + float dq0 = BaseAreaTreeDensityDiameter + .quadMeanDiameter(baseAreaUtil.getCoe(UTIL_ALL), treesPerHectareUtil.getCoe(UTIL_ALL)); if (dq0 < 7.5f) { throw new ProcessingException( @@ -1566,7 +1559,9 @@ void reconcileComponents( } float tphSumHigh = (float) UTIL_CLASSES.stream() - .mapToDouble(uc -> treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.lowBound)).sum(); + .mapToDouble( + uc -> BaseAreaTreeDensityDiameter.treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.lowBound) + ).sum(); if (tphSumHigh < treesPerHectareUtil.getCoe(UTIL_ALL)) { reconcileComponentsMode1(baseAreaUtil, treesPerHectareUtil, quadMeanDiameterUtil, tphSumHigh); @@ -1579,6 +1574,7 @@ void reconcileComponents( private static final List MODE_1_RECONCILE_AVAILABILITY_CLASSES = List .of(UtilizationClass.OVER225, UtilizationClass.U175TO225, UtilizationClass.U125TO175); + @SuppressWarnings("java:S3655") void reconcileComponentsMode1( Coefficients baseAreaUtil, Coefficients treesPerHectareUtil, Coefficients quadMeanDiameterUtil, float tphSumHigh @@ -1594,8 +1590,9 @@ void reconcileComponentsMode1( UTIL_CLASSES.forEach(uc -> quadMeanDiameterUtil.setCoe(uc.index, uc.lowBound)); for (var uc : MODE_1_RECONCILE_AVAILABILITY_CLASSES) { - float tphAvail = treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.previous().get().lowBound) - - treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.lowBound); + float tphAvail = BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.previous().get().lowBound) + - BaseAreaTreeDensityDiameter.treesPerHectare(baseAreaUtil.getCoe(uc.index), uc.lowBound); if (tphAvail < tphNeed) { baseAreaUtil.scalarInPlace(uc.previous().get().index, x -> x + baseAreaUtil.getCoe(uc.index)); @@ -1610,7 +1607,9 @@ void reconcileComponentsMode1( } UTIL_CLASSES.forEach( uc -> treesPerHectareUtil.setCoe( - uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) + uc.index, + BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) ) ); } @@ -1632,7 +1631,8 @@ void reconcileComponentsMode2Check( reconcileComponentsMode2(baseAreaUtil, treesPerHectareUtil, quadMeanDiameterUtil); return; } - float dWant = quadMeanDiameter(baseAreaUtil.getCoe(uc.index), treesPerHectareUtil.getCoe(uc.index)); + float dWant = BaseAreaTreeDensityDiameter + .quadMeanDiameter(baseAreaUtil.getCoe(uc.index), treesPerHectareUtil.getCoe(uc.index)); float dqI = quadMeanDiameterUtil.getCoe(uc.index); if (dqI >= uc.lowBound && dqI <= uc.highBound && abs(dWant - dqI) < 0.00001) { return; @@ -1649,7 +1649,7 @@ private void reconcileComponentsMode2( float baseAreaFixed = 0f; float treesPerHectareFixed = 0f; var quadMeanDiameterLimit = new boolean[] { false, false, false, false, false }; - Coefficients dqTrial = utilizationVector(); + Coefficients dqTrial = Utils.utilizationVector(); while (true) { n++; @@ -1675,7 +1675,7 @@ private void reconcileComponentsMode2( return; } - float dqAll = quadMeanDiameter(baAll, tphAll); + float dqAll = BaseAreaTreeDensityDiameter.quadMeanDiameter(baAll, tphAll); float k = dqAll * dqAll / baAll * sum; float sqrtK = sqrt(k); @@ -1717,16 +1717,17 @@ private void reconcileComponentsMode2( quadMeanDiameterLimit[violateClass.index] = true; baseAreaFixed += baseAreaUtil.getCoe(violateClass.index); - treesPerHectareFixed += treesPerHectare( - baseAreaUtil.getCoe(violateClass.index), dqTrial.getCoe(violateClass.index) - ); + treesPerHectareFixed += BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(violateClass.index), dqTrial.getCoe(violateClass.index)); } // Make BA's agree with DQ's and TPH's for (var uc : UTIL_CLASSES) { quadMeanDiameterUtil.setCoe(uc.index, dqTrial.getCoe(uc.index)); treesPerHectareUtil.setCoe( - uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) + uc.index, + BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.getCoe(uc.index)) ); } // RE VERIFY That sums are correct @@ -1740,6 +1741,7 @@ uc.index, treesPerHectare(baseAreaUtil.getCoe(uc.index), quadMeanDiameterUtil.ge } } + @SuppressWarnings("java:S3655") void reconcileComponentsMode3( Coefficients baseAreaUtil, Coefficients treesPerHectareUtil, Coefficients quadMeanDiameterUtil ) { @@ -1777,7 +1779,7 @@ void estimateBaseAreaByUtilization( ); float dq = quadMeanDiameterUtil.getCoe(UTIL_ALL); - var b = utilizationVector(); + var b = Utils.utilizationVector(); b.setCoe(0, baseAreaUtil.getCoe(UTIL_ALL)); for (int i = 1; i < UTIL_LARGEST; i++) { var coe = coeMap.get(i, spec.getGenus(), bec.getGrowthBec().getAlias()); @@ -1824,7 +1826,7 @@ void estimateQuadMeanDiameterByUtilization(BecDefinition bec, Coefficients quadM float quadMeanDiameter07 = quadMeanDiameterUtil.getCoe(UTIL_ALL); for (var uc : UTIL_CLASSES) { - log.atDebug().setMessage("For util level {}").addArgument(uc.name); + log.atDebug().setMessage("For util level {}").addArgument(uc.className); final var coeMap = Utils.>expectParsedControl( controlMap, UtilComponentDQParser.CONTROL_KEY, MatrixMap3.class ); @@ -1875,15 +1877,17 @@ void estimateQuadMeanDiameterByUtilization(BecDefinition bec, Coefficients quadM throw new IllegalStateException( "Should not be attempting to process small component or all large components" ); + default: + throw new IllegalStateException("Unknown utilization class " + uc); } - log.atDebug().setMessage("Util DQ for class {} is {}").addArgument(uc.name) + log.atDebug().setMessage("Util DQ for class {} is {}").addArgument(uc.className) .addArgument(quadMeanDiameterUtil.getCoe(uc.index)); } log.atTrace().setMessage("Estimated Diameters {}").addArgument( () -> UTIL_CLASSES.stream() - .map(uc -> String.format("%s: %d", uc.name, quadMeanDiameterUtil.getCoe(uc.index))) + .map(uc -> String.format("%s: %d", uc.className, quadMeanDiameterUtil.getCoe(uc.index))) ); } @@ -1914,15 +1918,14 @@ List findPrimarySpecies(Map allSpecies) { // Start with a deep copy of the species map so there are no side effects from // the manipulation this method does. var combined = new HashMap(allSpecies.size()); - allSpecies.entrySet().stream().forEach(spec -> { - combined.put(spec.getKey(), new FipSpecies(spec.getValue())); - }); + allSpecies.entrySet().stream().forEach(spec -> combined.put(spec.getKey(), new FipSpecies(spec.getValue()))); for (var combinationGroup : PRIMARY_SPECIES_TO_COMBINE) { var groupSpecies = combinationGroup.stream().map(combined::get).filter(Objects::nonNull).toList(); if (groupSpecies.size() < 2) { continue; } + @SuppressWarnings("java:S3655") var groupPrimary = new FipSpecies(groupSpecies.stream().sorted(PERCENT_GENUS_DESCENDING).findFirst().get()); var total = (float) groupSpecies.stream().mapToDouble(FipSpecies::getPercentGenus).sum(); combinationGroup.forEach(combined::remove); @@ -2521,9 +2524,7 @@ float normalizeUtilizationComponents(Coefficients components) throws ProcessingE if (sum <= 0f) { throw new ProcessingException("Total volume " + sum + " was not positive."); } - UTIL_CLASSES.forEach(uc -> { - components.setCoe(uc.index, components.getCoe(uc.index) * k); - }); + UTIL_CLASSES.forEach(uc -> components.setCoe(uc.index, components.getCoe(uc.index) * k)); return k; } @@ -2595,8 +2596,7 @@ private FipPolygon getPolygon( } private Optional heightMinimum(LayerType layer) { - @SuppressWarnings("unchecked") - var minima = (Map) controlMap.get(FipControlParser.MINIMA); + var minima = Utils.>expectParsedControl(controlMap, FipControlParser.MINIMA, Map.class); switch (layer) { case PRIMARY: return Optional.of(minima.get(FipControlParser.MINIMUM_HEIGHT)); @@ -2629,7 +2629,7 @@ void checkPolygon(FipPolygon polygon) throws ProcessingException { // error that total age is "less than" YTBH. Replicating that for now but // consider changing it. - if (primaryLayer.getAgeTotal() - primaryLayer.getYearsToBreastHeight() < 0.5f) { + if (primaryLayer.getAgeTotal().orElse(0f) - primaryLayer.getYearsToBreastHeight().orElse(0f) < 0.5f) { throw validationError( "Polygon %s has %s layer where total age is less than YTBH.", polygon.getPolygonIdentifier(), LayerType.PRIMARY @@ -2643,13 +2643,13 @@ void checkPolygon(FipPolygon polygon) throws ProcessingException { // layers. for (FipLayer layer : polygon.getLayers().values()) { - var height = layer.getHeight(); + var height = layer.getHeight().orElse(0f); throwIfPresent( heightMinimum(layer.getLayer()).filter(minimum -> height < minimum).map( minimum -> validationError( "Polygon %s has %s layer where height %.1f is less than minimum %.1f.", - polygon.getPolygonIdentifier(), layer.getLayer(), layer.getHeight(), minimum + polygon.getPolygonIdentifier(), layer.getLayer(), layer.getHeightSafe(), minimum ) ) ); @@ -2661,17 +2661,18 @@ void checkPolygon(FipPolygon polygon) throws ProcessingException { ); } - if (primaryLayer.getYearsToBreastHeight() < 0.5) { + if (primaryLayer.getYearsToBreastHeight().orElse(0f) < 0.5) { throw validationError( "Polygon %s has %s layer where years to breast height %.1f is less than minimum %.1f years.", - polygon.getPolygonIdentifier(), LayerType.PRIMARY, primaryLayer.getYearsToBreastHeight(), 0.5f + polygon.getPolygonIdentifier(), LayerType.PRIMARY, primaryLayer.getYearsToBreastHeightSafe(), 0.5f ); } - if (primaryLayer.getSiteIndex() < 0.5) { + if (primaryLayer.getSiteIndex().orElse(0f) < 0.5) { throw validationError( - "Polygon %s has %s layer where site index %.1f is less than minimum %.1f years.", - polygon.getPolygonIdentifier(), LayerType.PRIMARY, primaryLayer.getSiteIndex(), 0.5f + "Polygon %s has %s layer where site index %s is less than minimum %.1f years.", + polygon.getPolygonIdentifier(), LayerType.PRIMARY, + primaryLayer.getSiteIndex().map(x -> String.format("%.1f", x)).orElse("N/A"), 0.5f ); } @@ -2736,7 +2737,7 @@ float estimatePrimaryBaseArea( var baseArea = 0f; - float height = fipLayer.getHeight(); + float height = fipLayer.getHeight().orElse(0f); if (height > coe.getCoe(2) - 3f) { /* @formatter:off */ // if (HD .le. A(2) - 3.0) then @@ -2891,7 +2892,7 @@ float estimatePrimaryQuadMeanDiameter( ); var trAge = log(clamp(breastHeightAge, 5f, 350f)); - var height = fipLayer.getHeight(); + var height = fipLayer.getHeight().orElse(0f); if (height <= coe.getCoe(5)) { return 7.6f; @@ -2932,25 +2933,6 @@ float estimatePrimaryQuadMeanDiameter( return quadMeanDiameter; } - // FT_BD - static float treesPerHectare(float baseArea, float quadraticMeanDiameter) { - if (baseArea != 0) { - return baseArea / PI_40K / (quadraticMeanDiameter * quadraticMeanDiameter); - } - return 0f; - } - - // FD_BT - static float quadMeanDiameter(float baseArea, float treesPerHectare) { - if (baseArea > 1e6f || treesPerHectare > 1e6f || Float.isNaN(baseArea) || Float.isNaN(treesPerHectare)) { - return 0f; - } else if (baseArea > 0f && treesPerHectare > 0f) { - return sqrt(baseArea / treesPerHectare / PI_40K); - } - return 0f; - - } - private static void throwIfPresent(Optional opt) throws E { if (opt.isPresent()) { throw opt.get(); @@ -3027,7 +3009,7 @@ public float estimateMeanVolume(int volumeGroup, float loreyHeight, float quadMe final float baseArea = (float) (layerBa * percentL1[j] / 100d); spec.getBaseAreaByUtilization().setCoe(UTIL_ALL, baseArea); - final float tph = FipStart.treesPerHectare(baseArea, quadMeanDiameter); + final float tph = BaseAreaTreeDensityDiameter.treesPerHectare(baseArea, quadMeanDiameter); spec.getTreesPerHectareByUtilization().setCoe(UTIL_ALL, tph); treesPerHectareSum += tph; @@ -3041,7 +3023,7 @@ public float estimateMeanVolume(int volumeGroup, float loreyHeight, float quadMe } } - double dqFinal = FipStart + double dqFinal = BaseAreaTreeDensityDiameter .quadMeanDiameter(layer.getBaseAreaByUtilization().getCoe(UTIL_ALL), (float) treesPerHectareSum); var y = new double[point.length]; @@ -3070,7 +3052,9 @@ public void estimateSmallComponents(FipPolygon fPoly, VdypLayer layer) { float treesPerHectareSum = 0f; float volumeSum = 0f; - Region region = BecDefinitionParser.getBecs(controlMap).get(fPoly.getBiogeoclimaticZone()).get().getRegion(); + Region region = BecDefinitionParser.getBecs(controlMap).get(fPoly.getBiogeoclimaticZone()) + .orElseThrow(() -> new IllegalStateException("Could not find BEC " + fPoly.getBiogeoclimaticZone())) + .getRegion(); for (VdypSpecies spec : layer.getSpecies().values()) { float loreyHeightSpec = spec.getLoreyHeightByUtilization().getCoe(UTIL_ALL); // HLsp @@ -3101,7 +3085,8 @@ public void estimateSmallComponents(FipPolygon fPoly, VdypLayer layer) { // TODO Apply Compatibility Variables, not needed for FIPSTART spec.getLoreyHeightByUtilization().setCoe(UTIL_SMALL, loreyHeightSpecSmall); - float treesPerHectareSpecSmall = treesPerHectare(baseAreaSpecSmall, quadMeanDiameterSpecSmall); // TPHSMsp + float treesPerHectareSpecSmall = BaseAreaTreeDensityDiameter + .treesPerHectare(baseAreaSpecSmall, quadMeanDiameterSpecSmall); // TPHSMsp spec.getBaseAreaByUtilization().setCoe(UTIL_SMALL, baseAreaSpecSmall); spec.getTreesPerHectareByUtilization().setCoe(UTIL_SMALL, treesPerHectareSpecSmall); spec.getQuadraticMeanDiameterByUtilization().setCoe(UTIL_SMALL, quadMeanDiameterSpecSmall); @@ -3122,7 +3107,7 @@ public void estimateSmallComponents(FipPolygon fPoly, VdypLayer layer) { layer.getBaseAreaByUtilization().setCoe(UTIL_SMALL, baseAreaSum); layer.getTreesPerHectareByUtilization().setCoe(UTIL_SMALL, treesPerHectareSum); layer.getQuadraticMeanDiameterByUtilization() - .setCoe(UTIL_SMALL, quadMeanDiameter(baseAreaSum, treesPerHectareSum)); + .setCoe(UTIL_SMALL, BaseAreaTreeDensityDiameter.quadMeanDiameter(baseAreaSum, treesPerHectareSum)); layer.getWholeStemVolumeByUtilization().setCoe(UTIL_SMALL, volumeSum); } @@ -3216,7 +3201,7 @@ private float smallComponentProbability(VdypLayer layer, VdypSpecies spec, Regio float logit = // a0 + // a1 * coast + // - a2 * layer.getBreastHeightAge() + // + a2 * layer.getBreastHeightAge().orElse(0f) + // a3 * spec.getLoreyHeightByUtilization().getCoe(FipStart.UTIL_ALL); return exp(logit) / (1.0f + exp(logit)); diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/ModifierParser.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/ModifierParser.java index c660625b0..b771941f9 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/ModifierParser.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/ModifierParser.java @@ -21,6 +21,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.ValueParser; import ca.bc.gov.nrs.vdyp.io.parse.VeteranBQParser; import ca.bc.gov.nrs.vdyp.model.Coefficients; +import ca.bc.gov.nrs.vdyp.model.JProgram; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; import ca.bc.gov.nrs.vdyp.model.MatrixMap3; @@ -75,11 +76,11 @@ public class ModifierParser implements OptionalResourceControlMapModifier { public static final int MAX_MODS = 60; - int jprogram; + JProgram jprogram; static final int[] ipoint = { 1, 0, 2, 0, 0, 3, 4, 5, 0 }; - public ModifierParser(int jprogram) { + public ModifierParser(JProgram jprogram) { super(); this.jprogram = jprogram; } @@ -248,7 +249,7 @@ void modsByRegions(List mods, int offset, BiConsumer m boolean modIsForProgram(Map entry) { @SuppressWarnings("unchecked") var programs = (List) entry.get("programs"); - var index = ipoint[this.jprogram - 1]; + var index = ipoint[this.jprogram.getIndex() - 1]; if (index <= 0) throw new IllegalStateException("JProgram " + this.jprogram + " mapped to " + index); return programs.get(index - 1); diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayer.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayer.java index 3ce232b6a..8c648e4d1 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayer.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayer.java @@ -4,57 +4,96 @@ import java.util.Optional; import java.util.function.Consumer; +import ca.bc.gov.nrs.vdyp.common.Computed; import ca.bc.gov.nrs.vdyp.model.BaseVdypLayer; import ca.bc.gov.nrs.vdyp.model.LayerType; public class FipLayer extends BaseVdypLayer { - private float siteIndex; // FIPL_1/SI_L1 or FIPL_V/SI_V1 private float crownClosure; // FIPL_1/CC_L1 or FIP:_V/CC_V1 - private String siteGenus; // FIPL_1A/SITESP0_L1 or FIPL_VA/SITESP0_L1 private String siteSpecies; // FIPL_1A/SITESP64_L1 or FIPL_VA/SITESP64_L1 public FipLayer( - String polygonIdentifier, LayerType layer, float ageTotal, float yearsToBreastHeight, float height, - float siteIndex, float crownClosure, String siteGenus, String siteSpecies + String polygonIdentifier, LayerType layer, Optional ageTotal, Optional height, + Optional yearsToBreastHeight, Optional siteIndex, Optional siteCurveNumber, + Optional inventoryTypeGroup, Optional siteGenus, float crownClosure, String siteSpecies ) { - super(polygonIdentifier, layer, ageTotal, yearsToBreastHeight, height); - this.siteIndex = siteIndex; + super( + polygonIdentifier, layer, ageTotal, height, yearsToBreastHeight, siteIndex, siteCurveNumber, + inventoryTypeGroup, siteGenus + ); this.crownClosure = crownClosure; - this.siteGenus = siteGenus; - this.siteSpecies = siteGenus; - } - - public float getSiteIndex() { - return siteIndex; + this.siteSpecies = siteSpecies; } public float getCrownClosure() { return crownClosure; } - public String getSiteSp0() { - return siteGenus; + public String getSiteSpecies() { + return siteSpecies; } - public String getSiteSp64() { - return siteSpecies; + public void setCrownClosure(float crownClosure) { + this.crownClosure = crownClosure; } - public void setSiteIndex(float siteIndex) { - this.siteIndex = siteIndex; + public void setSiteSpecies(String siteSp64) { + this.siteSpecies = siteSp64; } - public void setCrownClosure(float crownClosure) { - this.crownClosure = crownClosure; + @Computed + public float getAgeTotalSafe() { + return super.getAgeTotal().orElseThrow(() -> new IllegalStateException()); } - public void setSiteGenus(String sireSp0) { - this.siteGenus = sireSp0; + @Computed + public float getHeightSafe() { + return super.getHeight().orElseThrow(() -> new IllegalStateException()); } - public void setSiteSpecies(String siteSp64) { - this.siteSpecies = siteSp64; + @Computed + public float getYearsToBreastHeightSafe() { + return super.getYearsToBreastHeight().orElseThrow(() -> new IllegalStateException()); + } + + @Computed + public void setAgeTotalSafe(float ageTotal) { + super.setAgeTotal(Optional.of(ageTotal)); + } + + @Computed + public void setHeightSafe(float height) { + super.setHeight(Optional.of(height)); + } + + @Computed + public void setYearsToBreastHeightSafe(float yearsToBreastHeight) { + super.setYearsToBreastHeight(Optional.of(yearsToBreastHeight)); + } + + @Override + public void setAgeTotal(Optional ageTotal) { + if(ageTotal.isEmpty()) { + throw new IllegalArgumentException("ageTotal must not be empty"); + } + super.setAgeTotal(ageTotal); + } + + @Override + public void setHeight(Optional height) { + if(height.isEmpty()) { + throw new IllegalArgumentException("height must not be empty"); + } + super.setHeight(height); + } + + @Override + public void setYearsToBreastHeight(Optional yearsToBreastHeight) { + if(yearsToBreastHeight.isEmpty()) { + throw new IllegalArgumentException("yearsToBreastHeight must not be empty"); + } + super.setYearsToBreastHeight(yearsToBreastHeight); } /** @@ -96,26 +135,14 @@ public static FipLayer build(FipPolygon polygon, Consumer config) { } public static class Builder extends BaseVdypLayer.Builder { - protected Optional siteIndex = Optional.empty(); protected Optional crownClosure = Optional.empty(); - protected Optional siteGenus = Optional.empty(); protected Optional siteSpecies = Optional.empty(); - public Builder siteIndex(float siteIndex) { - this.siteIndex = Optional.of(siteIndex); - return this; - } - public Builder crownClosure(float crownClosure) { this.crownClosure = Optional.of(crownClosure); return this; } - public Builder siteGenus(String siteGenus) { - this.siteGenus = Optional.of(siteGenus); - return this; - } - public Builder siteSpecies(String siteSpecies) { this.siteSpecies = Optional.of(siteSpecies); return this; @@ -124,24 +151,30 @@ public Builder siteSpecies(String siteSpecies) { @Override protected void check(Collection errors) { super.check(errors); - requirePresent(siteIndex, "siteIndex", errors); requirePresent(crownClosure, "crownClosure", errors); - requirePresent(siteGenus, "siteGenus", errors); requirePresent(siteSpecies, "siteSpecies", errors); - } @Override protected FipLayer doBuild() { + /* + * public FipLayer( String polygonIdentifier, LayerType layer, Optional + * ageTotal, Optional height, Optional yearsToBreastHeight, + * Optional siteIndex, Optional siteCurveNumber, + * Optional inventoryTypeGroup, Optional siteGenus, float + * crownClosure, String siteSpecies + */ return (new FipLayer( polygonIdentifier.get(), // layer.get(), // - ageTotal.get(), // - yearsToBreastHeight.get(), // - height.get(), // - siteIndex.get(), // + ageTotal, // + height, // + yearsToBreastHeight, // + siteIndex, // + siteCurveNumber, // + inventoryTypeGroup, // + siteGenus, // crownClosure.get(), // - siteGenus.get(), // siteSpecies.get() )); } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayerPrimary.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayerPrimary.java index 6ed23a42e..25846e268 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayerPrimary.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipLayerPrimary.java @@ -8,32 +8,23 @@ public class FipLayerPrimary extends FipLayer { - // TODO Confirm if these should be required instead of optional if we know it's - // a Primary layer. - private Optional siteCurveNumber = Optional.empty(); // FIPL_1/SCN_L1 + private Optional stockingClass; // FIPL_1ST/STK_L1 - private Optional stockingClass = Optional.empty(); // FIPL_1ST/STK_L1 - - private Optional primaryGenus = Optional.empty(); // FIPL_1C/JPRIME - - private Optional inventoryTypeGroup = Optional.empty(); + private Optional primaryGenus; // FIPL_1C/JPRIME public FipLayerPrimary( - String polygonIdentifier, float ageTotal, float yearsToBreastHeight, float height, float siteIndex, - float crownClosure, String siteGenus, String siteSpecies + String polygonIdentifier, Optional ageTotal, Optional height, + Optional yearsToBreastHeight, Optional siteIndex, Optional siteCurveNumber, + Optional inventoryTypeGroup, Optional siteGenus, float crownClosure, String siteSpecies, + Optional stockingClass, Optional primaryGenus ) { super( - polygonIdentifier, LayerType.PRIMARY, ageTotal, yearsToBreastHeight, height, siteIndex, crownClosure, - siteGenus, siteSpecies + polygonIdentifier, LayerType.PRIMARY, ageTotal, height, yearsToBreastHeight, siteIndex, siteCurveNumber, + inventoryTypeGroup, siteGenus, crownClosure, siteSpecies ); - } - - public Optional getSiteCurveNumber() { - return siteCurveNumber; - } + this.stockingClass = stockingClass; + this.primaryGenus = primaryGenus; - public void setSiteCurveNumber(Optional siteCurveNumber) { - this.siteCurveNumber = siteCurveNumber; } public Optional getStockingClass() { @@ -57,14 +48,6 @@ public Optional getPrimarySpeciesRecord() { return primaryGenus.map(this.getSpecies()::get); } - public Optional getInventoryTypeGroup() { - return inventoryTypeGroup; - } - - public void setInventoryTypeGroup(Optional inventoryTypeGroup) { - this.inventoryTypeGroup = inventoryTypeGroup; - } - /** * Accepts a configuration function that accepts a builder to configure. * @@ -103,19 +86,10 @@ public static FipLayerPrimary buildPrimary(FipPolygon polygon, Consumer siteCurveNumber = Optional.empty(); - private Optional stockingClass = Optional.empty(); private Optional primaryGenus = Optional.empty(); - private Optional inventoryTypeGroup = Optional.empty(); - - public Builder siteCurveNumber(Optional siteCurveNumber) { - this.siteCurveNumber = siteCurveNumber; - return this; - } - public Builder stockingClass(Optional stockingClass) { this.stockingClass = stockingClass; return this; @@ -126,15 +100,6 @@ public Builder primaryGenus(Optional primaryGenus) { return this; } - public Builder inventoryTypeGroup(Optional inventoryTypeGroup) { - this.inventoryTypeGroup = inventoryTypeGroup; - return this; - } - - public Builder siteCurveNumber(int siteCurveNumber) { - return siteCurveNumber(Optional.of(siteCurveNumber)); - } - public Builder stockingClass(char stockingClass) { return stockingClass(Optional.of(stockingClass)); } @@ -143,10 +108,6 @@ public Builder primaryGenus(String primaryGenus) { return primaryGenus(Optional.of(primaryGenus)); } - public Builder inventoryTypeGroup(int inventoryTypeGroup) { - return inventoryTypeGroup(Optional.of(inventoryTypeGroup)); - } - public PrimaryBuilder() { super(); this.layerType(LayerType.PRIMARY); @@ -154,21 +115,19 @@ public PrimaryBuilder() { @Override protected FipLayerPrimary doBuild() { - var result = new FipLayerPrimary( + return new FipLayerPrimary( polygonIdentifier.get(), // - ageTotal.get(), // - yearsToBreastHeight.get(), // - height.get(), // - siteIndex.get(), // + ageTotal, // + height, // + yearsToBreastHeight, // + siteIndex, // + siteCurveNumber, // + inventoryTypeGroup, // + siteGenus, // crownClosure.get(), // - siteGenus.get(), // - siteSpecies.get() + siteSpecies.get(), // + stockingClass, primaryGenus ); - result.setSiteCurveNumber(this.siteCurveNumber); - result.setStockingClass(this.stockingClass); - result.setPrimaryGenus(this.primaryGenus); - result.setInventoryTypeGroup(this.inventoryTypeGroup); - return result; } } diff --git a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipPolygon.java b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipPolygon.java index b250c0188..e325d7949 100644 --- a/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipPolygon.java +++ b/vdyp-fip/src/main/java/ca/bc/gov/nrs/vdyp/fip/model/FipPolygon.java @@ -5,12 +5,10 @@ import java.util.function.Consumer; import ca.bc.gov.nrs.vdyp.model.BaseVdypPolygon; +import ca.bc.gov.nrs.vdyp.model.FipMode; public class FipPolygon extends BaseVdypPolygon> { - private String forestInventoryZone; // FIP_P/FIZ - private String biogeoclimaticZone; // FIP_P/BEC - private Optional modeFip; // FIP_P2/MODE / MODEfip private Optional nonproductiveDescription; // FIP_P3/NPDESC private float yieldFactor; // FIP_P4/YLDFACT @@ -18,38 +16,11 @@ public FipPolygon( String polygonIdentifier, String fiz, String becIdentifier, Optional percentAvailable, Optional modeFip, Optional nonproductiveDescription, float yieldFactor ) { - super(polygonIdentifier, percentAvailable); - this.forestInventoryZone = fiz; - this.biogeoclimaticZone = becIdentifier; - this.modeFip = modeFip; + super(polygonIdentifier, percentAvailable, fiz, becIdentifier, modeFip); this.nonproductiveDescription = nonproductiveDescription; this.yieldFactor = yieldFactor; } - public String getForestInventoryZone() { - return forestInventoryZone; - } - - public void setForestInventoryZone(String forestInventoryZone) { - this.forestInventoryZone = forestInventoryZone; - } - - public String getBiogeoclimaticZone() { - return biogeoclimaticZone; - } - - public void setBiogeoclimaticZone(String biogeoclimaticZone) { - this.biogeoclimaticZone = biogeoclimaticZone; - } - - public Optional getModeFip() { - return modeFip; - } - - public void setModeFip(Optional modeFip) { - this.modeFip = modeFip; - } - public Optional getNonproductiveDescription() { return nonproductiveDescription; } @@ -91,9 +62,6 @@ public static FipPolygon build(Consumer config) { } public static class Builder extends BaseVdypPolygon.Builder> { - private Optional forestInventoryZone = Optional.empty(); - private Optional biogeoclimaticZone = Optional.empty(); - private Optional modeFip = Optional.empty(); private Optional nonproductiveDescription = Optional.empty(); private Optional yieldFactor = Optional.empty(); @@ -101,31 +69,11 @@ public Builder() { this.percentAvailable(Optional.empty()); } - public Builder forestInventoryZone(String forestInventoryZone) { - this.forestInventoryZone = Optional.of(forestInventoryZone); - return this; - } - - public Builder biogeoclimaticZone(String biogeoclimaticZone) { - this.biogeoclimaticZone = Optional.of(biogeoclimaticZone); - return this; - } - - public Builder modeFip(Optional modeFip) { - this.modeFip = modeFip; - return this; - } - public Builder nonproductiveDescription(Optional nonproductiveDescription) { this.nonproductiveDescription = nonproductiveDescription; return this; } - public Builder modeFip(FipMode modeFip) { - modeFip(Optional.of(modeFip)); - return this; - } - public Builder nonproductiveDescription(String nonproductiveDescription) { nonproductiveDescription(Optional.of(nonproductiveDescription)); return this; diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParserTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParserTest.java index 66af3bc72..af3fc400c 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParserTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipLayerParserTest.java @@ -35,7 +35,7 @@ void testParseEmpty() throws Exception { controlMap.put(FipLayerParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeStream(/* empty */)); + var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeInputStream(/* empty */)); parser.modify(controlMap, fileResolver); @@ -63,7 +63,7 @@ void testParseLayer() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 55 35.3 35.0 87.4 D D 1.0 0 13", "01002 S000001 00 1970 Z 55 0.0 0.0 0.0 0.0" ) @@ -90,13 +90,13 @@ void testParseLayer() throws Exception { LayerType.PRIMARY, allOf( hasProperty("polygonIdentifier", is("01002 S000001 00 1970")), // hasProperty("layer", is(LayerType.PRIMARY)), // - hasProperty("ageTotal", is(55f)), // - hasProperty("height", is(35.3f)), // - hasProperty("siteIndex", is(35.0f)), // + hasProperty("ageTotalSafe", is(55f)), // + hasProperty("heightSafe", is(35.3f)), // + hasProperty("siteIndex", present(is(35.0f))), // hasProperty("crownClosure", is(87.4f)), // - hasProperty("siteSp0", is("D")), // - hasProperty("siteSp64", is("D")), // - hasProperty("yearsToBreastHeight", is(1.0f)), // + hasProperty("siteGenus", present(is("D"))), // + hasProperty("siteSpecies", is("D")), // + hasProperty("yearsToBreastHeightSafe", is(1.0f)), // hasProperty("stockingClass", present(is('0'))), // hasProperty("inventoryTypeGroup", notPresent()), // hasProperty("siteCurveNumber", present(is(13))) @@ -119,7 +119,7 @@ void testParseTwoLayers() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000004 00 1970 V 195 45.2 22.3 4.0 B B 9.4 2 8", "01002 S000004 00 1970 1 85 42.3 31.9 82.8 H H 4.9 0 34", "01002 S000004 00 1970 Z 85 0.0 0.0 0.0 0.0" @@ -144,15 +144,18 @@ void testParseTwoLayers() throws Exception { assertThat( layers, hasSpecificEntry( - LayerType.PRIMARY, - allOf( - hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), - hasProperty("layer", is(LayerType.PRIMARY)), hasProperty("ageTotal", is(85f)), - hasProperty("height", is(42.3f)), hasProperty("siteIndex", is(31.9f)), - hasProperty("crownClosure", is(82.8f)), hasProperty("siteSp0", is("H")), - hasProperty("siteSp64", is("H")), hasProperty("yearsToBreastHeight", is(4.9f)), - hasProperty("stockingClass", present(is('0'))), - hasProperty("inventoryTypeGroup", notPresent()), + LayerType.PRIMARY, allOf( + hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), // + hasProperty("layer", is(LayerType.PRIMARY)), // + hasProperty("ageTotalSafe", is(85f)), // + hasProperty("heightSafe", is(42.3f)), // + hasProperty("siteIndex", present(is(31.9f))), // + hasProperty("crownClosure", is(82.8f)), // + hasProperty("siteGenus", present(is("H"))), // + hasProperty("siteSpecies", is("H")), // + hasProperty("yearsToBreastHeightSafe", is(4.9f)), // + hasProperty("stockingClass", present(is('0'))), // + hasProperty("inventoryTypeGroup", notPresent()), // hasProperty("siteCurveNumber", present(is(34))) ) ) @@ -160,13 +163,16 @@ void testParseTwoLayers() throws Exception { assertThat( layers, hasSpecificEntry( - LayerType.VETERAN, - allOf( - hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), - hasProperty("layer", is(LayerType.VETERAN)), hasProperty("ageTotal", is(195f)), - hasProperty("height", is(45.2f)), hasProperty("siteIndex", is(22.3f)), - hasProperty("crownClosure", is(4.0f)), hasProperty("siteSp0", is("B")), - hasProperty("siteSp64", is("B")), hasProperty("yearsToBreastHeight", is(9.4f)) + LayerType.VETERAN, allOf( + hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), // + hasProperty("layer", is(LayerType.VETERAN)), // + hasProperty("ageTotalSafe", is(195f)), // + hasProperty("heightSafe", is(45.2f)), // + hasProperty("siteIndex", present(is(22.3f))), // + hasProperty("crownClosure", is(4.0f)), // + hasProperty("siteGenus", present(is("B"))), // + hasProperty("siteSpecies", is("B")), // + hasProperty("yearsToBreastHeightSafe", is(9.4f)) // hasProperty("stockingClass", present(is("2"))), // hasProperty("siteCurveNumber", present(is(8))) ) @@ -188,7 +194,7 @@ void testIgnoreLayerIfHeightZero() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000004 00 1970 V 195 45.2 22.3 0.0 B B 9.4 2 8", "01002 S000004 00 1970 1 85 42.3 31.9 82.8 H H 4.9 0 34", "01002 S000004 00 1970 Z 85 0.0 0.0 0.0 0.0" @@ -213,15 +219,18 @@ void testIgnoreLayerIfHeightZero() throws Exception { assertThat( layers, hasSpecificEntry( - LayerType.PRIMARY, - allOf( - hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), - hasProperty("layer", is(LayerType.PRIMARY)), hasProperty("ageTotal", is(85f)), - hasProperty("height", is(42.3f)), hasProperty("siteIndex", is(31.9f)), - hasProperty("crownClosure", is(82.8f)), hasProperty("siteSp0", is("H")), - hasProperty("siteSp64", is("H")), hasProperty("yearsToBreastHeight", is(4.9f)), - hasProperty("stockingClass", present(is('0'))), - hasProperty("inventoryTypeGroup", notPresent()), + LayerType.PRIMARY, allOf( + hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), // + hasProperty("layer", is(LayerType.PRIMARY)), // + hasProperty("ageTotalSafe", is(85f)), // + hasProperty("heightSafe", is(42.3f)), // + hasProperty("siteIndex", present(is(31.9f))), // + hasProperty("crownClosure", is(82.8f)), // + hasProperty("siteGenus", present(is("H"))), // + hasProperty("siteSpecies", is("H")), // + hasProperty("yearsToBreastHeightSafe", is(4.9f)), // + hasProperty("stockingClass", present(is('0'))), // + hasProperty("inventoryTypeGroup", notPresent()), // hasProperty("siteCurveNumber", present(is(34))) ) ) @@ -242,7 +251,7 @@ void testIgnoreLayerIfCrownClosureZero() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000004 00 1970 V 195 0.0 22.3 4.0 B B 9.4 2 8", "01002 S000004 00 1970 1 85 42.3 31.9 82.8 H H 4.9 0 34", "01002 S000004 00 1970 Z 85 0.0 0.0 0.0 0.0" @@ -267,15 +276,18 @@ void testIgnoreLayerIfCrownClosureZero() throws Exception { assertThat( layers, hasSpecificEntry( - LayerType.PRIMARY, - allOf( - hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), - hasProperty("layer", is(LayerType.PRIMARY)), hasProperty("ageTotal", is(85f)), - hasProperty("height", is(42.3f)), hasProperty("siteIndex", is(31.9f)), - hasProperty("crownClosure", is(82.8f)), hasProperty("siteSp0", is("H")), - hasProperty("siteSp64", is("H")), hasProperty("yearsToBreastHeight", is(4.9f)), - hasProperty("stockingClass", present(is('0'))), - hasProperty("inventoryTypeGroup", notPresent()), + LayerType.PRIMARY, allOf( + hasProperty("polygonIdentifier", is("01002 S000004 00 1970")), // + hasProperty("layer", is(LayerType.PRIMARY)), // + hasProperty("ageTotalSafe", is(85f)), // + hasProperty("heightSafe", is(42.3f)), // + hasProperty("siteIndex", present(is(31.9f))), // + hasProperty("crownClosure", is(82.8f)), // + hasProperty("siteGenus", present(is("H"))), // + hasProperty("siteSpecies", is("H")), // + hasProperty("yearsToBreastHeightSafe", is(4.9f)), + hasProperty("stockingClass", present(is('0'))), // + hasProperty("inventoryTypeGroup", notPresent()), // hasProperty("siteCurveNumber", present(is(34))) ) ) diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParserTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParserTest.java index 267bbf069..23b75bd28 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParserTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipPolygonParserTest.java @@ -14,10 +14,10 @@ import org.junit.jupiter.api.Test; -import ca.bc.gov.nrs.vdyp.fip.model.FipMode; import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon; import ca.bc.gov.nrs.vdyp.io.parse.StreamingParser; import ca.bc.gov.nrs.vdyp.io.parse.StreamingParserFactory; +import ca.bc.gov.nrs.vdyp.model.FipMode; import ca.bc.gov.nrs.vdyp.test.TestUtils; import ca.bc.gov.nrs.vdyp.test.VdypMatchers; @@ -33,7 +33,7 @@ public void testParseEmpty() throws Exception { controlMap.put(FipPolygonParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeStream(/* empty */)); + var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeInputStream(/* empty */)); parser.modify(controlMap, fileResolver); @@ -59,8 +59,9 @@ public void testParsePolygon() throws Exception { controlMap.put(FipPolygonParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils - .fileResolver("test.dat", TestUtils.makeStream("Test Polygon A CWH 90.0 2 BLAH 0.95")); + var fileResolver = TestUtils.fileResolver( + "test.dat", TestUtils.makeInputStream("Test Polygon A CWH 90.0 2 BLAH 0.95") + ); parser.modify(controlMap, fileResolver); @@ -96,8 +97,9 @@ public void testParsePolygonWithBlanks() throws Exception { controlMap.put(FipPolygonParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils - .fileResolver("test.dat", TestUtils.makeStream("01002 S000001 00 1970 A CWH ")); + var fileResolver = TestUtils.fileResolver( + "test.dat", TestUtils.makeInputStream("01002 S000001 00 1970 A CWH ") + ); parser.modify(controlMap, fileResolver); @@ -135,7 +137,7 @@ public void testParseMultiple() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 A CWH 1.00", "01002 S000002 00 1970 A CWH 1.00", "01002 S000003 00 1970 A CWH 1.00", @@ -273,8 +275,9 @@ public void testParsePolygonZeroAsDefault() throws Exception { controlMap.put(FipPolygonParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils - .fileResolver("test.dat", TestUtils.makeStream("01002 S000001 00 1970 A CWH 0.0 0 0.00")); + var fileResolver = TestUtils.fileResolver( + "test.dat", TestUtils.makeInputStream("01002 S000001 00 1970 A CWH 0.0 0 0.00") + ); parser.modify(controlMap, fileResolver); @@ -310,8 +313,9 @@ public void testParsePolygonNegativeAsDefault() throws Exception { controlMap.put(FipPolygonParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils - .fileResolver("test.dat", TestUtils.makeStream("01002 S000001 00 1970 A CWH -1.0 -1.00")); + var fileResolver = TestUtils.fileResolver( + "test.dat", TestUtils.makeInputStream("01002 S000001 00 1970 A CWH -1.0 -1.00") + ); parser.modify(controlMap, fileResolver); diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParserTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParserTest.java index 42617bc4f..2d5cad3a9 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParserTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipSpeciesParserTest.java @@ -35,7 +35,7 @@ public void testParseEmpty() throws Exception { controlMap.put(FipSpeciesParser.CONTROL_KEY, "test.dat"); TestUtils.populateControlMapBecReal(controlMap); - var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeStream(/* empty */)); + var fileResolver = TestUtils.fileResolver("test.dat", TestUtils.makeInputStream(/* empty */)); parser.modify(controlMap, fileResolver); @@ -63,7 +63,7 @@ public void testParseOneGenus() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 B 100.0B 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 Z 0.0 0.0 0.0 0.0 0.0" ) @@ -109,7 +109,7 @@ public void testParseTwoGenera() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 B 75.0B 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 1 C 25.0C 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 Z 0.0 0.0 0.0 0.0 0.0" @@ -163,7 +163,7 @@ public void testParseTwoLayers() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 B 100.0B 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 V B 100.0B 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 Z 0.0 0.0 0.0 0.0 0.0" @@ -216,7 +216,7 @@ public void testParseTwoPolygons() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 B 100.0B 100.0 0.0 0.0 0.0", "01002 S000001 00 1970 Z 0.0 0.0 0.0 0.0 0.0", "01002 S000002 00 1970 1 B 100.0B 100.0 0.0 0.0 0.0", @@ -278,7 +278,7 @@ public void testParseMutipleSpecies() throws Exception { var fileResolver = TestUtils.fileResolver( "test.dat", - TestUtils.makeStream( + TestUtils.makeInputStream( "01002 S000001 00 1970 1 B 100.0B1 75.0B2 10.0B3 8.0B4 7.0", "01002 S000001 00 1970 Z 0.0 0.0 0.0 0.0 0.0" ) diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java index 2f0cd7daa..69c58a312 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/FipStartTest.java @@ -38,18 +38,17 @@ import org.easymock.EasyMock; import org.easymock.IMocksControl; -import org.easymock.bytebuddy.agent.builder.AgentBuilder; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.hamcrest.TypeSafeDiagnosingMatcher; import org.junit.jupiter.api.Test; +import ca.bc.gov.nrs.vdyp.common.Utils; import ca.bc.gov.nrs.vdyp.fip.FipStart.CompatibilityVariableMode; import ca.bc.gov.nrs.vdyp.fip.FipStart.VolumeComputeMode; import ca.bc.gov.nrs.vdyp.fip.model.FipLayer; import ca.bc.gov.nrs.vdyp.fip.model.FipLayerPrimary; -import ca.bc.gov.nrs.vdyp.fip.model.FipMode; import ca.bc.gov.nrs.vdyp.fip.model.FipPolygon; import ca.bc.gov.nrs.vdyp.fip.model.FipSpecies; import ca.bc.gov.nrs.vdyp.fip.test.FipTestUtils; @@ -61,10 +60,12 @@ import ca.bc.gov.nrs.vdyp.io.parse.StreamingParserFactory; import ca.bc.gov.nrs.vdyp.io.parse.VeteranLayerVolumeAdjustParser; import ca.bc.gov.nrs.vdyp.model.Coefficients; +import ca.bc.gov.nrs.vdyp.model.FipMode; import ca.bc.gov.nrs.vdyp.model.LayerType; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.model.StockingClassFactor; +import ca.bc.gov.nrs.vdyp.model.UtilizationClass; import ca.bc.gov.nrs.vdyp.model.VdypLayer; import ca.bc.gov.nrs.vdyp.model.VdypPolygon; import ca.bc.gov.nrs.vdyp.model.VdypSpecies; @@ -148,7 +149,7 @@ void testPolygonWithNoPrimaryLayer() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer2 = getTestVeteranLayer(polygonId, x -> { - x.setHeight(9f); + x.setHeight(Optional.of(9f)); }); polygon.setLayers(List.of(layer2)); @@ -176,7 +177,7 @@ void testPrimaryLayerHeightLessThanMinimum() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer = this.getTestPrimaryLayer("Test Polygon", x -> { - x.setHeight(4f); + x.setHeight(Optional.of(4f)); }); polygon.setLayers(Collections.singletonMap(LayerType.PRIMARY, layer)); @@ -205,7 +206,7 @@ void testVeteranLayerHeightLessThanMinimum() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer1 = getTestPrimaryLayer(polygonId, valid()); var layer2 = getTestVeteranLayer(polygonId, x -> { - x.setHeight(9f); + x.setHeight(Optional.of(9f)); }); polygon.setLayers(List.of(layer1, layer2)); @@ -234,7 +235,7 @@ void testPrimaryLayerYearsToBreastHeightLessThanMinimum() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer1 = getTestPrimaryLayer(polygonId, x -> { - x.setYearsToBreastHeight(0.2f); + x.setYearsToBreastHeight(Optional.of(0.2f)); }); polygon.setLayers(List.of(layer1)); @@ -266,8 +267,8 @@ void testPrimaryLayerTotalAgeLessThanYearsToBreastHeight() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer1 = getTestPrimaryLayer(polygonId, x -> { - x.setAgeTotal(7f); - x.setYearsToBreastHeight(8f); + x.setAgeTotal(Optional.of(7f)); + x.setYearsToBreastHeight(Optional.of(8f)); }); polygon.setLayers(List.of(layer1)); @@ -295,7 +296,7 @@ void testPrimaryLayerSiteIndexLessThanMinimum() throws Exception { var polygon = getTestPolygon(polygonId, valid()); var layer = this.getTestPrimaryLayer("Test Polygon", x -> { - x.setSiteIndex(0.2f); + x.setSiteIndex(Optional.of(0.2f)); }); polygon.setLayers(Collections.singletonMap(LayerType.PRIMARY, layer)); @@ -603,12 +604,12 @@ void testProcessVeteran() throws Exception { assertThat(result, hasProperty("layer", is(LayerType.VETERAN))); // Direct Copy - assertThat(result, hasProperty("ageTotal", is(8f))); - assertThat(result, hasProperty("height", is(6f))); - assertThat(result, hasProperty("yearsToBreastHeight", is(7f))); + assertThat(result, hasProperty("ageTotal", present(is(8f)))); + assertThat(result, hasProperty("height", present(is(6f)))); + assertThat(result, hasProperty("yearsToBreastHeight", present(is(7f)))); // Computed - assertThat(result, hasProperty("breastHeightAge", is(1f))); + assertThat(result, hasProperty("breastHeightAge", present(is(1f)))); // Remap species assertThat( @@ -640,7 +641,7 @@ void testProcessVeteranYearsToBreastHeightLessThanMinimum() throws Exception { var fipPolygon = getTestPolygon(polygonId, valid()); var fipLayer = getTestVeteranLayer(polygonId, (l) -> { - l.setYearsToBreastHeight(5.0f); + l.setYearsToBreastHeight(Optional.of(5.0f)); }); var fipSpecies = getTestSpecies(polygonId, LayerType.VETERAN, valid()); fipPolygon.setLayers(Collections.singletonMap(LayerType.VETERAN, fipLayer)); @@ -672,10 +673,10 @@ void testProcessVeteranYearsToBreastHeightLessThanMinimum() throws Exception { assertThat(result, notNullValue()); // Set minimum - assertThat(result, hasProperty("yearsToBreastHeight", is(6f))); + assertThat(result, hasProperty("yearsToBreastHeight", present(is(6f)))); // Computed based on minimum - assertThat(result, hasProperty("breastHeightAge", is(2f))); + assertThat(result, hasProperty("breastHeightAge", present(is(2f)))); } @@ -759,13 +760,13 @@ void testProcessPrimary() throws Exception { x.setForestInventoryZone("A"); }); var fipLayer = getTestPrimaryLayer(polygonId, x -> { - x.setAgeTotal(55f); - x.setHeight(35.3f); - x.setSiteIndex(35f); + x.setAgeTotalSafe(55f); + x.setHeight(Optional.of(35.3f)); + x.setSiteIndex(Optional.of(35f)); x.setCrownClosure(87.4f); - x.setSiteGenus("D"); + x.setSiteGenus(Optional.of("D")); x.setSiteSpecies("D"); - x.setYearsToBreastHeight(1f); + x.setYearsToBreastHeight(Optional.of(1f)); x.setPrimaryGenus(Optional.of("H")); x.setInventoryTypeGroup(Optional.of(13)); }); @@ -799,11 +800,11 @@ void testProcessPrimary() throws Exception { assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); assertThat(result, hasProperty("layer", is(LayerType.PRIMARY))); - assertThat(result, hasProperty("ageTotal", is(55f))); - assertThat(result, hasProperty("height", is(35.3f))); - assertThat(result, hasProperty("yearsToBreastHeight", is(1f))); + assertThat(result, hasProperty("ageTotal", present(is(55f)))); + assertThat(result, hasProperty("height", present(is(35.3f)))); + assertThat(result, hasProperty("yearsToBreastHeight", present(is(1f)))); - assertThat(result, hasProperty("breastHeightAge", is(54f))); + assertThat(result, hasProperty("breastHeightAge", present(is(54f)))); assertThat( result, @@ -1224,7 +1225,7 @@ void testEstimateVeteranLayerDQ() throws Exception { var fipPolygon = getTestPolygon(polygonId, valid()); var fipLayer = getTestVeteranLayer(polygonId, x -> { - x.setHeight(10f); + x.setHeight(Optional.of(10f)); }); var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", x -> { var map = new LinkedHashMap(); @@ -1599,17 +1600,14 @@ void testEstimatePrimaryNetBreakage() throws Exception { var utilizationClass = UtilizationClass.ALL; var breakageGroup = 20; - var quadMeanDiameterUtil = new Coefficients( - new float[] { 0f, 13.4943399f, 10.2402296f, 14.6183214f, 19.3349762f, 25.6280651f }, -1 - ); - var closeUtilizationUtil = new Coefficients( - new float[] { 0f, 6.41845179f, 0.0353721268f, 2.99654913f, 2.23212862f, 1.1544019f }, -1 - ); - var closeUtilizationNetOfDecayAndWasteUtil = new Coefficients( - new float[] { 0f, 6.18276405f, 0.0347718038f, 2.93580461f, 2.169273853f, 1.04291379f }, -1 - ); + var quadMeanDiameterUtil = Utils + .utilizationVector(0f, 13.4943399f, 10.2402296f, 14.6183214f, 19.3349762f, 25.6280651f); + var closeUtilizationUtil = Utils + .utilizationVector(0f, 6.41845179f, 0.0353721268f, 2.99654913f, 2.23212862f, 1.1544019f); + var closeUtilizationNetOfDecayAndWasteUtil = Utils + .utilizationVector(0f, 6.18276405f, 0.0347718038f, 2.93580461f, 2.169273853f, 1.04291379f); - var closeUtilizationNetOfDecayWasteAndBreakageUtil = FipStart.utilizationVector(); + var closeUtilizationNetOfDecayWasteAndBreakageUtil = Utils.utilizationVector(); app.estimateNetDecayWasteAndBreakageVolume( utilizationClass, breakageGroup, quadMeanDiameterUtil, closeUtilizationUtil, @@ -1635,13 +1633,13 @@ void testProcessAsVeteranLayer() throws Exception { }); var fipLayer = getTestVeteranLayer(polygonId, x -> { - x.setAgeTotal(105f); - x.setHeight(26.2f); - x.setSiteIndex(16.7f); + x.setAgeTotal(Optional.of(105f)); + x.setHeight(Optional.of(26.2f)); + x.setSiteIndex(Optional.of(16.7f)); x.setCrownClosure(4.0f); - x.setSiteGenus("H"); + x.setSiteGenus(Optional.of("H")); x.setSiteSpecies("H"); - x.setYearsToBreastHeight(7.1f); + x.setYearsToBreastHeight(Optional.of(7.1f)); }); var fipSpecies1 = getTestSpecies(polygonId, LayerType.VETERAN, "B", x -> { var map = new LinkedHashMap(); @@ -1675,10 +1673,10 @@ void testProcessAsVeteranLayer() throws Exception { assertThat(result, hasProperty("polygonIdentifier", is(polygonId))); assertThat(result, hasProperty("layer", is(LayerType.VETERAN))); - assertThat(result, hasProperty("ageTotal", closeTo(105f))); // LVCOM3/AGETOTLV - assertThat(result, hasProperty("breastHeightAge", closeTo(97.9000015f))); // LVCOM3/AGEBHLV - assertThat(result, hasProperty("yearsToBreastHeight", closeTo(7.0999999f))); // LVCOM3/YTBHLV - assertThat(result, hasProperty("height", closeTo(26.2000008f))); // LVCOM3/HDLV + assertThat(result, hasProperty("ageTotal", present(closeTo(105f)))); // LVCOM3/AGETOTLV + assertThat(result, hasProperty("breastHeightAge", present(closeTo(97.9000015f)))); // LVCOM3/AGEBHLV + assertThat(result, hasProperty("yearsToBreastHeight", present(closeTo(7.0999999f)))); // LVCOM3/YTBHLV + assertThat(result, hasProperty("height", present(closeTo(26.2000008f)))); // LVCOM3/HDLV assertThat(result, hasProperty("species", aMapWithSize(3))); var resultSpeciesMap = result.getSpecies(); @@ -2076,13 +2074,13 @@ void testEstimatePrimaryBaseArea() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(38.2999992f); - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(38.2999992f)); + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2116,13 +2114,13 @@ void testEstimatePrimaryBaseAreaHeightCloseToA2() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(10.1667995f); // Altered this in the debugger while running VDYP7 - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(10.1667995f)); // Altered this in the debugger while running VDYP7 + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2156,13 +2154,13 @@ void testEstimatePrimaryBaseAreaLowCrownClosure() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(38.2999992f); - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(38.2999992f)); + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(9f); // Altered this in the debugger while running VDYP7 - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2196,13 +2194,13 @@ void testEstimatePrimaryBaseAreaLowResult() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(7f); // Altered this in the debugger while running VDYP7 - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(7f)); // Altered this in the debugger while running VDYP7 + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2239,13 +2237,13 @@ void testEstimatePrimaryQuadMeanDiameter() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(38.2999992f); - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(38.2999992f)); + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2279,13 +2277,13 @@ void testEstimatePrimaryQuadMeanDiameterHeightLessThanA5() throws Exception { var bec = becLookup.get("CWH").get(); var layer = this.getTestPrimaryLayer("test polygon", l -> { - l.setAgeTotal(85f); - l.setHeight(4.74730005f); // Tweak this to be less than A5 for this BEC and SP0 - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(85f)); + l.setHeight(Optional.of(4.74730005f)); // Tweak this to be less than A5 for this BEC and SP0 + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2320,13 +2318,13 @@ void testEstimatePrimaryQuadMeanDiameterResultLargerThanUpperBound() throws Exce var layer = this.getTestPrimaryLayer("test polygon", l -> { // Tweak the values to produce a very large DQ - l.setAgeTotal(350f); - l.setHeight(80f); - l.setSiteIndex(28.6000004f); + l.setAgeTotal(Optional.of(350f)); + l.setHeight(Optional.of(80f)); + l.setSiteIndex(Optional.of(28.6000004f)); l.setCrownClosure(82.8000031f); - l.setYearsToBreastHeight(5.4000001f); + l.setYearsToBreastHeight(Optional.of(5.4000001f)); l.setSiteCurveNumber(Optional.of(34)); - l.setSiteGenus("H"); + l.setSiteGenus(Optional.of("H")); l.setSiteSpecies("H"); }); @@ -2443,7 +2441,6 @@ void testFindRootsForPrimaryLayerDiameterAndAreaOneSpecies() throws Exception { layer.getBaseAreaByUtilization().setCoe(0, 76.5122147f); layer.getTreesPerHectareByUtilization().setCoe(0, 845.805969f); layer.getQuadraticMeanDiameterByUtilization().setCoe(0, 33.9379082f); - layer.setAgeTotal(285f); var spec = VdypSpecies.build(layer, builder -> { builder.genus("Y"); @@ -2993,7 +2990,7 @@ void testEstimateQuadMeanDiameterByUtilization() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var coe = FipStart.utilizationVector(); + var coe = Utils.utilizationVector(); coe.setCoe(FipStart.UTIL_ALL, 31.6622887f); var bec = BecDefinitionParser.getBecs(controlMap).get("CWH").get(); @@ -3019,7 +3016,7 @@ void testEstimateQuadMeanDiameterByUtilization2() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var coe = FipStart.utilizationVector(); + var coe = Utils.utilizationVector(); coe.setCoe(FipStart.UTIL_ALL, 13.4943399f); var bec = BecDefinitionParser.getBecs(controlMap).get("MH").get(); @@ -3045,8 +3042,8 @@ void testEstimateBaseAreaByUtilization() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var dq = FipStart.utilizationVector(); - var ba = FipStart.utilizationVector(); + var dq = Utils.utilizationVector(); + var ba = Utils.utilizationVector(); dq.setCoe(0, 31.6622887f); dq.setCoe(1, 10.0594692f); dq.setCoe(2, 14.966774f); @@ -3078,9 +3075,9 @@ void testReconcileComponentsMode1() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var dq = FipStart.utilizationVector(); - var ba = FipStart.utilizationVector(); - var tph = FipStart.utilizationVector(); + var dq = Utils.utilizationVector(); + var ba = Utils.utilizationVector(); + var tph = Utils.utilizationVector(); // '082E004 615 1988' with component BA re-ordered from smallest to largest to // force mode 1. @@ -3116,9 +3113,9 @@ void testReconcileComponentsMode2() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var dq = FipStart.utilizationVector(); - var ba = FipStart.utilizationVector(); - var tph = FipStart.utilizationVector(); + var dq = Utils.utilizationVector(); + var ba = Utils.utilizationVector(); + var tph = Utils.utilizationVector(); dq.setCoe(0, 31.6622887f); dq.setCoe(1, 10.0594692f); dq.setCoe(2, 14.966774f); @@ -3150,9 +3147,9 @@ void testReconcileComponentsMode3() throws ProcessingException { var app = new FipStart(); app.setControlMap(controlMap); - var dq = FipStart.utilizationVector(); - var ba = FipStart.utilizationVector(); - var tph = FipStart.utilizationVector(); + var dq = Utils.utilizationVector(); + var ba = Utils.utilizationVector(); + var tph = Utils.utilizationVector(); // Set of inputs that cause mode 2 to fail over into mode 3 @@ -3187,9 +3184,9 @@ void testEstimateWholeStemVolumeByUtilizationClass() throws ProcessingException var app = new FipStart(); app.setControlMap(controlMap); - var dq = FipStart.utilizationVector(); - var ba = FipStart.utilizationVector(); - var wsv = FipStart.utilizationVector(); + var dq = Utils.utilizationVector(); + var ba = Utils.utilizationVector(); + var wsv = Utils.utilizationVector(); dq.setCoe(0, 13.4943399f); dq.setCoe(1, 10.2402296f); @@ -3438,7 +3435,7 @@ void testCreateVdypPolygon() throws ProcessingException { builder.percentGenus(90f); }); - processedLayers.get(LayerType.PRIMARY).setHeight(15f); + processedLayers.get(LayerType.PRIMARY).setHeight(Optional.of(15f)); // processedLayers.get(LayerType.PRIMARY).setCrownClosure(60f); var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); @@ -3498,7 +3495,7 @@ void testCreateVdypPolygonPercentForestLandGiven() throws ProcessingException { builder.percentGenus(90f); }); - processedLayers.get(LayerType.PRIMARY).setHeight(15f); + processedLayers.get(LayerType.PRIMARY).setHeight(Optional.of(15f)); // processedLayers.get(LayerType.PRIMARY).setCrownClosure(60f); var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); @@ -3552,7 +3549,7 @@ void testCreateVdypPolygonFipYoung() throws ProcessingException { builder.percentGenus(90f); }); - processedLayers.get(LayerType.PRIMARY).setHeight(15f); + processedLayers.get(LayerType.PRIMARY).setHeight(Optional.of(15f)); // processedLayers.get(LayerType.PRIMARY).setCrownClosure(60f); var vdypPolygon = app.createVdypPolygon(fipPolygon, processedLayers); @@ -3607,19 +3604,17 @@ void testApplyStockingFactor() throws ProcessingException { builder.height(20f); }); - vdypLayer.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); + vdypLayer.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); vdypLayer.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec1 = VdypSpecies.build(vdypLayer, builder -> { @@ -3630,16 +3625,16 @@ void testApplyStockingFactor() throws ProcessingException { builder.breakageGroup(-1); }); - spec1.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec1.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec2 = VdypSpecies.build(vdypLayer, builder -> { @@ -3650,16 +3645,16 @@ void testApplyStockingFactor() throws ProcessingException { builder.breakageGroup(-1); }); - spec2.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec2.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); @@ -3739,19 +3734,17 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { builder.yearsToBreastHeight(8.5f); builder.height(20f); }); - vdypLayer.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); + vdypLayer.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); vdypLayer.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec1 = VdypSpecies.build(vdypLayer, builder -> { @@ -3762,16 +3755,16 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { builder.breakageGroup(-1); }); - spec1.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec1.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec2 = VdypSpecies.build(vdypLayer, builder -> { @@ -3782,16 +3775,16 @@ void testApplyStockingFactorNoFactorForLayer() throws ProcessingException { builder.breakageGroup(-1); }); - spec2.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec2.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); @@ -3883,19 +3876,17 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { builder.height(20f); }); - vdypLayer.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + vdypLayer.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - vdypLayer.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) - ); + vdypLayer.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + vdypLayer.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); vdypLayer.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec1 = VdypSpecies.build(vdypLayer, builder -> { @@ -3906,16 +3897,16 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { builder.breakageGroup(-1); }); - spec1.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec1.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec1.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec1.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec1.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); var spec2 = VdypSpecies.build(vdypLayer, builder -> { @@ -3926,16 +3917,16 @@ void testApplyStockingFactorNoFactorForClass() throws ProcessingException { builder.breakageGroup(-1); }); - spec2.setLoreyHeightByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setQuadraticMeanDiameterByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setLoreyHeightByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); + spec2.setQuadraticMeanDiameterByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f, 1f)); - spec2.setBaseAreaByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setTreesPerHectareByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setWholeStemVolumeByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); - spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setBaseAreaByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setTreesPerHectareByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setWholeStemVolumeByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); + spec2.setCloseUtilizationVolumeNetOfDecayAndWasteByUtilization(Utils.utilizationVector(1f, 1f, 1f, 1f, 1f)); spec2.setCloseUtilizationVolumeNetOfDecayWasteAndBreakageByUtilization( - FipStart.utilizationVector(1f, 1f, 1f, 1f, 1f) + Utils.utilizationVector(1f, 1f, 1f, 1f, 1f) ); app.adjustForStocking(vdypLayer, fipPrimaryLayer, BecDefinitionParser.getBecs(controlMap).get("IDF").get()); diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/ModifierParserTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/ModifierParserTest.java index 75e257486..ae7a4d1b6 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/ModifierParserTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/ModifierParserTest.java @@ -13,6 +13,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -28,6 +29,7 @@ import ca.bc.gov.nrs.vdyp.io.parse.HLNonprimaryCoefficientParser; import ca.bc.gov.nrs.vdyp.io.parse.VeteranBQParser; import ca.bc.gov.nrs.vdyp.model.Coefficients; +import ca.bc.gov.nrs.vdyp.model.JProgram; import ca.bc.gov.nrs.vdyp.model.MatrixMap; import ca.bc.gov.nrs.vdyp.model.MatrixMap2; import ca.bc.gov.nrs.vdyp.model.MatrixMap2Impl; @@ -42,7 +44,7 @@ class ModifierParserTest { @Test void testNoFilenameForControlFile() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.empty()); @@ -51,7 +53,13 @@ void testNoFilenameForControlFile() throws Exception { var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + + @Override + public OutputStream resolveForOutput(String filename) throws IOException { fail("Should not call FileResolver::resolve"); return null; } @@ -71,7 +79,7 @@ public String toString(String filename) throws IOException { @Test void testMissingControlFile() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -80,12 +88,18 @@ void testMissingControlFile() throws Exception { var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); throw new IOException(); } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { fail("Should not call FileResolver::toString"); @@ -102,7 +116,7 @@ public String toString(String filename) throws IOException { @Test void testLoadEmptyFile() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -113,17 +127,23 @@ void testLoadEmptyFile() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream(); + var is = TestUtils.makeInputStream(); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -178,7 +198,7 @@ protected void modifierDefaultAsserts(Map controlMap) { @Test void testBaDqSpecies() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -189,17 +209,23 @@ void testBaDqSpecies() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("201 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); + var is = TestUtils.makeInputStream("201 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -237,7 +263,7 @@ public String toString(String filename) throws IOException { @Test void testBaDqSpeciesDifferentProgram() throws Exception { - var parser = new ModifierParser(3); + var parser = new ModifierParser(JProgram.VRI_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -248,17 +274,23 @@ void testBaDqSpeciesDifferentProgram() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("201 1 0 0 0 0 0 0.000 0.000 0.000 0.000"); + var is = TestUtils.makeInputStream("201 1 0 0 0 0 0 0.000 0.000 0.000 0.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -273,7 +305,7 @@ public String toString(String filename) throws IOException { @Test void testIgnoreAfterStop() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -284,17 +316,23 @@ void testIgnoreAfterStop() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("999", "201 1 0 0 0 0 0 0.000 0.000 0.000 0.000"); + var is = TestUtils.makeInputStream("999", "201 1 0 0 0 0 0 0.000 0.000 0.000 0.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -309,7 +347,7 @@ public String toString(String filename) throws IOException { @Test void testIgnoreCommentsAndBlanks() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -320,17 +358,23 @@ void testIgnoreCommentsAndBlanks() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("", " x", "000 x", "201 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); + var is = TestUtils.makeInputStream("", " x", "000 x", "201 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -368,7 +412,7 @@ public String toString(String filename) throws IOException { @Test void testBaDqAllSpecies() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -379,17 +423,23 @@ void testBaDqAllSpecies() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("200 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); + var is = TestUtils.makeInputStream("200 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -419,7 +469,7 @@ public String toString(String filename) throws IOException { @Test void testVetBq() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -430,17 +480,23 @@ void testVetBq() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("098 1 0 0 0 0 0 0.200 0.300"); + var is = TestUtils.makeInputStream("098 1 0 0 0 0 0 0.200 0.300"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -470,7 +526,7 @@ private MatrixMap2 populateVetBq(Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -481,17 +537,23 @@ void testDecayWaste() throws Exception { populateHlP3(controlMap); populateHlNP(controlMap); - var is = TestUtils.makeStream("301 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); + var is = TestUtils.makeInputStream("301 1 0 0 0 0 0 2.000 3.000 4.000 5.000"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; @@ -529,7 +591,7 @@ public String toString(String filename) throws IOException { @Test public void testHL() throws Exception { - var parser = new ModifierParser(1); + var parser = new ModifierParser(JProgram.FIP_START); Map controlMap = new HashMap<>(); controlMap.put(ModifierParser.CONTROL_KEY, Optional.of("testFilename")); @@ -541,17 +603,23 @@ public void testHL() throws Exception { var hlP3Map = populateHlP3(controlMap); var hlNPMap = populateHlNP(controlMap); - var is = TestUtils.makeStream("401 1 0 0 0 0 0 0.200 0.300 0.500 0.700"); + var is = TestUtils.makeInputStream("401 1 0 0 0 0 0 0.200 0.300 0.500 0.700"); var fileResolver = new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { assertThat(filename, is("testFilename")); return is; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not call FileResolver::resolve"); + return null; + } + @Override public String toString(String filename) throws IOException { return filename; diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java index 92753f4cb..88ed9e013 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/RootFinderTest.java @@ -5,7 +5,6 @@ import static org.hamcrest.Matchers.contains; import java.util.Arrays; -import java.util.LinkedHashMap; import java.util.Map; import org.apache.commons.math3.analysis.MultivariateMatrixFunction; import org.apache.commons.math3.analysis.MultivariateVectorFunction; diff --git a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/test/FipTestUtils.java b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/test/FipTestUtils.java index 9aaaacb87..beb043711 100644 --- a/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/test/FipTestUtils.java +++ b/vdyp-fip/src/test/java/ca/bc/gov/nrs/vdyp/fip/test/FipTestUtils.java @@ -1,7 +1,10 @@ package ca.bc.gov.nrs.vdyp.fip.test; +import static org.junit.jupiter.api.Assertions.fail; + import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.Arrays; import java.util.HashMap; import java.util.Map; @@ -14,6 +17,7 @@ import ca.bc.gov.nrs.vdyp.io.FileResolver; import ca.bc.gov.nrs.vdyp.io.parse.ControlFileParserTest; import ca.bc.gov.nrs.vdyp.io.parse.ResourceParseException; +import ca.bc.gov.nrs.vdyp.model.JProgram; import ca.bc.gov.nrs.vdyp.model.Region; import ca.bc.gov.nrs.vdyp.test.TestUtils; @@ -51,7 +55,7 @@ public class FipTestUtils { * @param controlMap */ public static void modifyControlMap(HashMap controlMap) { - int jprogram = 1; + JProgram jprogram = JProgram.FIP_START; TestUtils.populateControlMapFromResource(controlMap, new ModifierParser(jprogram), "mod19813.prm"); } @@ -82,17 +86,24 @@ public static FileResolver fileResolver(Class klazz) { return new FileResolver() { @Override - public InputStream resolve(String filename) throws IOException { + public InputStream resolveForInput(String filename) throws IOException { InputStream resourceAsStream = klazz.getResourceAsStream(filename); if (resourceAsStream == null) throw new IOException("Could not load " + filename); return resourceAsStream; } + @Override + public OutputStream resolveForOutput(String filename) throws IOException { + fail("Should not be opening file " + filename + " for output"); + return null; + } + @Override public String toString(String filename) throws IOException { return klazz.getResource(filename).toString(); } + }; } }